Merge "Fix DPM.getScreenCaptureDisabled on parent user" into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 8547ec1..a60ced5 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -65,13 +65,13 @@
         "android.sdk.flags-aconfig-java",
         "android.security.flags-aconfig-java",
         "android.server.app.flags-aconfig-java",
+        "android.service.appprediction.flags-aconfig-java",
         "android.service.autofill.flags-aconfig-java",
         "android.service.chooser.flags-aconfig-java",
         "android.service.compat.flags-aconfig-java",
         "android.service.controls.flags-aconfig-java",
         "android.service.dreams.flags-aconfig-java",
         "android.service.notification.flags-aconfig-java",
-        "android.service.appprediction.flags-aconfig-java",
         "android.service.quickaccesswallet.flags-aconfig-java",
         "android.service.voice.flags-aconfig-java",
         "android.speech.flags-aconfig-java",
@@ -107,6 +107,7 @@
         "com.android.server.flags.services-aconfig-java",
         "com.android.text.flags-aconfig-java",
         "com.android.window.flags.window-aconfig-java",
+        "configinfra_framework_flags_java_lib",
         "conscrypt_exported_aconfig_flags_lib",
         "device_policy_aconfig_flags_lib",
         "display_flags_lib",
@@ -277,6 +278,7 @@
 java_aconfig_library {
     name: "com.android.input.flags-aconfig-java",
     aconfig_declarations: "com.android.input.flags-aconfig",
+    host_supported: true,
     defaults: ["framework-minus-apex-aconfig-java-defaults"],
 }
 
@@ -521,7 +523,10 @@
     package: "android.companion.virtualdevice.flags",
     container: "system",
     exportable: true,
-    srcs: ["core/java/android/companion/virtual/flags/*.aconfig"],
+    srcs: [
+        "core/java/android/companion/virtual/flags/flags.aconfig",
+        "core/java/android/companion/virtual/flags/launched_flags.aconfig",
+    ],
 }
 
 java_aconfig_library {
@@ -546,7 +551,7 @@
     name: "android.companion.virtual.flags-aconfig",
     package: "android.companion.virtual.flags",
     container: "system",
-    srcs: ["core/java/android/companion/virtual/*.aconfig"],
+    srcs: ["core/java/android/companion/virtual/flags/deprecated_flags_do_not_edit.aconfig"],
 }
 
 // InputMethod
@@ -826,8 +831,8 @@
     min_sdk_version: "30",
     apex_available: [
         "//apex_available:platform",
-        "com.android.permission",
         "com.android.nfcservices",
+        "com.android.permission",
     ],
 }
 
@@ -1583,6 +1588,13 @@
 }
 
 java_aconfig_library {
+    name: "android.app.appfunctions.flags-aconfig-java-host",
+    aconfig_declarations: "android.app.appfunctions.flags-aconfig",
+    defaults: ["framework-minus-apex-aconfig-java-defaults"],
+    host_supported: true,
+}
+
+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"],
diff --git a/Android.bp b/Android.bp
index a1f6e30..9d8c8a6 100644
--- a/Android.bp
+++ b/Android.bp
@@ -408,7 +408,6 @@
         "bouncycastle-repackaged-unbundled",
         "com.android.sysprop.foldlockbehavior",
         "com.android.sysprop.view",
-        "configinfra_framework_flags_java_lib",
         "framework-internal-utils",
         "dynamic_instrumentation_manager_aidl-java",
         // If MimeMap ever becomes its own APEX, then this dependency would need to be removed
@@ -537,45 +536,6 @@
     }),
 }
 
-// This is identical to "framework-minus-apex" but with "jarjar_shards" hardcodd.
-// (also "stem" is commented out to avoid a conflict with the "framework-minus-apex")
-// TODO(b/383559945) This module is just for local testing / verification. It's not used
-// by anything. Remove it once we roll out RELEASE_USE_SHARDED_JARJAR_ON_FRAMEWORK_MINUS_APEX.
-java_library {
-    name: "framework-minus-apex_jarjar-sharded",
-    defaults: [
-        "framework-minus-apex-with-libs-defaults",
-        "framework-non-updatable-lint-defaults",
-    ],
-    installable: true,
-    // For backwards compatibility.
-    // stem: "framework",
-    apex_available: ["//apex_available:platform"],
-    visibility: [
-        "//frameworks/base",
-        "//frameworks/base/location",
-        // TODO(b/147128803) remove the below lines
-        "//frameworks/base/apex/blobstore/framework",
-        "//frameworks/base/apex/jobscheduler/framework",
-        "//frameworks/base/packages/Tethering/tests/unit",
-        "//packages/modules/Connectivity/Tethering/tests/unit",
-    ],
-    errorprone: {
-        javacflags: [
-            "-Xep:AndroidFrameworkCompatChange:ERROR",
-            "-Xep:AndroidFrameworkUid:ERROR",
-        ],
-    },
-    lint: {
-        baseline_filename: "lint-baseline.xml",
-        warning_checks: [
-            "FlaggedApi",
-        ],
-    },
-    jarjar_prefix: "com.android.internal.hidden_from_bootclasspath",
-    jarjar_shards: "10",
-}
-
 java_library {
     name: "framework-minus-apex-intdefs",
     defaults: ["framework-minus-apex-with-libs-defaults"],
diff --git a/apct-tests/perftests/aconfig/Android.bp b/apct-tests/perftests/aconfig/Android.bp
new file mode 100644
index 0000000..715923d
--- /dev/null
+++ b/apct-tests/perftests/aconfig/Android.bp
@@ -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 {
+    default_team: "trendy_team_android_core_experiments",
+    // 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: "AconfigPerfTests",
+    srcs: ["src/**/*.java"],
+    static_libs: [
+        "aconfig_device_paths_java_util",
+        "androidx.test.rules",
+        "apct-perftests-utils",
+        "collector-device-lib",
+        "truth",
+    ],
+    platform_apis: true,
+    certificate: "platform",
+    test_suites: ["device-tests"],
+    data: [":perfetto_artifacts"],
+}
diff --git a/apct-tests/perftests/aconfig/AndroidManifest.xml b/apct-tests/perftests/aconfig/AndroidManifest.xml
new file mode 100644
index 0000000..e9d7c17
--- /dev/null
+++ b/apct-tests/perftests/aconfig/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?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.aconfig">
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="com.android.perftests.aconfig"/>
+
+</manifest>
\ No newline at end of file
diff --git a/apct-tests/perftests/aconfig/AndroidTest.xml b/apct-tests/perftests/aconfig/AndroidTest.xml
new file mode 100644
index 0000000..036e031
--- /dev/null
+++ b/apct-tests/perftests/aconfig/AndroidTest.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Runs AconfigPerfTests 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="AconfigPerfTests.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_detailed.textproto" value="/data/misc/perfetto-traces/trace_config.textproto" />
+    </target_preparer>
+
+
+    <!-- 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" />
+    </metrics_collector>
+
+    <!-- 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.aconfig" />
+        <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" />
+
+        <option name="instrumentation-arg" key="newRunListenerMode" value="true" />
+
+    </test>
+</configuration>
\ No newline at end of file
diff --git a/apct-tests/perftests/aconfig/OWNERS b/apct-tests/perftests/aconfig/OWNERS
new file mode 100644
index 0000000..2202076
--- /dev/null
+++ b/apct-tests/perftests/aconfig/OWNERS
@@ -0,0 +1 @@
+file:platform/packages/modules/ConfigInfrastructure:/OWNERS
\ No newline at end of file
diff --git a/apct-tests/perftests/aconfig/src/android/os/flagging/AconfigPackagePerfTest.java b/apct-tests/perftests/aconfig/src/android/os/flagging/AconfigPackagePerfTest.java
new file mode 100644
index 0000000..e790874
--- /dev/null
+++ b/apct-tests/perftests/aconfig/src/android/os/flagging/AconfigPackagePerfTest.java
@@ -0,0 +1,138 @@
+/*
+ * 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.os.flagging;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.aconfig.DeviceProtosTestUtil;
+import android.aconfig.nano.Aconfig.parsed_flag;
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+@RunWith(Parameterized.class)
+public class AconfigPackagePerfTest {
+
+    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    @Parameterized.Parameters(name = "isPlatform_{0}")
+    public static Collection<Object[]> data() {
+        return Arrays.asList(new Object[][] {{false}, {true}});
+    }
+
+    private static final Set<String> PLATFORM_CONTAINERS = Set.of("system", "vendor", "product");
+    private static List<parsed_flag> sFlags;
+
+    @BeforeClass
+    public static void init() {
+        try {
+            sFlags = DeviceProtosTestUtil.loadAndParseFlagProtos();
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    // if this variable is true, then the test query flags from system/product/vendor
+    // if this variable is false, then the test query flags from updatable partitions
+    @Parameterized.Parameter(0)
+    public boolean mIsPlatform;
+
+    @Test
+    public void timeAconfigPackageLoadOnePackage() {
+        String packageName = "";
+        for (parsed_flag flag : sFlags) {
+            if (mIsPlatform == PLATFORM_CONTAINERS.contains(flag.container)) {
+                packageName = flag.package_;
+                break;
+            }
+        }
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            AconfigPackage.load(packageName);
+        }
+    }
+
+    @Test
+    public void timeAconfigPackageLoadMultiplePackages() {
+        // load num packages
+        int packageNum = 25;
+        Set<String> packageSet = new HashSet<>();
+        for (parsed_flag flag : sFlags) {
+            if (mIsPlatform == PLATFORM_CONTAINERS.contains(flag.container)) {
+                packageSet.add(flag.package_);
+            }
+            if (packageSet.size() >= packageNum) {
+                break;
+            }
+        }
+        List<String> packageList = new ArrayList(packageSet);
+        assertThat(packageList.size()).isAtLeast(packageNum);
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        for (int i = 0; state.keepRunning(); i++) {
+            AconfigPackage.load(packageList.get(i % packageNum));
+        }
+    }
+
+    @Test
+    public void timeAconfigPackageGetBooleanFlagValue() {
+        // get one package contains num of flags
+        int flagNum = 20;
+        List<parsed_flag> l = findNumFlagsInSamePackage(flagNum, mIsPlatform);
+        List<String> flagName = new ArrayList<>();
+        String packageName = l.get(0).package_;
+        for (parsed_flag flag : l) {
+            flagName.add(flag.name);
+        }
+        assertThat(flagName.size()).isAtLeast(flagNum);
+        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        AconfigPackage ap = AconfigPackage.load(packageName);
+        for (int i = 0; state.keepRunning(); i++) {
+            ap.getBooleanFlagValue(flagName.get(i % flagNum), false);
+        }
+    }
+
+    private static List<parsed_flag> findNumFlagsInSamePackage(int num, boolean isPlatform) {
+        Map<String, List<parsed_flag>> packageToFlag = new HashMap<>();
+        List<parsed_flag> ret = new ArrayList<parsed_flag>();
+        for (parsed_flag flag : sFlags) {
+            if (isPlatform == PLATFORM_CONTAINERS.contains(flag.container)) {
+                ret =
+                        packageToFlag.computeIfAbsent(
+                                flag.package_, k -> new ArrayList<parsed_flag>());
+                ret.add(flag);
+                if (ret.size() >= num) {
+                    break;
+                }
+            }
+        }
+        return ret;
+    }
+}
diff --git a/apct-tests/perftests/core/src/android/app/OverlayManagerPerfTest.java b/apct-tests/perftests/core/src/android/app/OverlayManagerPerfTest.java
index a12121f..5d39ccc 100644
--- a/apct-tests/perftests/core/src/android/app/OverlayManagerPerfTest.java
+++ b/apct-tests/perftests/core/src/android/app/OverlayManagerPerfTest.java
@@ -20,7 +20,6 @@
 
 import android.content.Context;
 import android.content.om.OverlayManager;
-import android.os.UserHandle;
 import android.perftests.utils.BenchmarkState;
 import android.perftests.utils.PerfStatusReporter;
 import android.perftests.utils.TestPackageInstaller;
@@ -127,7 +126,7 @@
     private void assertSetEnabled(boolean enabled, Context context, Stream<String> packagesStream) {
         final var overlayPackages = packagesStream.toList();
         overlayPackages.forEach(
-                name -> sOverlayManager.setEnabled(name, enabled, UserHandle.SYSTEM));
+                name -> sOverlayManager.setEnabled(name, enabled, context.getUser()));
 
         // Wait for the overlay changes to propagate
         final var endTime = System.nanoTime() + TimeUnit.SECONDS.toNanos(20);
@@ -174,7 +173,7 @@
             // Disable the overlay and remove the idmap for the next iteration of the test
             state.pauseTiming();
             assertSetEnabled(false, sContext, packageName);
-            sOverlayManager.invalidateCachesForOverlay(packageName, UserHandle.SYSTEM);
+            sOverlayManager.invalidateCachesForOverlay(packageName, sContext.getUser());
             state.resumeTiming();
         }
     }
@@ -189,7 +188,7 @@
             // Disable the overlay and remove the idmap for the next iteration of the test
             state.pauseTiming();
             assertSetEnabled(false, sContext, packageName);
-            sOverlayManager.invalidateCachesForOverlay(packageName, UserHandle.SYSTEM);
+            sOverlayManager.invalidateCachesForOverlay(packageName, sContext.getUser());
             state.resumeTiming();
         }
     }
diff --git a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ClientEndpoint.java b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ClientEndpoint.java
index 1a7258a..4c34165 100644
--- a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ClientEndpoint.java
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ClientEndpoint.java
@@ -20,6 +20,7 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.lang.AutoCloseable;
 import java.net.InetAddress;
 import java.net.SocketException;
 import java.nio.channels.ClosedChannelException;
@@ -33,7 +34,7 @@
  * Client-side endpoint. Provides basic services for sending/receiving messages from the client
  * socket.
  */
-final class ClientEndpoint {
+final class ClientEndpoint implements AutoCloseable {
     private final SSLSocket socket;
     private InputStream input;
     private OutputStream output;
@@ -56,6 +57,11 @@
         }
     }
 
+    @Override
+    public void close() {
+        stop();
+    }
+
     void stop() {
         try {
             socket.close();
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 f20b170..9e45c4a 100644
--- a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ClientSocketPerfTest.java
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ClientSocketPerfTest.java
@@ -44,24 +44,21 @@
 import javax.crypto.NoSuchPaddingException;
 
 import org.junit.Rule;
+import org.junit.After;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import junitparams.JUnitParamsRunner;
 import junitparams.Parameters;
 import android.conscrypt.ServerEndpoint.MessageProcessor;
 
-/**
- * Benchmark for comparing performance of server socket implementations.
- */
+/** Benchmark for comparing performance of server socket implementations. */
 @RunWith(JUnitParamsRunner.class)
 @LargeTest
 public final class ClientSocketPerfTest {
 
     @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
 
-    /**
-     * Provider for the test configuration
-     */
+    /** Provider for the test configuration */
     private class Config {
         EndpointFactory a_clientFactory;
         EndpointFactory b_serverFactory;
@@ -69,19 +66,22 @@
         String d_cipher;
         ChannelType e_channelType;
         PerfTestProtocol f_protocol;
-        Config(EndpointFactory clientFactory,
-            EndpointFactory serverFactory,
-            int messageSize,
-            String cipher,
-            ChannelType channelType,
-            PerfTestProtocol protocol) {
-          a_clientFactory = clientFactory;
-          b_serverFactory = serverFactory;
-          c_messageSize = messageSize;
-          d_cipher = cipher;
-          e_channelType = channelType;
-          f_protocol = protocol;
+
+        Config(
+                EndpointFactory clientFactory,
+                EndpointFactory serverFactory,
+                int messageSize,
+                String cipher,
+                ChannelType channelType,
+                PerfTestProtocol protocol) {
+            a_clientFactory = clientFactory;
+            b_serverFactory = serverFactory;
+            c_messageSize = messageSize;
+            d_cipher = cipher;
+            e_channelType = channelType;
+            f_protocol = protocol;
         }
+
         public EndpointFactory clientFactory() {
             return a_clientFactory;
         }
@@ -112,23 +112,43 @@
         for (EndpointFactory endpointFactory : EndpointFactory.values()) {
             for (ChannelType channelType : ChannelType.values()) {
                 for (PerfTestProtocol protocol : PerfTestProtocol.values()) {
-                    params.add(new Object[] {new Config(endpointFactory,
-                        endpointFactory, 64, "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
-                        channelType, protocol)});
-                    params.add(new Object[] {new Config(endpointFactory,
-                        endpointFactory, 512, "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
-                        channelType, protocol)});
-                    params.add(new Object[] {new Config(endpointFactory,
-                        endpointFactory, 4096, "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
-                        channelType, protocol)});
+                    params.add(
+                            new Object[] {
+                                new Config(
+                                        endpointFactory,
+                                        endpointFactory,
+                                        64,
+                                        "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
+                                        channelType,
+                                        protocol)
+                            });
+                    params.add(
+                            new Object[] {
+                                new Config(
+                                        endpointFactory,
+                                        endpointFactory,
+                                        512,
+                                        "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
+                                        channelType,
+                                        protocol)
+                            });
+                    params.add(
+                            new Object[] {
+                                new Config(
+                                        endpointFactory,
+                                        endpointFactory,
+                                        4096,
+                                        "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
+                                        channelType,
+                                        protocol)
+                            });
                 }
             }
         }
         return params;
     }
 
-    private ClientEndpoint client;
-    private ServerEndpoint server;
+    private SocketPair socketPair = new SocketPair();
     private byte[] message;
     private ExecutorService executor;
     private Future<?> sendingFuture;
@@ -137,46 +157,78 @@
     private static final AtomicLong bytesCounter = new AtomicLong();
     private AtomicBoolean recording = new AtomicBoolean();
 
+    private static class SocketPair implements AutoCloseable {
+        public ClientEndpoint client;
+        public ServerEndpoint server;
+
+        SocketPair() {
+            client = null;
+            server = null;
+        }
+
+        @Override
+        public void close() {
+            if (client != null) {
+                client.stop();
+            }
+            if (server != null) {
+                server.stop();
+            }
+        }
+    }
+
     private void setup(Config config) throws Exception {
         message = newTextMessage(512);
 
         // Always use the same server for consistency across the benchmarks.
-        server = config.serverFactory().newServer(
-                config.messageSize(), config.protocol().getProtocols(),
-                ciphers(config));
+        socketPair.server =
+                config.serverFactory()
+                        .newServer(
+                                config.messageSize(),
+                                config.protocol().getProtocols(),
+                                ciphers(config));
+        socketPair.server.init();
 
-        server.setMessageProcessor(new ServerEndpoint.MessageProcessor() {
-            @Override
-            public void processMessage(byte[] inMessage, int numBytes, OutputStream os) {
-                if (recording.get()) {
-                    // Server received a message, increment the count.
-                    bytesCounter.addAndGet(numBytes);
-                }
-            }
-        });
-        Future<?> connectedFuture = server.start();
+        socketPair.server.setMessageProcessor(
+                new ServerEndpoint.MessageProcessor() {
+                    @Override
+                    public void processMessage(byte[] inMessage, int numBytes, OutputStream os) {
+                        if (recording.get()) {
+                            // Server received a message, increment the count.
+                            bytesCounter.addAndGet(numBytes);
+                        }
+                    }
+                });
+        Future<?> connectedFuture = socketPair.server.start();
 
-        client = config.clientFactory().newClient(
-            config.channelType(), server.port(), config.protocol().getProtocols(), ciphers(config));
-        client.start();
+        socketPair.client =
+                config.clientFactory()
+                        .newClient(
+                                config.channelType(),
+                                socketPair.server.port(),
+                                config.protocol().getProtocols(),
+                                ciphers(config));
+        socketPair.client.start();
 
         // Wait for the initial connection to complete.
         connectedFuture.get(5, TimeUnit.SECONDS);
 
         executor = Executors.newSingleThreadExecutor();
-        sendingFuture = executor.submit(new Runnable() {
-            @Override
-            public void run() {
-                try {
-                    Thread thread = Thread.currentThread();
-                    while (!stopping && !thread.isInterrupted()) {
-                        client.sendMessage(message);
-                    }
-                } finally {
-                    client.flush();
-                }
-            }
-        });
+        sendingFuture =
+                executor.submit(
+                        new Runnable() {
+                            @Override
+                            public void run() {
+                                try {
+                                    Thread thread = Thread.currentThread();
+                                    while (!stopping && !thread.isInterrupted()) {
+                                        socketPair.client.sendMessage(message);
+                                    }
+                                } finally {
+                                    socketPair.client.flush();
+                                }
+                            }
+                        });
     }
 
     void close() throws Exception {
@@ -185,29 +237,37 @@
         // Wait for the sending thread to stop.
         sendingFuture.get(5, TimeUnit.SECONDS);
 
-        client.stop();
-        server.stop();
-        executor.shutdown();
-        executor.awaitTermination(5, TimeUnit.SECONDS);
+        if (socketPair != null) {
+            socketPair.close();
+        }
+        if (executor != null) {
+            executor.shutdown();
+            executor.awaitTermination(5, TimeUnit.SECONDS);
+        }
     }
 
-    /**
-     * Simple benchmark for the amount of time to send a given number of messages
-     */
+    /** Simple benchmark for the amount of time to send a given number of messages */
     @Test
     @Parameters(method = "getParams")
     public void time(Config config) throws Exception {
-        reset();
-        setup(config);
-        recording.set(true);
+        try {
+            reset();
+            setup(config);
+            recording.set(true);
 
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-        while (state.keepRunning()) {
-          while (bytesCounter.get() < config.messageSize()) {
-          }
-          bytesCounter.set(0);
+            BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+            while (state.keepRunning()) {
+                while (bytesCounter.get() < config.messageSize()) {}
+                bytesCounter.set(0);
+            }
+            recording.set(false);
+        } finally {
+            close();
         }
-        recording.set(false);
+    }
+
+    @After
+    public void tearDown() throws Exception {
         close();
     }
 
@@ -219,4 +279,4 @@
     private String[] ciphers(Config config) {
         return new String[] {config.cipher()};
     }
-}
\ No newline at end of file
+}
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 1e4f124..83eaaa1 100644
--- a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerEndpoint.java
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerEndpoint.java
@@ -16,10 +16,14 @@
 
 package android.conscrypt;
 
+import static org.conscrypt.TestUtils.getLoopbackAddress;
+
 import java.io.EOFException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.lang.AutoCloseable;
+import java.net.InetSocketAddress;
 import java.net.ServerSocket;
 import java.net.SocketException;
 import java.nio.channels.ClosedChannelException;
@@ -37,7 +41,7 @@
 /**
  * A simple socket-based test server.
  */
-final class ServerEndpoint {
+final class ServerEndpoint implements AutoCloseable {
     /**
      * A processor for receipt of a single message.
      */
@@ -82,7 +86,11 @@
         this.messageSize = messageSize;
         this.protocols = protocols;
         this.cipherSuites = cipherSuites;
-        buffer = new byte[messageSize];
+        this.buffer = new byte[messageSize];
+    }
+
+    void init() throws IOException {
+        serverSocket.bind(new InetSocketAddress(getLoopbackAddress(), 0));
     }
 
     void setMessageProcessor(MessageProcessor messageProcessor) {
@@ -94,6 +102,11 @@
         return executor.submit(new AcceptTask());
     }
 
+    @Override
+    public void close() {
+        stop();
+    }
+
     void stop() {
         try {
             stopping = true;
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 af3c405..90a87ce 100644
--- a/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerSocketPerfTest.java
+++ b/apct-tests/perftests/core/src/android/conscrypt/conscrypt/ServerSocketPerfTest.java
@@ -44,6 +44,7 @@
 import junitparams.Parameters;
 
 import org.junit.Rule;
+import org.junit.After;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -115,14 +116,33 @@
         return params;
     }
 
-    private ClientEndpoint client;
-    private ServerEndpoint server;
+    private SocketPair socketPair = new SocketPair();
     private ExecutorService executor;
     private Future<?> receivingFuture;
     private volatile boolean stopping;
     private static final AtomicLong bytesCounter = new AtomicLong();
     private AtomicBoolean recording = new AtomicBoolean();
 
+    private static class SocketPair implements AutoCloseable {
+        public ClientEndpoint client;
+        public ServerEndpoint server;
+
+        SocketPair() {
+            client = null;
+            server = null;
+        }
+
+        @Override
+        public void close() {
+            if (client != null) {
+                client.stop();
+            }
+            if (server != null) {
+                server.stop();
+            }
+        }
+    }
+
     private void setup(final Config config) throws Exception {
         recording.set(false);
 
@@ -130,9 +150,10 @@
 
         final ChannelType channelType = config.channelType();
 
-        server = config.serverFactory().newServer(config.messageSize(),
+        socketPair.server = config.serverFactory().newServer(config.messageSize(),
             new String[] {"TLSv1.3", "TLSv1.2"}, ciphers(config));
-        server.setMessageProcessor(new MessageProcessor() {
+        socketPair.server.init();
+        socketPair.server.setMessageProcessor(new MessageProcessor() {
             @Override
             public void processMessage(byte[] inMessage, int numBytes, OutputStream os) {
                 try {
@@ -151,20 +172,20 @@
             }
         });
 
-        Future<?> connectedFuture = server.start();
+        Future<?> connectedFuture = socketPair.server.start();
 
         // Always use the same client for consistency across the benchmarks.
-        client = config.clientFactory().newClient(
-                ChannelType.CHANNEL, server.port(),
+        socketPair.client = config.clientFactory().newClient(
+                ChannelType.CHANNEL, socketPair.server.port(),
                 new String[] {"TLSv1.3", "TLSv1.2"}, ciphers(config));
-        client.start();
+        socketPair.client.start();
 
         // Wait for the initial connection to complete.
         connectedFuture.get(5, TimeUnit.SECONDS);
 
         // Start the server-side streaming by sending a message to the server.
-        client.sendMessage(message);
-        client.flush();
+        socketPair.client.sendMessage(message);
+        socketPair.client.flush();
 
         executor = Executors.newSingleThreadExecutor();
         receivingFuture = executor.submit(new Runnable() {
@@ -173,7 +194,7 @@
                 Thread thread = Thread.currentThread();
                 byte[] buffer = new byte[config.messageSize()];
                 while (!stopping && !thread.isInterrupted()) {
-                    int numBytes = client.readMessage(buffer);
+                    int numBytes = socketPair.client.readMessage(buffer);
                     if (numBytes < 0) {
                         return;
                     }
@@ -191,25 +212,38 @@
     void close() throws Exception {
         stopping = true;
         // Stop and wait for sending to complete.
-        server.stop();
-        client.stop();
-        executor.shutdown();
-        receivingFuture.get(5, TimeUnit.SECONDS);
-        executor.awaitTermination(5, TimeUnit.SECONDS);
+        if (socketPair != null) {
+            socketPair.close();
+        }
+        if (executor != null) {
+            executor.shutdown();
+            executor.awaitTermination(5, TimeUnit.SECONDS);
+        }
+        if (receivingFuture != null) {
+            receivingFuture.get(5, TimeUnit.SECONDS);
+        }
     }
 
     @Test
     @Parameters(method = "getParams")
     public void throughput(Config config) throws Exception {
-        setup(config);
-        BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
-        while (state.keepRunning()) {
-          recording.set(true);
-          while (bytesCounter.get() < config.messageSize()) {
-          }
-          bytesCounter.set(0);
-          recording.set(false);
+        try {
+            setup(config);
+            BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+            while (state.keepRunning()) {
+                recording.set(true);
+                while (bytesCounter.get() < config.messageSize()) {
+                }
+                bytesCounter.set(0);
+                recording.set(false);
+            }
+        } finally {
+            close();
         }
+    }
+
+    @After
+    public void tearDown() throws Exception {
         close();
     }
 
diff --git a/apct-tests/perftests/core/src/android/content/pm/SystemFeaturesPerfTest.java b/apct-tests/perftests/core/src/android/content/pm/SystemFeaturesPerfTest.java
new file mode 100644
index 0000000..43f5453
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/content/pm/SystemFeaturesPerfTest.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 android.content.pm;
+
+import android.perftests.utils.BenchmarkState;
+import android.perftests.utils.PerfStatusReporter;
+
+import androidx.test.filters.LargeTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.pm.RoSystemFeatures;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class SystemFeaturesPerfTest {
+    // As each query is relatively cheap, add an inner iteration loop to reduce execution noise.
+    private static final int NUM_ITERATIONS = 10;
+
+    @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();
+
+    @Test
+    public void hasSystemFeature_PackageManager() {
+        final PackageManager pm =
+                InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageManager();
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            for (int i = 0; i < NUM_ITERATIONS; ++i) {
+                pm.hasSystemFeature(PackageManager.FEATURE_WATCH);
+                pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK);
+                pm.hasSystemFeature(PackageManager.FEATURE_IPSEC_TUNNELS);
+                pm.hasSystemFeature(PackageManager.FEATURE_AUTOFILL);
+                pm.hasSystemFeature("com.android.custom.feature.1");
+                pm.hasSystemFeature("foo");
+                pm.hasSystemFeature("");
+            }
+        }
+    }
+
+    @Test
+    public void hasSystemFeature_SystemFeaturesCache() {
+        final PackageManager pm =
+                InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageManager();
+        final SystemFeaturesCache cache =
+                new SystemFeaturesCache(Arrays.asList(pm.getSystemAvailableFeatures()));
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            for (int i = 0; i < NUM_ITERATIONS; ++i) {
+                cache.maybeHasFeature(PackageManager.FEATURE_WATCH, 0);
+                cache.maybeHasFeature(PackageManager.FEATURE_LEANBACK, 0);
+                cache.maybeHasFeature(PackageManager.FEATURE_IPSEC_TUNNELS, 0);
+                cache.maybeHasFeature(PackageManager.FEATURE_AUTOFILL, 0);
+                cache.maybeHasFeature("com.android.custom.feature.1", 0);
+                cache.maybeHasFeature("foo", 0);
+                cache.maybeHasFeature("", 0);
+            }
+        }
+    }
+
+    @Test
+    public void hasSystemFeature_RoSystemFeatures() {
+        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
+        while (state.keepRunning()) {
+            for (int i = 0; i < NUM_ITERATIONS; ++i) {
+                RoSystemFeatures.maybeHasFeature(PackageManager.FEATURE_WATCH, 0);
+                RoSystemFeatures.maybeHasFeature(PackageManager.FEATURE_LEANBACK, 0);
+                RoSystemFeatures.maybeHasFeature(PackageManager.FEATURE_IPSEC_TUNNELS, 0);
+                RoSystemFeatures.maybeHasFeature(PackageManager.FEATURE_AUTOFILL, 0);
+                RoSystemFeatures.maybeHasFeature("com.android.custom.feature.1", 0);
+                RoSystemFeatures.maybeHasFeature("foo", 0);
+                RoSystemFeatures.maybeHasFeature("", 0);
+            }
+        }
+    }
+}
diff --git a/apct-tests/perftests/windowmanager/src/android/wm/InTaskTransitionTest.java b/apct-tests/perftests/windowmanager/src/android/wm/InTaskTransitionTest.java
index 2d2cf1c8..b04d08f 100644
--- a/apct-tests/perftests/windowmanager/src/android/wm/InTaskTransitionTest.java
+++ b/apct-tests/perftests/windowmanager/src/android/wm/InTaskTransitionTest.java
@@ -34,11 +34,20 @@
 import org.junit.Rule;
 import org.junit.Test;
 
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+
 /** Measure the performance of warm launch activity in the same task. */
 public class InTaskTransitionTest extends WindowManagerPerfTestBase
         implements RemoteCallback.OnResultListener {
 
     private static final long TIMEOUT_MS = 5000;
+    private static final String LOG_SEPARATOR = "LOG_SEPARATOR";
 
     @Rule
     public final PerfManualStatusReporter mPerfStatusReporter = new PerfManualStatusReporter();
@@ -62,6 +71,7 @@
 
         final ManualBenchmarkState state = mPerfStatusReporter.getBenchmarkState();
         long measuredTimeNs = 0;
+        long firstStartTime = 0;
 
         boolean readerStarted = false;
         while (state.keepRunning(measuredTimeNs)) {
@@ -70,6 +80,10 @@
                 readerStarted = true;
             }
             final long startTime = SystemClock.elapsedRealtimeNanos();
+            if (readerStarted && firstStartTime == 0) {
+                firstStartTime = startTime;
+                executeShellCommand("log -t " + LOG_SEPARATOR + " " + firstStartTime);
+            }
             activity.startActivity(next);
             synchronized (mMetricsReader) {
                 try {
@@ -89,6 +103,7 @@
                 state.addExtraResult("windowsDrawnDelayMs", metrics.mWindowsDrawnDelayMs);
             }
         }
+        addExtraTransitionInfo(firstStartTime, state);
     }
 
     @Override
@@ -99,6 +114,46 @@
         }
     }
 
+    private void addExtraTransitionInfo(long startTime, ManualBenchmarkState state) {
+        final ProcessBuilder pb = new ProcessBuilder("sh");
+        final String startLine = String.valueOf(startTime);
+        final String commitTimeStr = " commit=";
+        boolean foundStartLine = false;
+        try {
+            final Process process = pb.start();
+            final InputStream in = process.getInputStream();
+            final PrintWriter out = new PrintWriter(new BufferedWriter(
+                    new OutputStreamWriter(process.getOutputStream())), true /* autoFlush */);
+            out.println("logcat -v brief -d *:S WindowManager:V " + LOG_SEPARATOR + ":I"
+                    + " | grep -e 'Finish Transition' -e " + LOG_SEPARATOR);
+            out.println("exit");
+
+            String line;
+            try (BufferedReader reader = new BufferedReader(new InputStreamReader(in))) {
+                while ((line = reader.readLine()) != null) {
+                    if (!foundStartLine) {
+                        if (line.contains(startLine)) {
+                            foundStartLine = true;
+                        }
+                        continue;
+                    }
+                    final int strPos = line.indexOf(commitTimeStr);
+                    if (strPos < 0) {
+                        continue;
+                    }
+                    final int endPos = line.indexOf("ms", strPos);
+                    if (endPos > strPos) {
+                        final int commitDelayMs = Math.round(Float.parseFloat(
+                                line.substring(strPos + commitTimeStr.length(), endPos)));
+                        state.addExtraResult("commitDelayMs", commitDelayMs);
+                    }
+                }
+            }
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
     /** The test activity runs on a different process to trigger metrics logs. */
     public static class TestActivity extends Activity implements Runnable {
         static final String CALLBACK = "callback";
diff --git a/apex/jobscheduler/framework/aconfig/job.aconfig b/apex/jobscheduler/framework/aconfig/job.aconfig
index 8b1a40c..a0dfd19 100644
--- a/apex/jobscheduler/framework/aconfig/job.aconfig
+++ b/apex/jobscheduler/framework/aconfig/job.aconfig
@@ -17,14 +17,6 @@
 }
 
 flag {
-    name: "backup_jobs_exemption"
-    is_exported: true
-    namespace: "backstage_power"
-    description: "Introduce a new RUN_BACKUP_JOBS permission and exemption logic allowing for longer running jobs for apps whose primary purpose is to backup or sync content."
-    bug: "318731461"
-}
-
-flag {
    name: "handle_abandoned_jobs"
    namespace: "backstage_power"
    description: "Detect, report and take action on jobs that maybe abandoned by the app without calling jobFinished."
diff --git a/boot/preloaded-classes b/boot/preloaded-classes
index b83bd4e..9926aef 100644
--- a/boot/preloaded-classes
+++ b/boot/preloaded-classes
@@ -6470,6 +6470,7 @@
 android.os.connectivity.WifiBatteryStats$1
 android.os.connectivity.WifiBatteryStats
 android.os.flagging.AconfigPackage
+android.os.flagging.PlatformAconfigPackage
 android.os.health.HealthKeys$Constant
 android.os.health.HealthKeys$Constants
 android.os.health.HealthKeys$SortedIntArray
diff --git a/config/preloaded-classes b/config/preloaded-classes
index e53c78f..bdd95f8 100644
--- a/config/preloaded-classes
+++ b/config/preloaded-classes
@@ -6474,6 +6474,7 @@
 android.os.connectivity.WifiBatteryStats$1
 android.os.connectivity.WifiBatteryStats
 android.os.flagging.AconfigPackage
+android.os.flagging.PlatformAconfigPackage
 android.os.health.HealthKeys$Constant
 android.os.health.HealthKeys$Constants
 android.os.health.HealthKeys$SortedIntArray
diff --git a/core/api/current.txt b/core/api/current.txt
index d0b3a51..6707c15d 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -8905,7 +8905,7 @@
   @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public abstract class AppFunctionService extends android.app.Service {
     ctor public AppFunctionService();
     method @NonNull public final android.os.IBinder onBind(@Nullable android.content.Intent);
-    method @MainThread public abstract void onExecuteFunction(@NonNull android.app.appfunctions.ExecuteAppFunctionRequest, @NonNull String, @NonNull android.os.CancellationSignal, @NonNull android.os.OutcomeReceiver<android.app.appfunctions.ExecuteAppFunctionResponse,android.app.appfunctions.AppFunctionException>);
+    method @MainThread public abstract void onExecuteFunction(@NonNull android.app.appfunctions.ExecuteAppFunctionRequest, @NonNull String, @NonNull android.content.pm.SigningInfo, @NonNull android.os.CancellationSignal, @NonNull android.os.OutcomeReceiver<android.app.appfunctions.ExecuteAppFunctionResponse,android.app.appfunctions.AppFunctionException>);
     field @NonNull public static final String SERVICE_INTERFACE = "android.app.appfunctions.AppFunctionService";
   }
 
@@ -8947,18 +8947,19 @@
     method public android.content.ClipData getClipData();
     method public android.os.Bundle getExtras();
     method public android.content.Intent getIntent();
+    method @FlaggedApi("com.android.window.flags.enable_desktop_windowing_app_to_web_education") @Nullable public android.net.Uri getSessionTransferUri();
     method public String getStructuredData();
     method public android.net.Uri getWebUri();
     method public boolean isAppProvidedIntent();
     method public boolean isAppProvidedWebUri();
     method public void setClipData(android.content.ClipData);
     method public void setIntent(android.content.Intent);
+    method @FlaggedApi("com.android.window.flags.enable_desktop_windowing_app_to_web_education") public void setSessionTransferUri(@Nullable android.net.Uri);
     method public void setStructuredData(String);
     method public void setWebUri(android.net.Uri);
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.app.assist.AssistContent> CREATOR;
     field @FlaggedApi("android.app.appfunctions.flags.enable_app_function_manager") public static final String EXTRA_APP_FUNCTION_DATA = "android.app.assist.extra.APP_FUNCTION_DATA";
-    field @FlaggedApi("com.android.window.flags.enable_desktop_windowing_app_to_web_education") public static final String EXTRA_SESSION_TRANSFER_WEB_URI = "android.app.assist.extra.SESSION_TRANSFER_WEB_URI";
   }
 
   public class AssistStructure implements android.os.Parcelable {
@@ -9190,8 +9191,9 @@
 package android.app.jank {
 
   @FlaggedApi("android.app.jank.detailed_app_jank_metrics_api") public final class AppJankStats {
-    ctor public AppJankStats(int, @NonNull String, @Nullable String, @Nullable String, long, long, @NonNull android.app.jank.RelativeFrameTimeHistogram);
+    ctor public AppJankStats(int, @NonNull String, @Nullable String, @Nullable String, @Nullable String, long, long, @NonNull android.app.jank.RelativeFrameTimeHistogram);
     method public long getJankyFrameCount();
+    method @Nullable public String getNavigationComponent();
     method @NonNull public android.app.jank.RelativeFrameTimeHistogram getRelativeFrameTimeHistogram();
     method public long getTotalFrameCount();
     method public int getUid();
@@ -27202,7 +27204,7 @@
     field public static final int AMBIENT_BACKLIGHT_EVENT_DISABLED = 2; // 0x2
     field public static final int AMBIENT_BACKLIGHT_EVENT_ENABLED = 1; // 0x1
     field public static final int AMBIENT_BACKLIGHT_EVENT_INTERRUPTED = 4; // 0x4
-    field public static final int AMBIENT_BACKLIGHT_EVENT_METADATA = 3; // 0x3
+    field public static final int AMBIENT_BACKLIGHT_EVENT_METADATA_AVAILABLE = 3; // 0x3
     field @NonNull public static final android.os.Parcelable.Creator<android.media.quality.AmbientBacklightEvent> CREATOR;
   }
 
@@ -27210,13 +27212,15 @@
     ctor public AmbientBacklightMetadata(@NonNull String, int, int, int, int, int, @NonNull int[]);
     method public int describeContents();
     method public int getColorFormat();
-    method public int getCompressAlgorithm();
-    method @IntRange(from=0, to=128) public int getHorizontalZonesNumber();
+    method public int getCompressionAlgorithm();
+    method @IntRange(from=0, to=128) public int getHorizontalZonesCount();
     method @NonNull public String getPackageName();
     method public int getSource();
-    method @IntRange(from=0, to=80) public int getVerticalZonesNumber();
-    method @NonNull public int[] getZonesColors();
+    method @IntRange(from=0, to=80) public int getVerticalZonesCount();
+    method @NonNull public int[] getZoneColors();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field public static final int ALGORITHM_NONE = 0; // 0x0
+    field public static final int ALGORITHM_RLE = 1; // 0x1
     field @NonNull public static final android.os.Parcelable.Creator<android.media.quality.AmbientBacklightMetadata> CREATOR;
   }
 
@@ -27224,15 +27228,13 @@
     ctor public AmbientBacklightSettings(int, int, int, int, int, boolean, int);
     method public int describeContents();
     method public int getColorFormat();
-    method @IntRange(from=0) public int getHorizontalZonesNumber();
+    method @IntRange(from=0) public int getHorizontalZonesCount();
     method @IntRange(from=1) public int getMaxFps();
     method public int getSource();
     method public int getThreshold();
-    method @IntRange(from=0) public int getVerticalZonesNumber();
+    method @IntRange(from=0) public int getVerticalZonesCount();
     method public boolean isLetterboxOmitted();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field public static final int ALGORITHM_NONE = 0; // 0x0
-    field public static final int ALGORITHM_RLE = 1; // 0x1
     field @NonNull public static final android.os.Parcelable.Creator<android.media.quality.AmbientBacklightSettings> CREATOR;
     field public static final int SOURCE_AUDIO = 1; // 0x1
     field public static final int SOURCE_AUDIO_VIDEO = 3; // 0x3
@@ -27241,6 +27243,10 @@
   }
 
   @FlaggedApi("android.media.tv.flags.media_quality_fw") public class MediaQualityContract {
+    field public static final String LEVEL_HIGH = "level_high";
+    field public static final String LEVEL_LOW = "level_low";
+    field public static final String LEVEL_MEDIUM = "level_medium";
+    field public static final String LEVEL_OFF = "level_off";
   }
 
   public static final class MediaQualityContract.PictureQuality {
@@ -27273,20 +27279,41 @@
   }
 
   public static final class MediaQualityContract.SoundQuality {
+    field public static final String PARAMETER_AUTO_VOLUME_CONTROL = "auto_volume_control";
     field public static final String PARAMETER_BALANCE = "balance";
     field public static final String PARAMETER_BASS = "bass";
+    field public static final String PARAMETER_DIALOGUE_ENHANCER = "dialogue_enhancer";
+    field public static final String PARAMETER_DIGITAL_OUTPUT_DELAY_MILLIS = "digital_output_delay_millis";
+    field public static final String PARAMETER_DIGITAL_OUTPUT_MODE = "digital_output_mode";
+    field public static final String PARAMETER_DOLBY_AUDIO_PROCESSING_DOLBY_ATMOS = "dolby_audio_processing_dolby_atmos";
+    field public static final String PARAMETER_DOLBY_AUDIO_PROCESSING_SOUND_MODE = "dolby_audio_processing_sound_mode";
+    field public static final String PARAMETER_DOLBY_AUDIO_PROCESSING_SURROUND_VIRTUALIZER = "dolby_audio_processing_surround_virtualizer";
+    field public static final String PARAMETER_DOLBY_AUDIO_PROCESSING_VOLUME_LEVELER = "dolby_audio_processing_volume_leveler";
+    field public static final String PARAMETER_DOWN_MIX_MODE = "down_mix_mode";
+    field public static final String PARAMETER_DTS_DRC = "dts_drc";
+    field public static final String PARAMETER_DTS_VIRTUAL_X_DEFINITION = "dts_virtual_x_definition";
+    field public static final String PARAMETER_DTS_VIRTUAL_X_DIALOG_CLARITY = "dts_virtual_x_dialog_clarity";
+    field public static final String PARAMETER_DTS_VIRTUAL_X_HEIGHT = "dts_virtual_x_height";
+    field public static final String PARAMETER_DTS_VIRTUAL_X_LIMITER = "dts_virtual_x_limiter";
+    field public static final String PARAMETER_DTS_VIRTUAL_X_TBHDX = "dts_virtual_x_tbhdx";
+    field public static final String PARAMETER_DTS_VIRTUAL_X_TRU_SURROUND_X = "dts_virtual_x_tru_surround_x";
+    field public static final String PARAMETER_DTS_VIRTUAL_X_TRU_VOLUME_HD = "dts_virtual_x_tru_volume_hd";
+    field public static final String PARAMETER_EARC = "earc";
+    field public static final String PARAMETER_SPEAKERS = "speakers";
+    field public static final String PARAMETER_SPEAKERS_DELAY_MILLIS = "speakers_delay_millis";
+    field public static final String PARAMETER_SURROUND_SOUND = "surround_sound";
     field public static final String PARAMETER_TREBLE = "treble";
   }
 
   @FlaggedApi("android.media.tv.flags.media_quality_fw") public final class MediaQualityManager {
-    method public void addActiveProcessingPictureListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.quality.MediaQualityManager.ActiveProcessingPictureListener);
+    method public void addActiveProcessingPictureListener(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.util.List<android.media.quality.ActiveProcessingPicture>>);
     method public void createPictureProfile(@NonNull android.media.quality.PictureProfile);
     method public void createSoundProfile(@NonNull android.media.quality.SoundProfile);
-    method @NonNull public java.util.List<android.media.quality.PictureProfile> getAvailablePictureProfiles(boolean);
-    method @NonNull public java.util.List<android.media.quality.SoundProfile> getAvailableSoundProfiles(boolean);
-    method @NonNull public java.util.List<android.media.quality.ParamCapability> getParamCapabilities(@NonNull java.util.List<java.lang.String>);
-    method @Nullable public android.media.quality.PictureProfile getPictureProfile(int, @NonNull String, boolean);
-    method @Nullable public android.media.quality.SoundProfile getSoundProfile(int, @NonNull String, boolean);
+    method @NonNull public java.util.List<android.media.quality.PictureProfile> getAvailablePictureProfiles(@Nullable android.media.quality.MediaQualityManager.ProfileQueryParams);
+    method @NonNull public java.util.List<android.media.quality.SoundProfile> getAvailableSoundProfiles(@Nullable android.media.quality.MediaQualityManager.ProfileQueryParams);
+    method @NonNull public java.util.List<android.media.quality.ParameterCapability> getParameterCapabilities(@NonNull java.util.List<java.lang.String>);
+    method @Nullable public android.media.quality.PictureProfile getPictureProfile(int, @NonNull String, @Nullable android.media.quality.MediaQualityManager.ProfileQueryParams);
+    method @Nullable public android.media.quality.SoundProfile getSoundProfile(int, @NonNull String, @Nullable android.media.quality.MediaQualityManager.ProfileQueryParams);
     method public boolean isAmbientBacklightEnabled();
     method public boolean isAutoPictureQualityEnabled();
     method public boolean isAutoSoundQualityEnabled();
@@ -27294,7 +27321,7 @@
     method public void registerAmbientBacklightCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.quality.MediaQualityManager.AmbientBacklightCallback);
     method public void registerPictureProfileCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.quality.MediaQualityManager.PictureProfileCallback);
     method public void registerSoundProfileCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.quality.MediaQualityManager.SoundProfileCallback);
-    method public void removeActiveProcessingPictureListener(@NonNull android.media.quality.MediaQualityManager.ActiveProcessingPictureListener);
+    method public void removeActiveProcessingPictureListener(@NonNull java.util.function.Consumer<java.util.List<android.media.quality.ActiveProcessingPicture>>);
     method public void removePictureProfile(@NonNull String);
     method public void removeSoundProfile(@NonNull String);
     method public void setAmbientBacklightEnabled(boolean);
@@ -27306,48 +27333,57 @@
     method public void updateSoundProfile(@NonNull String, @NonNull android.media.quality.SoundProfile);
   }
 
-  public static interface MediaQualityManager.ActiveProcessingPictureListener {
-    method public void onActiveProcessingPicturesChanged(@NonNull java.util.List<android.media.quality.ActiveProcessingPicture>);
-  }
-
-  public abstract static class MediaQualityManager.AmbientBacklightCallback {
-    ctor public MediaQualityManager.AmbientBacklightCallback();
+  public static interface MediaQualityManager.AmbientBacklightCallback {
     method public void onAmbientBacklightEvent(@NonNull android.media.quality.AmbientBacklightEvent);
   }
 
   public abstract static class MediaQualityManager.PictureProfileCallback {
     ctor public MediaQualityManager.PictureProfileCallback();
     method public void onError(@Nullable String, int);
-    method public void onParamCapabilitiesChanged(@Nullable String, @NonNull java.util.List<android.media.quality.ParamCapability>);
+    method public void onParameterCapabilitiesChanged(@Nullable String, @NonNull java.util.List<android.media.quality.ParameterCapability>);
     method public void onPictureProfileAdded(@NonNull String, @NonNull android.media.quality.PictureProfile);
     method public void onPictureProfileRemoved(@NonNull String, @NonNull android.media.quality.PictureProfile);
     method public void onPictureProfileUpdated(@NonNull String, @NonNull android.media.quality.PictureProfile);
   }
 
+  public static final class MediaQualityManager.ProfileQueryParams implements android.os.Parcelable {
+    method public boolean areParametersIncluded();
+    method public int describeContents();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.media.quality.MediaQualityManager.ProfileQueryParams> CREATOR;
+  }
+
+  public static final class MediaQualityManager.ProfileQueryParams.Builder {
+    ctor public MediaQualityManager.ProfileQueryParams.Builder();
+    method @NonNull public android.media.quality.MediaQualityManager.ProfileQueryParams build();
+    method @NonNull public android.media.quality.MediaQualityManager.ProfileQueryParams.Builder setParametersIncluded(boolean);
+  }
+
   public abstract static class MediaQualityManager.SoundProfileCallback {
     ctor public MediaQualityManager.SoundProfileCallback();
     method public void onError(@Nullable String, int);
-    method public void onParamCapabilitiesChanged(@Nullable String, @NonNull java.util.List<android.media.quality.ParamCapability>);
+    method public void onParameterCapabilitiesChanged(@Nullable String, @NonNull java.util.List<android.media.quality.ParameterCapability>);
     method public void onSoundProfileAdded(@NonNull String, @NonNull android.media.quality.SoundProfile);
     method public void onSoundProfileRemoved(@NonNull String, @NonNull android.media.quality.SoundProfile);
     method public void onSoundProfileUpdated(@NonNull String, @NonNull android.media.quality.SoundProfile);
   }
 
-  @FlaggedApi("android.media.tv.flags.media_quality_fw") public final class ParamCapability implements android.os.Parcelable {
+  @FlaggedApi("android.media.tv.flags.media_quality_fw") public final class ParameterCapability implements android.os.Parcelable {
     method public int describeContents();
     method @NonNull public android.os.Bundle getCapabilities();
-    method @NonNull public String getParamName();
-    method public int getParamType();
+    method @NonNull public String getParameterName();
+    method public int getParameterType();
     method public boolean isSupported();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field public static final String CAPABILITY_DEFAULT = "default";
     field public static final String CAPABILITY_ENUM = "enum";
     field public static final String CAPABILITY_MAX = "max";
     field public static final String CAPABILITY_MIN = "min";
-    field @NonNull public static final android.os.Parcelable.Creator<android.media.quality.ParamCapability> CREATOR;
+    field @NonNull public static final android.os.Parcelable.Creator<android.media.quality.ParameterCapability> CREATOR;
     field public static final int TYPE_DOUBLE = 3; // 0x3
     field public static final int TYPE_INT = 1; // 0x1
     field public static final int TYPE_LONG = 2; // 0x2
+    field public static final int TYPE_NONE = 0; // 0x0
     field public static final int TYPE_STRING = 4; // 0x4
   }
 
@@ -33623,16 +33659,23 @@
   }
 
   @FlaggedApi("android.os.cpu_gpu_headrooms") public final class CpuHeadroomParams {
-    ctor public CpuHeadroomParams();
     method public int getCalculationType();
-    method @IntRange(from=0x32, to=0x2710) public long getCalculationWindowMillis();
-    method public void setCalculationType(int);
-    method public void setCalculationWindowMillis(@IntRange(from=0x32, to=0x2710) int);
-    method public void setTids(@NonNull int...);
+    method public long getCalculationWindowMillis();
+    method @NonNull public int[] getTids();
+    method @NonNull public android.os.CpuHeadroomParams.Builder toBuilder();
     field public static final int CPU_HEADROOM_CALCULATION_TYPE_AVERAGE = 1; // 0x1
     field public static final int CPU_HEADROOM_CALCULATION_TYPE_MIN = 0; // 0x0
   }
 
+  public static final class CpuHeadroomParams.Builder {
+    ctor public CpuHeadroomParams.Builder();
+    ctor public CpuHeadroomParams.Builder(@NonNull android.os.CpuHeadroomParams);
+    method @NonNull public android.os.CpuHeadroomParams build();
+    method @NonNull public android.os.CpuHeadroomParams.Builder setCalculationType(int);
+    method @NonNull public android.os.CpuHeadroomParams.Builder setCalculationWindowMillis(@IntRange(from=1) int);
+    method @NonNull public android.os.CpuHeadroomParams.Builder setTids(@NonNull int...);
+  }
+
   public final class CpuUsageInfo implements android.os.Parcelable {
     method public int describeContents();
     method public long getActive();
@@ -33881,13 +33924,20 @@
   }
 
   @FlaggedApi("android.os.cpu_gpu_headrooms") public final class GpuHeadroomParams {
-    ctor public GpuHeadroomParams();
     method public int getCalculationType();
-    method @IntRange(from=0x32, to=0x2710) public int getCalculationWindowMillis();
-    method public void setCalculationType(int);
-    method public void setCalculationWindowMillis(@IntRange(from=0x32, to=0x2710) int);
+    method public int getCalculationWindowMillis();
     field public static final int GPU_HEADROOM_CALCULATION_TYPE_AVERAGE = 1; // 0x1
     field public static final int GPU_HEADROOM_CALCULATION_TYPE_MIN = 0; // 0x0
+    field public static final int GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX = 10000; // 0x2710
+    field public static final int GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN = 50; // 0x32
+  }
+
+  public static final class GpuHeadroomParams.Builder {
+    ctor public GpuHeadroomParams.Builder();
+    ctor public GpuHeadroomParams.Builder(@NonNull android.os.GpuHeadroomParams);
+    method @NonNull public android.os.GpuHeadroomParams build();
+    method @NonNull public android.os.GpuHeadroomParams.Builder setCalculationType(int);
+    method @NonNull public android.os.GpuHeadroomParams.Builder setCalculationWindowMillis(@IntRange(from=1) int);
   }
 
   public class Handler {
@@ -35153,9 +35203,12 @@
 
   public class SystemHealthManager {
     method @FlaggedApi("android.os.cpu_gpu_headrooms") @FloatRange(from=0.0f, to=100.0f) public float getCpuHeadroom(@Nullable android.os.CpuHeadroomParams);
+    method @FlaggedApi("android.os.cpu_gpu_headrooms") @NonNull public android.util.Pair<java.lang.Integer,java.lang.Integer> getCpuHeadroomCalculationWindowRange();
     method @FlaggedApi("android.os.cpu_gpu_headrooms") public long getCpuHeadroomMinIntervalMillis();
     method @FlaggedApi("android.os.cpu_gpu_headrooms") @FloatRange(from=0.0f, to=100.0f) public float getGpuHeadroom(@Nullable android.os.GpuHeadroomParams);
+    method @FlaggedApi("android.os.cpu_gpu_headrooms") @NonNull public android.util.Pair<java.lang.Integer,java.lang.Integer> getGpuHeadroomCalculationWindowRange();
     method @FlaggedApi("android.os.cpu_gpu_headrooms") public long getGpuHeadroomMinIntervalMillis();
+    method @FlaggedApi("android.os.cpu_gpu_headrooms") @IntRange(from=1) public int getMaxCpuHeadroomTidsSize();
     method @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public void getPowerMonitorReadings(@NonNull java.util.List<android.os.PowerMonitor>, @Nullable java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.os.PowerMonitorReadings,java.lang.RuntimeException>);
     method @FlaggedApi("com.android.server.power.optimization.power_monitor_api") public void getSupportedPowerMonitors(@Nullable java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.util.List<android.os.PowerMonitor>>);
     method public android.os.health.HealthStats takeMyUidSnapshot();
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index a60fd11..93f3119 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -581,7 +581,7 @@
 package android.accounts {
 
   public class AccountManager {
-    method @FlaggedApi("android.app.admin.flags.split_create_managed_profile_enabled") @NonNull @RequiresPermission(anyOf={android.Manifest.permission.COPY_ACCOUNTS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) public android.accounts.AccountManagerFuture<java.lang.Boolean> copyAccountToUser(@NonNull android.accounts.Account, @NonNull android.os.UserHandle, @NonNull android.os.UserHandle, @Nullable android.accounts.AccountManagerCallback<java.lang.Boolean>, @Nullable android.os.Handler);
+    method @FlaggedApi("android.app.admin.flags.split_create_managed_profile_enabled") @NonNull @RequiresPermission(anyOf={android.Manifest.permission.COPY_ACCOUNTS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) public android.accounts.AccountManagerFuture<java.lang.Boolean> copyAccountToUser(@NonNull android.accounts.Account, @NonNull android.os.UserHandle, @NonNull android.os.UserHandle, @Nullable android.os.Handler, @Nullable android.accounts.AccountManagerCallback<java.lang.Boolean>);
     method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public android.accounts.AccountManagerFuture<android.os.Bundle> finishSessionAsUser(android.os.Bundle, android.app.Activity, android.os.UserHandle, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler);
   }
 
@@ -1290,7 +1290,6 @@
 
   public class WallpaperManager {
     method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public void clearWallpaper(int, int);
-    method @FlaggedApi("android.app.customization_packs_apis") @NonNull @RequiresPermission(android.Manifest.permission.READ_WALLPAPER_INTERNAL) public android.util.SparseArray<android.graphics.Rect> getBitmapCrops(int);
     method @FlaggedApi("android.app.customization_packs_apis") public static int getOrientation(@NonNull android.graphics.Point);
     method @FloatRange(from=0.0f, to=1.0f) @RequiresPermission(android.Manifest.permission.SET_WALLPAPER_DIM_AMOUNT) public float getWallpaperDimAmount();
     method @FlaggedApi("android.app.customization_packs_apis") @Nullable public android.os.ParcelFileDescriptor getWallpaperFile(int, boolean);
@@ -7992,13 +7991,13 @@
 package android.media.quality {
 
   @FlaggedApi("android.media.tv.flags.media_quality_fw") public final class MediaQualityManager {
-    method public void addGlobalActiveProcessingPictureListener(@NonNull java.util.concurrent.Executor, @NonNull android.media.quality.MediaQualityManager.ActiveProcessingPictureListener);
+    method public void addGlobalActiveProcessingPictureListener(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.util.List<android.media.quality.ActiveProcessingPicture>>);
     method @NonNull public java.util.List<java.lang.String> getPictureProfileAllowList();
     method @NonNull public java.util.List<java.lang.String> getPictureProfilePackageNames();
-    method @NonNull public java.util.List<android.media.quality.PictureProfile> getPictureProfilesByPackage(@NonNull String, boolean);
+    method @NonNull public java.util.List<android.media.quality.PictureProfile> getPictureProfilesByPackage(@NonNull String, @Nullable android.media.quality.MediaQualityManager.ProfileQueryParams);
     method @NonNull public java.util.List<java.lang.String> getSoundProfileAllowList();
     method @NonNull public java.util.List<java.lang.String> getSoundProfilePackageNames();
-    method @NonNull public java.util.List<android.media.quality.SoundProfile> getSoundProfilesByPackage(@NonNull String, boolean);
+    method @NonNull public java.util.List<android.media.quality.SoundProfile> getSoundProfilesByPackage(@NonNull String, @Nullable android.media.quality.MediaQualityManager.ProfileQueryParams);
     method public void setAutoPictureQualityEnabled(boolean);
     method public void setAutoSoundQualityEnabled(boolean);
     method public boolean setDefaultPictureProfile(@Nullable String);
@@ -8095,16 +8094,16 @@
     method @Deprecated @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public void deleteModel(java.util.UUID);
     method public int getDetectionServiceOperationsTimeout();
     method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public android.media.soundtrigger.SoundTriggerManager.Model getModel(java.util.UUID);
-    method @FlaggedApi("android.media.soundtrigger.manager_api") @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public int getModelState(@NonNull java.util.UUID);
+    method @FlaggedApi("android.media.soundtrigger.manager_api") @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) @WorkerThread public int getModelState(@NonNull java.util.UUID);
     method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public android.hardware.soundtrigger.SoundTrigger.ModuleProperties getModuleProperties();
     method @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public int getParameter(@NonNull java.util.UUID, int);
-    method @FlaggedApi("android.media.soundtrigger.manager_api") @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public boolean isRecognitionActive(@NonNull java.util.UUID);
-    method @FlaggedApi("android.media.soundtrigger.manager_api") @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public int loadSoundModel(@NonNull android.hardware.soundtrigger.SoundTrigger.SoundModel);
+    method @FlaggedApi("android.media.soundtrigger.manager_api") @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) @WorkerThread public boolean isRecognitionActive(@NonNull java.util.UUID);
+    method @FlaggedApi("android.media.soundtrigger.manager_api") @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) @WorkerThread public int loadSoundModel(@NonNull android.hardware.soundtrigger.SoundTrigger.SoundModel);
     method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public android.hardware.soundtrigger.SoundTrigger.ModelParamRange queryParameter(@Nullable java.util.UUID, int);
     method @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public int setParameter(@Nullable java.util.UUID, int, int);
-    method @FlaggedApi("android.media.soundtrigger.manager_api") @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public int startRecognition(@NonNull java.util.UUID, @Nullable android.os.Bundle, @NonNull android.content.ComponentName, @NonNull android.hardware.soundtrigger.SoundTrigger.RecognitionConfig);
-    method @FlaggedApi("android.media.soundtrigger.manager_api") @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public int stopRecognition(@NonNull java.util.UUID);
-    method @FlaggedApi("android.media.soundtrigger.manager_api") @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public int unloadSoundModel(@NonNull java.util.UUID);
+    method @FlaggedApi("android.media.soundtrigger.manager_api") @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) @WorkerThread public int startRecognition(@NonNull java.util.UUID, @Nullable android.os.Bundle, @NonNull android.content.ComponentName, @NonNull android.hardware.soundtrigger.SoundTrigger.RecognitionConfig);
+    method @FlaggedApi("android.media.soundtrigger.manager_api") @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) @WorkerThread public int stopRecognition(@NonNull java.util.UUID);
+    method @FlaggedApi("android.media.soundtrigger.manager_api") @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) @WorkerThread public int unloadSoundModel(@NonNull java.util.UUID);
     method @Deprecated @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public void updateModel(android.media.soundtrigger.SoundTriggerManager.Model);
   }
 
@@ -8320,104 +8319,6 @@
     method public void onSetMain(boolean);
   }
 
-  @FlaggedApi("android.media.tv.flags.tif_extension_standardization") public final class TvInputServiceExtensionManager {
-    method @RequiresPermission(android.Manifest.permission.TV_INPUT_HARDWARE) public int registerExtensionIBinder(@NonNull String, @NonNull android.os.IBinder);
-    field public static final String IANALOG_ATTRIBUTE_INTERFACE = "android.media.tv.extension.analog.IAnalogAttributeInterface";
-    field public static final String IANALOG_AUDIO_INFO = "android.media.tv.extension.signal.IAnalogAudioInfo";
-    field public static final String IAUDIO_SIGNAL_INFO = "android.media.tv.extension.signal.IAudioSignalInfo";
-    field public static final String IAUDIO_SIGNAL_INFO_LISTENER = "android.media.tv.extension.signal.IAudioSignalInfoListener";
-    field public static final String IBROADCAST_TIME = "android.media.tv.extension.time.IBroadcastTime";
-    field public static final String ICAM_APP_INFO_LISTENER = "android.media.tv.extension.cam.ICamAppInfoListener";
-    field public static final String ICAM_APP_INFO_SERVICE = "android.media.tv.extension.cam.ICamAppInfoService";
-    field public static final String ICAM_DRM_INFO_LISTENER = "android.media.tv.extension.cam.ICamDrmInfoListener";
-    field public static final String ICAM_HOST_CONTROL_ASK_RELEASE_REPLY_CALLBACK = "android.media.tv.extension.cam.ICamHostControlAskReleaseReplyCallback";
-    field public static final String ICAM_HOST_CONTROL_INFO_LISTENER = "android.media.tv.extension.cam.ICamHostControlInfoListener";
-    field public static final String ICAM_HOST_CONTROL_SERVICE = "android.media.tv.extension.cam.ICamHostControlService";
-    field public static final String ICAM_HOST_CONTROL_TUNE_QUIETLY_FLAG = "android.media.tv.extension.cam.ICamHostControlTuneQuietlyFlag";
-    field public static final String ICAM_HOST_CONTROL_TUNE_QUIETLY_FLAG_LISTENER = "android.media.tv.extension.cam.ICamHostControlTuneQuietlyFlagListener";
-    field public static final String ICAM_INFO_LISTENER = "android.media.tv.extension.cam.ICamInfoListener";
-    field public static final String ICAM_MONITORING_SERVICE = "android.media.tv.extension.cam.ICamMonitoringService";
-    field public static final String ICAM_PIN_CAPABILITY_LISTENER = "android.media.tv.extension.cam.ICamPinCapabilityListener";
-    field public static final String ICAM_PIN_SERVICE = "android.media.tv.extension.cam.ICamPinService";
-    field public static final String ICAM_PIN_STATUS_LISTENER = "android.media.tv.extension.cam.ICamPinStatusListener";
-    field public static final String ICAM_PROFILE_INTERFACE = "android.media.tv.extension.cam.ICamProfileInterface";
-    field public static final String ICHANNEL_LIST_TRANSFER = "android.media.tv.extension.servicedb.IChannelListTransfer";
-    field public static final String ICHANNEL_TUNED_INTERFACE = "android.media.tv.extension.tune.IChannelTunedInterface";
-    field public static final String ICHANNEL_TUNED_LISTENER = "android.media.tv.extension.tune.IChannelTunedListener";
-    field public static final String ICI_OPERATOR_INTERFACE = "android.media.tv.extension.cam.ICiOperatorInterface";
-    field public static final String ICI_OPERATOR_LISTENER = "android.media.tv.extension.cam.ICiOperatorListener";
-    field public static final String ICLIENT_TOKEN = "android.media.tv.extension.clienttoken.IClientToken";
-    field public static final String ICONTENT_CONTROL_SERVICE = "android.media.tv.extension.cam.IContentControlService";
-    field public static final String IDATA_SERVICE_SIGNAL_INFO = "android.media.tv.extension.teletext.IDataServiceSignalInfo";
-    field public static final String IDATA_SERVICE_SIGNAL_INFO_LISTENER = "android.media.tv.extension.teletext.IDataServiceSignalInfoListener";
-    field public static final String IDELETE_RECORDED_CONTENTS_CALLBACK = "android.media.tv.extension.pvr.IDeleteRecordedContentsCallback";
-    field public static final String IDOWNLOADABLE_RATING_TABLE_MONITOR = "android.media.tv.extension.rating.IDownloadableRatingTableMonitor";
-    field public static final String IENTER_MENU_ERROR_CALLBACK = "android.media.tv.extension.cam.IEnterMenuErrorCallback";
-    field public static final String IEVENT_DOWNLOAD = "android.media.tv.extension.event.IEventDownload";
-    field public static final String IEVENT_DOWNLOAD_LISTENER = "android.media.tv.extension.event.IEventDownloadListener";
-    field public static final String IEVENT_DOWNLOAD_SESSION = "android.media.tv.extension.event.IEventDownloadSession";
-    field public static final String IEVENT_MONITOR = "android.media.tv.extension.event.IEventMonitor";
-    field public static final String IEVENT_MONITOR_LISTENER = "android.media.tv.extension.event.IEventMonitorListener";
-    field public static final String IFAVORITE_NETWORK = "android.media.tv.extension.scan.IFavoriteNetwork";
-    field public static final String IFAVORITE_NETWORK_LISTENER = "android.media.tv.extension.scan.IFavoriteNetworkListener";
-    field public static final String IGET_INFO_RECORDED_CONTENTS_CALLBACK = "android.media.tv.extension.pvr.IGetInfoRecordedContentsCallback";
-    field public static final String IHDMI_SIGNAL_INFO_LISTENER = "android.media.tv.extension.signal.IHdmiSignalInfoListener";
-    field public static final String IHDMI_SIGNAL_INTERFACE = "android.media.tv.extension.signal.IHdmiSignalInterface";
-    field public static final String IHDPLUS_INFO = "android.media.tv.extension.scan.IHDPlusInfo";
-    field public static final String ILCNV2_CHANNEL_LIST = "android.media.tv.extension.scan.ILcnV2ChannelList";
-    field public static final String ILCNV2_CHANNEL_LIST_LISTENER = "android.media.tv.extension.scan.ILcnV2ChannelListListener";
-    field public static final String ILCN_CONFLICT = "android.media.tv.extension.scan.ILcnConflict";
-    field public static final String ILCN_CONFLICT_LISTENER = "android.media.tv.extension.scan.ILcnConflictListener";
-    field public static final String IMMI_INTERFACE = "android.media.tv.extension.cam.IMmiInterface";
-    field public static final String IMMI_SESSION = "android.media.tv.extension.cam.IMmiSession";
-    field public static final String IMMI_STATUS_CALLBACK = "android.media.tv.extension.cam.IMmiStatusCallback";
-    field public static final String IMUX_TUNE = "android.media.tv.extension.tune.IMuxTune";
-    field public static final String IMUX_TUNE_SESSION = "android.media.tv.extension.tune.IMuxTuneSession";
-    field public static final String IOAD_UPDATE_INTERFACE = "android.media.tv.extension.oad.IOadUpdateInterface";
-    field public static final String IOPERATOR_DETECTION = "android.media.tv.extension.scan.IOperatorDetection";
-    field public static final String IOPERATOR_DETECTION_LISTENER = "android.media.tv.extension.scan.IOperatorDetectionListener";
-    field public static final String IPMT_RATING_INTERFACE = "android.media.tv.extension.rating.IPmtRatingInterface";
-    field public static final String IPMT_RATING_LISTENER = "android.media.tv.extension.rating.IPmtRatingListener";
-    field public static final String IPROGRAM_INFO = "android.media.tv.extension.rating.IProgramInfo";
-    field public static final String IPROGRAM_INFO_LISTENER = "android.media.tv.extension.rating.IProgramInfoListener";
-    field public static final String IRATING_INTERFACE = "android.media.tv.extension.rating.IRatingInterface";
-    field public static final String IRECORDED_CONTENTS = "android.media.tv.extension.pvr.IRecordedContents";
-    field public static final String IREGION_CHANNEL_LIST = "android.media.tv.extension.scan.IRegionChannelList";
-    field public static final String IREGION_CHANNEL_LIST_LISTENER = "android.media.tv.extension.scan.IRegionChannelListListener";
-    field public static final String ISCAN_BACKGROUND_SERVICE_UPDATE = "android.media.tv.extension.scanbsu.IScanBackgroundServiceUpdate";
-    field public static final String ISCAN_BACKGROUND_SERVICE_UPDATE_LISTENER = "android.media.tv.extension.scanbsu.IScanBackgroundServiceUpdateListener";
-    field public static final String ISCAN_INTERFACE = "android.media.tv.extension.scan.IScanInterface";
-    field public static final String ISCAN_LISTENER = "android.media.tv.extension.scan.IScanListener";
-    field public static final String ISCAN_SAT_SEARCH = "android.media.tv.extension.scan.IScanSatSearch";
-    field public static final String ISCAN_SESSION = "android.media.tv.extension.scan.IScanSession";
-    field public static final String ISCREEN_MODE_SETTINGS = "android.media.tv.extension.screenmode.IScreenModeSettings";
-    field public static final String ISERVICE_LIST = "android.media.tv.extension.servicedb.IServiceList";
-    field public static final String ISERVICE_LIST_EDIT = "android.media.tv.extension.servicedb.IServiceListEdit";
-    field public static final String ISERVICE_LIST_EDIT_LISTENER = "android.media.tv.extension.servicedb.IServiceListEditListener";
-    field public static final String ISERVICE_LIST_EXPORT_LISTENER = "android.media.tv.extension.servicedb.IServiceListExportListener";
-    field public static final String ISERVICE_LIST_EXPORT_SESSION = "android.media.tv.extension.servicedb.IServiceListExportSession";
-    field public static final String ISERVICE_LIST_IMPORT_LISTENER = "android.media.tv.extension.servicedb.IServiceListImportListener";
-    field public static final String ISERVICE_LIST_IMPORT_SESSION = "android.media.tv.extension.servicedb.IServiceListImportSession";
-    field public static final String ISERVICE_LIST_SET_CHANNEL_LIST_LISTENER = "android.media.tv.extension.servicedb.IServiceListSetChannelListListener";
-    field public static final String ISERVICE_LIST_SET_CHANNEL_LIST_SESSION = "android.media.tv.extension.servicedb.IServiceListSetChannelListSession";
-    field public static final String ISERVICE_LIST_TRANSFER_INTERFACE = "android.media.tv.extension.servicedb.IServiceListTransferInterface";
-    field public static final String ITARGET_REGION = "android.media.tv.extension.scan.ITargetRegion";
-    field public static final String ITARGET_REGION_LISTENER = "android.media.tv.extension.scan.ITargetRegionListener";
-    field public static final String ITELETEXT_PAGE_SUB_CODE = "android.media.tv.extension.teletext.ITeletextPageSubCode";
-    field public static final String ITKGS_INFO = "android.media.tv.extension.scan.ITkgsInfo";
-    field public static final String ITKGS_INFO_LISTENER = "android.media.tv.extension.scan.ITkgsInfoListener";
-    field public static final String ITUNER_FRONTEND_SIGNAL_INFO_INTERFACE = "android.media.tv.extension.signal.ITunerFrontendSignalInfoInterface";
-    field public static final String ITUNER_FRONTEND_SIGNAL_INFO_LISTENER = "android.media.tv.extension.signal.ITunerFrontendSignalInfoListener";
-    field public static final String IVBI_RATING_INTERFACE = "android.media.tv.extension.rating.IVbiRatingInterface";
-    field public static final String IVBI_RATING_LISTENER = "android.media.tv.extension.rating.IVbiRatingListener";
-    field public static final String IVIDEO_SIGNAL_INFO = "android.media.tv.extension.signal.IVideoSignalInfo";
-    field public static final String IVIDEO_SIGNAL_INFO_LISTENER = "android.media.tv.extension.signal.IVideoSignalInfoListener";
-    field public static final int REGISTER_FAIL_IMPLEMENTATION_NOT_STANDARDIZED = 2; // 0x2
-    field public static final int REGISTER_FAIL_NAME_NOT_STANDARDIZED = 1; // 0x1
-    field public static final int REGISTER_FAIL_REMOTE_EXCEPTION = 3; // 0x3
-    field public static final int REGISTER_SUCCESS = 0; // 0x0
-  }
-
   public abstract static class TvRecordingClient.RecordingCallback {
     method public void onEvent(String, String, android.os.Bundle);
   }
@@ -15143,6 +15044,7 @@
     method public int getCellularIdentifier();
     method public int getNasProtocolMessage();
     method @NonNull public String getPlmn();
+    method @FlaggedApi("com.android.internal.telephony.flags.vendor_specific_cellular_identifier_disclosure_indications") public boolean isBenign();
     method public boolean isEmergency();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field public static final int CELLULAR_IDENTIFIER_IMEI = 2; // 0x2
@@ -15160,6 +15062,8 @@
     field public static final int NAS_PROTOCOL_MESSAGE_IMSI_DETACH_INDICATION = 11; // 0xb
     field public static final int NAS_PROTOCOL_MESSAGE_LOCATION_UPDATE_REQUEST = 5; // 0x5
     field public static final int NAS_PROTOCOL_MESSAGE_REGISTRATION_REQUEST = 7; // 0x7
+    field @FlaggedApi("com.android.internal.telephony.flags.vendor_specific_cellular_identifier_disclosure_indications") public static final int NAS_PROTOCOL_MESSAGE_THREAT_IDENTIFIER_FALSE = 12; // 0xc
+    field @FlaggedApi("com.android.internal.telephony.flags.vendor_specific_cellular_identifier_disclosure_indications") public static final int NAS_PROTOCOL_MESSAGE_THREAT_IDENTIFIER_TRUE = 13; // 0xd
     field public static final int NAS_PROTOCOL_MESSAGE_TRACKING_AREA_UPDATE_REQUEST = 4; // 0x4
     field public static final int NAS_PROTOCOL_MESSAGE_UNKNOWN = 0; // 0x0
   }
@@ -15956,7 +15860,7 @@
     method @RequiresPermission(android.Manifest.permission.READ_PRECISE_PHONE_STATE) public default void onCallStatesChanged(@NonNull java.util.List<android.telephony.CallState>);
   }
 
-  @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static interface TelephonyCallback.CarrierRoamingNtnModeListener {
+  @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public static interface TelephonyCallback.CarrierRoamingNtnListener {
     method public default void onCarrierRoamingNtnAvailableServicesChanged(@NonNull int[]);
     method public default void onCarrierRoamingNtnEligibleStateChanged(boolean);
     method public void onCarrierRoamingNtnModeChanged(boolean);
@@ -18634,9 +18538,9 @@
     method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public void onSatelliteCapabilitiesChanged(@NonNull android.telephony.satellite.SatelliteCapabilities);
   }
 
-  @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public interface SatelliteCommunicationAllowedStateCallback {
-    method public default void onSatelliteAccessConfigurationChanged(@Nullable android.telephony.satellite.SatelliteAccessConfiguration);
-    method public void onSatelliteCommunicationAllowedStateChanged(boolean);
+  @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public interface SatelliteCommunicationAccessStateCallback {
+    method public void onAccessAllowedStateChanged(boolean);
+    method public default void onAccessConfigurationChanged(@Nullable android.telephony.satellite.SatelliteAccessConfiguration);
   }
 
   @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") public final class SatelliteDatagram implements android.os.Parcelable {
@@ -18666,16 +18570,16 @@
 
   @FlaggedApi("com.android.internal.telephony.flags.satellite_state_change_listener") public final class SatelliteManager {
     method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void addAttachRestrictionForCarrier(int, int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
-    method @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void deprovisionSatellite(@NonNull java.util.List<android.telephony.satellite.SatelliteSubscriberInfo>, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
+    method @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void deprovisionSatellite(@NonNull java.util.List<android.telephony.satellite.SatelliteSubscriberInfo>, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.telephony.satellite.SatelliteManager.SatelliteException>);
     method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void deprovisionService(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
     method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @NonNull @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public java.util.Set<java.lang.Integer> getAttachRestrictionReasonsForCarrier(int);
     method @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") @NonNull @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int[] getSatelliteDisallowedReasons();
     method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @NonNull @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public java.util.List<java.lang.String> getSatellitePlmnsForCarrier(int);
     method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void pollPendingDatagrams(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
-    method @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void provisionSatellite(@NonNull java.util.List<android.telephony.satellite.SatelliteSubscriberInfo>, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
+    method @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void provisionSatellite(@NonNull java.util.List<android.telephony.satellite.SatelliteSubscriberInfo>, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Void,android.telephony.satellite.SatelliteManager.SatelliteException>);
     method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void provisionService(@NonNull String, @NonNull byte[], @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
     method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForCapabilitiesChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteCapabilitiesCallback);
-    method @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForCommunicationAllowedStateChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteCommunicationAllowedStateCallback);
+    method @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForCommunicationAccessStateChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteCommunicationAccessStateCallback);
     method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForIncomingDatagram(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteDatagramCallback);
     method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForModemStateChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteModemStateCallback);
     method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void registerForNtnSignalStrengthChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.NtnSignalStrengthCallback);
@@ -18704,7 +18608,7 @@
     method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void startTransmissionUpdates(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>, @NonNull android.telephony.satellite.SatelliteTransmissionUpdateCallback);
     method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void stopTransmissionUpdates(@NonNull android.telephony.satellite.SatelliteTransmissionUpdateCallback, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
     method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForCapabilitiesChanged(@NonNull android.telephony.satellite.SatelliteCapabilitiesCallback);
-    method @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForCommunicationAllowedStateChanged(@NonNull android.telephony.satellite.SatelliteCommunicationAllowedStateCallback);
+    method @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForCommunicationAccessStateChanged(@NonNull android.telephony.satellite.SatelliteCommunicationAccessStateCallback);
     method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForIncomingDatagram(@NonNull android.telephony.satellite.SatelliteDatagramCallback);
     method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForModemStateChanged(@NonNull android.telephony.satellite.SatelliteModemStateCallback);
     method @FlaggedApi("com.android.internal.telephony.flags.oem_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void unregisterForNtnSignalStrengthChanged(@NonNull android.telephony.satellite.NtnSignalStrengthCallback);
@@ -18833,9 +18737,9 @@
     method public int describeContents();
     method public int getCarrierId();
     method @NonNull public String getNiddApn();
-    method public int getSubId();
     method @NonNull public String getSubscriberId();
     method public int getSubscriberIdType();
+    method public int getSubscriptionId();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.telephony.satellite.SatelliteSubscriberInfo> CREATOR;
     field public static final int SUBSCRIBER_ID_TYPE_ICCID = 0; // 0x0
@@ -18847,9 +18751,9 @@
     method @NonNull public android.telephony.satellite.SatelliteSubscriberInfo build();
     method @NonNull public android.telephony.satellite.SatelliteSubscriberInfo.Builder setCarrierId(int);
     method @NonNull public android.telephony.satellite.SatelliteSubscriberInfo.Builder setNiddApn(@NonNull String);
-    method @NonNull public android.telephony.satellite.SatelliteSubscriberInfo.Builder setSubId(int);
     method @NonNull public android.telephony.satellite.SatelliteSubscriberInfo.Builder setSubscriberId(@NonNull String);
     method @NonNull public android.telephony.satellite.SatelliteSubscriberInfo.Builder setSubscriberIdType(int);
+    method @NonNull public android.telephony.satellite.SatelliteSubscriberInfo.Builder setSubscriptionId(int);
   }
 
   @FlaggedApi("com.android.internal.telephony.flags.satellite_system_apis") public final class SatelliteSubscriberProvisionStatus implements android.os.Parcelable {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index ed8042d..a352d9d 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -2,7 +2,7 @@
 package android {
 
   public static final class Manifest.permission {
-    field @FlaggedApi("com.android.server.accessibility.motion_event_observing") public static final String ACCESSIBILITY_MOTION_EVENT_OBSERVING = "android.permission.ACCESSIBILITY_MOTION_EVENT_OBSERVING";
+    field public static final String ACCESSIBILITY_MOTION_EVENT_OBSERVING = "android.permission.ACCESSIBILITY_MOTION_EVENT_OBSERVING";
     field public static final String ACCESS_NOTIFICATIONS = "android.permission.ACCESS_NOTIFICATIONS";
     field public static final String ACTIVITY_EMBEDDING = "android.permission.ACTIVITY_EMBEDDING";
     field public static final String ADJUST_RUNTIME_PERMISSIONS_POLICY = "android.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY";
@@ -536,6 +536,7 @@
     method @Nullable public android.graphics.Bitmap getBitmap();
     method @Nullable public android.graphics.Bitmap getBitmapAsUser(int, boolean, int);
     method @FlaggedApi("com.android.window.flags.multi_crop") @NonNull @RequiresPermission(android.Manifest.permission.READ_WALLPAPER_INTERNAL) public java.util.List<android.graphics.Rect> getBitmapCrops(@NonNull java.util.List<android.graphics.Point>, int, boolean);
+    method @FlaggedApi("android.app.customization_packs_apis") @NonNull @RequiresPermission(android.Manifest.permission.READ_WALLPAPER_INTERNAL) public android.util.SparseArray<android.graphics.Rect> getBitmapCrops(int);
     method @FlaggedApi("com.android.window.flags.multi_crop") @NonNull public java.util.List<android.graphics.Rect> getBitmapCrops(@NonNull android.graphics.Point, @NonNull java.util.List<android.graphics.Point>, @Nullable java.util.Map<android.graphics.Point,android.graphics.Rect>);
     method public boolean isLockscreenLiveWallpaperEnabled();
     method @Nullable public android.graphics.Rect peekBitmapDimensions();
@@ -2113,6 +2114,11 @@
     method public boolean isAidlHal();
   }
 
+  public static final class MediaMuxer.OutputFormat {
+    field public static final int MUXER_OUTPUT_FIRST = 0; // 0x0
+    field public static final int MUXER_OUTPUT_LAST = 4; // 0x4
+  }
+
   public final class MediaRoute2Info implements android.os.Parcelable {
     method @NonNull public String getOriginalId();
   }
@@ -3409,6 +3415,10 @@
     method public void onBindClient(@Nullable android.content.Intent);
   }
 
+  public class TelecomManager {
+    method @FlaggedApi("com.android.server.telecom.flags.voip_call_monitor_refactor") public boolean hasForegroundServiceDelegation(@Nullable android.telecom.PhoneAccountHandle);
+  }
+
 }
 
 package android.telephony {
@@ -4532,7 +4542,6 @@
     field public final int displayId;
     field public final boolean isDuplicateTouchToWallpaper;
     field public final boolean isFocusable;
-    field public final boolean isPreventSplitting;
     field public final boolean isTouchable;
     field public final boolean isTrustedOverlay;
     field public final boolean isVisible;
diff --git a/core/api/test-lint-baseline.txt b/core/api/test-lint-baseline.txt
index 349b4ed..fe23517 100644
--- a/core/api/test-lint-baseline.txt
+++ b/core/api/test-lint-baseline.txt
@@ -1977,6 +1977,8 @@
     Documentation mentions 'TODO'
 
 
+UnflaggedApi: android.Manifest.permission#ACCESSIBILITY_MOTION_EVENT_OBSERVING:
+    New API must be flagged with @FlaggedApi: field android.Manifest.permission.ACCESSIBILITY_MOTION_EVENT_OBSERVING
 UnflaggedApi: android.Manifest.permission#MANAGE_REMOTE_AUTH:
     New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_REMOTE_AUTH
 UnflaggedApi: android.Manifest.permission#RESERVED_FOR_TESTING_SIGNATURE:
@@ -2057,6 +2059,10 @@
     New API must be flagged with @FlaggedApi: method android.media.AudioManager.getFocusFadeOutDurationForTest()
 UnflaggedApi: android.media.AudioManager#getFocusUnmuteDelayAfterFadeOutForTest():
     New API must be flagged with @FlaggedApi: method android.media.AudioManager.getFocusUnmuteDelayAfterFadeOutForTest()
+UnflaggedApi: android.media.MediaMuxer.OutputFormat#MUXER_OUTPUT_FIRST:
+    New API must be flagged with @FlaggedApi: field android.media.MediaMuxer.OutputFormat.MUXER_OUTPUT_FIRST
+UnflaggedApi: android.media.MediaMuxer.OutputFormat#MUXER_OUTPUT_LAST:
+    New API must be flagged with @FlaggedApi: field android.media.MediaMuxer.OutputFormat.MUXER_OUTPUT_LAST
 UnflaggedApi: android.media.RingtoneSelection:
     New API must be flagged with @FlaggedApi: class android.media.RingtoneSelection
 UnflaggedApi: android.media.RingtoneSelection#DEFAULT_SELECTION_URI_STRING:
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index 72450999..ddc1ae2 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -30,7 +30,6 @@
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.Size;
-import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.annotation.UserHandleAware;
@@ -2019,23 +2018,22 @@
      * @param account the account to copy
      * @param fromUser the user to copy the account from
      * @param toUser the target user
-     * @param callback Callback to invoke when the request completes,
-     *     null for no callback
      * @param handler {@link Handler} identifying the callback thread,
      *     null for the main thread
+     * @param callback Callback to invoke when the request completes,
+     *     null for no callback
      * @return An {@link AccountManagerFuture} which resolves to a Boolean indicated whether it
      * succeeded.
      * @hide
      */
-    @SuppressLint("SamShouldBeLast")
     @NonNull
     @SystemApi
     @RequiresPermission(anyOf = {COPY_ACCOUNTS, INTERACT_ACROSS_USERS_FULL})
     @FlaggedApi(FLAG_SPLIT_CREATE_MANAGED_PROFILE_ENABLED)
     public AccountManagerFuture<Boolean> copyAccountToUser(
             @NonNull final Account account, @NonNull final UserHandle fromUser,
-            @NonNull final UserHandle toUser, @Nullable AccountManagerCallback<Boolean> callback,
-            @Nullable Handler handler) {
+            @NonNull final UserHandle toUser, @Nullable Handler handler,
+            @Nullable AccountManagerCallback<Boolean> callback) {
         if (account == null) throw new IllegalArgumentException("account is null");
         if (toUser == null || fromUser == null) {
             throw new IllegalArgumentException("fromUser and toUser cannot be null");
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index c3ef104..4782205 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -1027,9 +1027,6 @@
     /** The autofill client controller. Always access via {@link #getAutofillClientController()}. */
     private AutofillClientController mAutofillClientController;
 
-    /** @hide */
-    boolean mEnterAnimationComplete;
-
     private boolean mIsInMultiWindowMode;
     /** @hide */
     boolean mIsInPictureInPictureMode;
@@ -1273,8 +1270,8 @@
      * Requests to show the “Open in browser” education. “Open in browser” is a feature
      * within the app header that allows users to switch from an app to the web. The feature
      * is made available when an application is opened by a user clicking a link or when a
-     * link is provided by an application. Links can be provided by utilizing
-     * {@link AssistContent#EXTRA_AUTHENTICATING_USER_WEB_URI} or
+     * link is provided by an application. Links can be provided by calling
+     * {@link AssistContent#setSessionTransferUri} or
      * {@link AssistContent#setWebUri}.
      *
      * <p>This method should be utilized when an activity wants to nudge the user to switch
@@ -1287,7 +1284,7 @@
      * disruptive to the user to show the education and when it is optimal to switch the user to a
      * browser session. Before requesting to show the education, developers should assert that they
      * have set a link that can be used by the "Open in browser" feature through either
-     * {@link AssistContent#EXTRA_AUTHENTICATING_USER_WEB_URI} or
+     * {@link AssistContent#setSessionTransferUri} or
      * {@link AssistContent#setWebUri} so that users are navigated to a relevant page if they choose
      * to switch to the browser. If a URI is not set using either method, "Open in browser" will
      * utilize a generic link if available which will direct users to the homepage of the site
@@ -1296,7 +1293,7 @@
      * the user will not be provided with the option to switch to the browser and the education will
      * not be shown if requested.
      *
-     * @see android.app.assist.AssistContent#EXTRA_SESSION_TRANSFER_WEB_URI
+     * @see android.app.assist.AssistContent#setSessionTransferUri
      */
     @FlaggedApi(com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB_EDUCATION)
     public final void requestOpenInBrowserEducation() {
@@ -2898,7 +2895,6 @@
         mCalled = true;
 
         getAutofillClientController().onActivityStopped(mIntent, mChangingConfigurations);
-        mEnterAnimationComplete = false;
 
         notifyVoiceInteractionManagerServiceActivityEvent(
                 VoiceInteractionSession.VOICE_INTERACTION_ACTIVITY_EVENT_STOP);
@@ -5770,6 +5766,11 @@
     @FlaggedApi(Flags.FLAG_DEVICE_AWARE_PERMISSION_APIS_ENABLED)
     public final void requestPermissions(@NonNull String[] permissions, int requestCode,
             int deviceId) {
+        // Pre M apps shouldn't request permissions, as permissions are granted at install time.
+        if (getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.M) {
+            onRequestPermissionsResult(requestCode, new String[0], new int[0], deviceId);
+        }
+
         if (requestCode < 0) {
             throw new IllegalArgumentException("requestCode should be >= 0");
         }
@@ -8589,8 +8590,6 @@
      * @hide
      */
     public void dispatchEnterAnimationComplete() {
-        mEnterAnimationComplete = true;
-        mInstrumentation.onEnterAnimationComplete();
         onEnterAnimationComplete();
         if (getWindow() != null && getWindow().getDecorView() != null) {
             View decorView = getWindow().getDecorView();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 717a2ac..1f3e655 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -105,6 +105,7 @@
 import android.content.res.AssetManager;
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
+import android.content.res.ResourceTimer;
 import android.content.res.Resources;
 import android.content.res.ResourcesImpl;
 import android.content.res.loader.ResourcesLoader;
@@ -5253,6 +5254,7 @@
 
             Resources.dumpHistory(pw, "");
             pw.flush();
+            ResourceTimer.dumpTimers(info.fd.getFileDescriptor(), "-refresh");
             if (info.finishCallback != null) {
                 info.finishCallback.sendResult(null);
             }
diff --git a/core/java/android/app/BroadcastOptions.java b/core/java/android/app/BroadcastOptions.java
index 4db3727..0e45902 100644
--- a/core/java/android/app/BroadcastOptions.java
+++ b/core/java/android/app/BroadcastOptions.java
@@ -38,6 +38,7 @@
 import android.os.PowerExemptionManager;
 import android.os.PowerExemptionManager.ReasonCode;
 import android.os.PowerExemptionManager.TempAllowListType;
+import android.os.Process;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -76,6 +77,7 @@
             FLAG_IS_ALARM_BROADCAST,
             FLAG_SHARE_IDENTITY,
             FLAG_INTERACTIVE,
+            FLAG_DEBUG_LOG,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface Flags {}
@@ -86,6 +88,7 @@
     private static final int FLAG_IS_ALARM_BROADCAST = 1 << 3;
     private static final int FLAG_SHARE_IDENTITY = 1 << 4;
     private static final int FLAG_INTERACTIVE = 1 << 5;
+    private static final int FLAG_DEBUG_LOG = 1 << 6;
 
     /**
      * Change ID which is invalid.
@@ -1082,6 +1085,34 @@
     }
 
     /**
+     * If enabled, additional debug messages for broadcast delivery will be logged.
+     *
+     * <p> This will only take effect when used by {@link Process#SHELL_UID}
+     * or {@link Process#ROOT_UID} or by apps under instrumentation.
+     *
+     * @hide
+     */
+    @NonNull
+    public BroadcastOptions setDebugLogEnabled(boolean enabled) {
+        if (enabled) {
+            mFlags |= FLAG_DEBUG_LOG;
+        } else {
+            mFlags &= ~FLAG_DEBUG_LOG;
+        }
+        return this;
+    }
+
+    /**
+     * @return if additional debug messages for broadcast delivery are enabled.
+     *
+     * @see #setDebugLogEnabled(boolean)
+     * @hide
+     */
+    public boolean isDebugLogEnabled() {
+        return (mFlags & FLAG_DEBUG_LOG) != 0;
+    }
+
+    /**
      * Returns the created options as a Bundle, which can be passed to
      * {@link android.content.Context#sendBroadcast(android.content.Intent)
      * Context.sendBroadcast(Intent)} and related methods.
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index a6c1a57..0451ac0 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -270,6 +270,6 @@
 
     int[] getAllowedAdjustmentKeyTypes();
     void setAssistantAdjustmentKeyTypeState(int type, boolean enabled);
-    String[] getTypeAdjustmentDeniedPackages();
-    void setTypeAdjustmentForPackageState(String pkg, boolean enabled);
+    int[] getAllowedAdjustmentKeyTypesForPackage(String pkg);
+    void setAssistantAdjustmentKeyTypeStateForPackage(String pkg, int type, boolean enabled);
 }
diff --git a/core/java/android/app/IUserSwitchObserver.aidl b/core/java/android/app/IUserSwitchObserver.aidl
index 1ff7a17..d71ee7c 100644
--- a/core/java/android/app/IUserSwitchObserver.aidl
+++ b/core/java/android/app/IUserSwitchObserver.aidl
@@ -19,10 +19,10 @@
 import android.os.IRemoteCallback;
 
 /** {@hide} */
-interface IUserSwitchObserver {
-    void onBeforeUserSwitching(int newUserId);
-    oneway void onUserSwitching(int newUserId, IRemoteCallback reply);
-    oneway void onUserSwitchComplete(int newUserId);
-    oneway void onForegroundProfileSwitch(int newProfileId);
-    oneway void onLockedBootComplete(int newUserId);
+oneway interface IUserSwitchObserver {
+    void onBeforeUserSwitching(int newUserId, IRemoteCallback reply);
+    void onUserSwitching(int newUserId, IRemoteCallback reply);
+    void onUserSwitchComplete(int newUserId);
+    void onForegroundProfileSwitch(int newProfileId);
+    void onLockedBootComplete(int newUserId);
 }
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index 7eacaac..b611acf 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -59,7 +59,6 @@
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
-import android.view.SurfaceControl;
 import android.view.ViewConfiguration;
 import android.view.Window;
 import android.view.WindowManagerGlobal;
@@ -137,7 +136,6 @@
     private PerformanceCollector mPerformanceCollector;
     private Bundle mPerfMetrics = new Bundle();
     private UiAutomation mUiAutomation;
-    private final Object mAnimationCompleteLock = new Object();
 
     @RavenwoodKeep
     public Instrumentation() {
@@ -455,31 +453,6 @@
         idler.waitForIdle();
     }
 
-    private void waitForEnterAnimationComplete(Activity activity) {
-        synchronized (mAnimationCompleteLock) {
-            long timeout = 5000;
-            try {
-                // We need to check that this specified Activity completed the animation, not just
-                // any Activity. If it was another Activity, then decrease the timeout by how long
-                // it's already waited and wait for the thread to wakeup again.
-                while (timeout > 0 && !activity.mEnterAnimationComplete) {
-                    long startTime = System.currentTimeMillis();
-                    mAnimationCompleteLock.wait(timeout);
-                    long totalTime = System.currentTimeMillis() - startTime;
-                    timeout -= totalTime;
-                }
-            } catch (InterruptedException e) {
-            }
-        }
-    }
-
-    /** @hide */
-    public void onEnterAnimationComplete() {
-        synchronized (mAnimationCompleteLock) {
-            mAnimationCompleteLock.notifyAll();
-        }
-    }
-
     /**
      * Execute a call on the application's main thread, blocking until it is
      * complete.  Useful for doing things that are not thread-safe, such as
@@ -640,13 +613,14 @@
             activity = aw.activity;
         }
 
-        // Do not call this method within mSync, lest it could block the main thread.
-        waitForEnterAnimationComplete(activity);
-
-        // Apply an empty transaction to ensure SF has a chance to update before
-        // the Activity is ready (b/138263890).
-        try (SurfaceControl.Transaction t = new SurfaceControl.Transaction()) {
-            t.apply(true);
+        // Typically, callers expect that the launched activity can receive input events after this
+        // method returns, so wait until a stable state, i.e. animation is finished and input info
+        // is updated.
+        try {
+            WindowManagerGlobal.getWindowManagerService()
+                    .syncInputTransactions(true /* waitForAnimations */);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
         }
         return activity;
     }
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 8ffea23..5176aee 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -774,8 +774,9 @@
 
     /**
      * Bit to be bitwise-ored into the {@link #flags} field that should be
-     * set by the system if this notification is a promoted ongoing notification, either via a
-     * user setting or allowlist.
+     * set by the system if this notification is a promoted ongoing notification, both because it
+     * {@link #hasPromotableCharacteristics()} and the user has not disabled the feature for this
+     * app.
      *
      * Applications cannot set this flag directly, but the posting app and
      * {@link android.service.notification.NotificationListenerService} can read it.
@@ -5855,7 +5856,9 @@
                 return null;
             }
             final int size = mContext.getResources().getDimensionPixelSize(
-                    R.dimen.notification_badge_size);
+                    Flags.notificationsRedesignTemplates()
+                            ? R.dimen.notification_2025_badge_size
+                            : R.dimen.notification_badge_size);
             Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
             Canvas canvas = new Canvas(bitmap);
             badge.setBounds(0, 0, size, size);
@@ -6204,7 +6207,7 @@
             int textColor = Colors.flattenAlpha(getPrimaryTextColor(p), pillColor);
             contentView.setInt(R.id.expand_button, "setDefaultTextColor", textColor);
             contentView.setInt(R.id.expand_button, "setDefaultPillColor", pillColor);
-            // Use different highlighted colors for conversations' unread count
+            // Use different highlighted colors for e.g. unopened groups
             if (p.mHighlightExpander) {
                 pillColor = Colors.flattenAlpha(
                         getColors(p).getTertiaryFixedDimAccentColor(), bgColor);
@@ -6453,7 +6456,10 @@
             big.setColorStateList(R.id.snooze_button, "setImageTintList", actionColor);
             big.setColorStateList(R.id.bubble_button, "setImageTintList", actionColor);
 
-            if (Flags.notificationsRedesignTemplates()) {
+            // Update margins to leave space for the top line (but not for HUNs, which use a
+            // different layout that already accounts for that).
+            if (Flags.notificationsRedesignTemplates()
+                    && p.mViewType != StandardTemplateParams.VIEW_TYPE_HEADS_UP) {
                 int margin = getContentMarginTop(mContext,
                         R.dimen.notification_2025_content_margin_top);
                 big.setViewLayoutMargin(R.id.notification_main_column, RemoteViews.MARGIN_TOP,
@@ -6801,6 +6807,8 @@
         public RemoteViews makeNotificationGroupHeader() {
             return makeNotificationHeader(mParams.reset()
                     .viewType(StandardTemplateParams.VIEW_TYPE_GROUP_HEADER)
+                    // Highlight group expander until the group is first opened
+                    .highlightExpander(Flags.notificationsRedesignTemplates())
                     .fillTextsFrom(this));
         }
 
@@ -6817,6 +6825,12 @@
                     getHeaderLayoutResource());
             resetNotificationHeader(header);
             bindNotificationHeader(header, p);
+            if (Flags.notificationsRedesignTemplates()
+                    && (p.mViewType == StandardTemplateParams.VIEW_TYPE_MINIMIZED
+                    || p.mViewType == StandardTemplateParams.VIEW_TYPE_PUBLIC)) {
+                // Center top line vertically in minimized and public header-only views
+                header.setBoolean(R.id.notification_header, "centerTopLine", true);
+            }
             return header;
         }
 
@@ -6970,12 +6984,14 @@
          * @param useRegularSubtext uses the normal subtext set if there is one available. Otherwise
          *                          a new subtext is created consisting of the content of the
          *                          notification.
+         * @param highlightExpander whether the expander should use the highlighted colors
          * @hide
          */
-        public RemoteViews makeLowPriorityContentView(boolean useRegularSubtext) {
+        public RemoteViews makeLowPriorityContentView(boolean useRegularSubtext,
+                boolean highlightExpander) {
             StandardTemplateParams p = mParams.reset()
                     .viewType(StandardTemplateParams.VIEW_TYPE_MINIMIZED)
-                    .highlightExpander(false)
+                    .highlightExpander(highlightExpander)
                     .fillTextsFrom(this);
             if (!useRegularSubtext || TextUtils.isEmpty(p.mSubText)) {
                 p.summaryText(createSummaryText());
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index ec10913..aede8aa 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -17,6 +17,7 @@
 package android.app;
 
 import static android.Manifest.permission.POST_NOTIFICATIONS;
+import static android.app.NotificationChannel.DEFAULT_CHANNEL_ID;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.service.notification.Flags.notificationClassification;
 
@@ -50,6 +51,7 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.os.IBinder;
+import android.os.IpcDataCache;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.RemoteException;
@@ -71,6 +73,8 @@
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.time.InstantSource;
@@ -1202,12 +1206,20 @@
      * package (see {@link Context#createPackageContext(String, int)}).</p>
      */
     public NotificationChannel getNotificationChannel(String channelId) {
-        INotificationManager service = service();
-        try {
-            return service.getNotificationChannel(mContext.getOpPackageName(),
-                    mContext.getUserId(), mContext.getPackageName(), channelId);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+        if (Flags.nmBinderPerfCacheChannels()) {
+            return getChannelFromList(channelId,
+                    mNotificationChannelListCache.query(new NotificationChannelQuery(
+                            mContext.getOpPackageName(),
+                            mContext.getPackageName(),
+                            mContext.getUserId())));
+        } else {
+            INotificationManager service = service();
+            try {
+                return service.getNotificationChannel(mContext.getOpPackageName(),
+                        mContext.getUserId(), mContext.getPackageName(), channelId);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
         }
     }
 
@@ -1222,13 +1234,21 @@
      */
     public @Nullable NotificationChannel getNotificationChannel(@NonNull String channelId,
             @NonNull String conversationId) {
-        INotificationManager service = service();
-        try {
-            return service.getConversationNotificationChannel(mContext.getOpPackageName(),
-                    mContext.getUserId(), mContext.getPackageName(), channelId, true,
-                    conversationId);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+        if (Flags.nmBinderPerfCacheChannels()) {
+            return getConversationChannelFromList(channelId, conversationId,
+                    mNotificationChannelListCache.query(new NotificationChannelQuery(
+                            mContext.getOpPackageName(),
+                            mContext.getPackageName(),
+                            mContext.getUserId())));
+        } else {
+            INotificationManager service = service();
+            try {
+                return service.getConversationNotificationChannel(mContext.getOpPackageName(),
+                        mContext.getUserId(), mContext.getPackageName(), channelId, true,
+                        conversationId);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
         }
     }
 
@@ -1241,15 +1261,62 @@
      * {@link Context#createPackageContext(String, int)}).</p>
      */
     public List<NotificationChannel> getNotificationChannels() {
-        INotificationManager service = service();
-        try {
-            return service.getNotificationChannels(mContext.getOpPackageName(),
-                    mContext.getPackageName(), mContext.getUserId()).getList();
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+        if (Flags.nmBinderPerfCacheChannels()) {
+            return mNotificationChannelListCache.query(new NotificationChannelQuery(
+               mContext.getOpPackageName(),
+               mContext.getPackageName(),
+               mContext.getUserId()));
+        } else {
+            INotificationManager service = service();
+            try {
+                return service.getNotificationChannels(mContext.getOpPackageName(),
+                        mContext.getPackageName(), mContext.getUserId()).getList();
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
         }
     }
 
+    // channel list assumed to be associated with the appropriate package & user id already.
+    private static NotificationChannel getChannelFromList(String channelId,
+            List<NotificationChannel> channels) {
+        if (channels == null) {
+            return null;
+        }
+        if (channelId == null) {
+            channelId = DEFAULT_CHANNEL_ID;
+        }
+        for (NotificationChannel channel : channels) {
+            if (channelId.equals(channel.getId())) {
+                return channel;
+            }
+        }
+        return null;
+    }
+
+    private static NotificationChannel getConversationChannelFromList(String channelId,
+            String conversationId, List<NotificationChannel> channels) {
+        if (channels == null) {
+            return null;
+        }
+        if (channelId == null) {
+            channelId = DEFAULT_CHANNEL_ID;
+        }
+        if (conversationId == null) {
+            return getChannelFromList(channelId, channels);
+        }
+        NotificationChannel parent = null;
+        for (NotificationChannel channel : channels) {
+            if (conversationId.equals(channel.getConversationId())
+                    && channelId.equals(channel.getParentChannelId())) {
+                return channel;
+            } else if (channelId.equals(channel.getId())) {
+                parent = channel;
+            }
+        }
+        return parent;
+    }
+
     /**
      * Deletes the given notification channel.
      *
@@ -1328,6 +1395,71 @@
         }
     }
 
+    private static final String NOTIFICATION_CHANNEL_CACHE_API = "getNotificationChannel";
+    private static final String NOTIFICATION_CHANNEL_LIST_CACHE_NAME = "getNotificationChannels";
+    private static final int NOTIFICATION_CHANNEL_CACHE_SIZE = 10;
+
+    private final IpcDataCache.QueryHandler<NotificationChannelQuery, List<NotificationChannel>>
+            mNotificationChannelListQueryHandler = new IpcDataCache.QueryHandler<>() {
+                @Override
+                public List<NotificationChannel> apply(NotificationChannelQuery query) {
+                    INotificationManager service = service();
+                    try {
+                        return service.getNotificationChannels(query.callingPkg,
+                                query.targetPkg, query.userId).getList();
+                    } catch (RemoteException e) {
+                        throw e.rethrowFromSystemServer();
+                    }
+                }
+
+                @Override
+                public boolean shouldBypassCache(@NonNull NotificationChannelQuery query) {
+                    // Other locations should also not be querying the cache in the first place if
+                    // the flag is not enabled, but this is an extra precaution.
+                    if (!Flags.nmBinderPerfCacheChannels()) {
+                        Log.wtf(TAG,
+                                "shouldBypassCache called when nm_binder_perf_cache_channels off");
+                        return true;
+                    }
+                    return false;
+                }
+            };
+
+    private final IpcDataCache<NotificationChannelQuery, List<NotificationChannel>>
+            mNotificationChannelListCache =
+            new IpcDataCache<>(NOTIFICATION_CHANNEL_CACHE_SIZE, IpcDataCache.MODULE_SYSTEM,
+                    NOTIFICATION_CHANNEL_CACHE_API, NOTIFICATION_CHANNEL_LIST_CACHE_NAME,
+                    mNotificationChannelListQueryHandler);
+
+    private record NotificationChannelQuery(
+            String callingPkg,
+            String targetPkg,
+            int userId) {}
+
+    /**
+     * @hide
+     */
+    public static void invalidateNotificationChannelCache() {
+        if (Flags.nmBinderPerfCacheChannels()) {
+            IpcDataCache.invalidateCache(IpcDataCache.MODULE_SYSTEM,
+                    NOTIFICATION_CHANNEL_CACHE_API);
+        } else {
+            // if we are here, we have failed to flag something
+            Log.wtf(TAG, "invalidateNotificationChannelCache called without flag");
+        }
+    }
+
+    /**
+     * For testing only: running tests with a cache requires marking the cache's property for
+     * testing, as test APIs otherwise cannot invalidate the cache. This must be called after
+     * calling PropertyInvalidatedCache.setTestMode(true).
+     * @hide
+     */
+    @VisibleForTesting
+    public void setChannelCacheToTestMode() {
+        mNotificationChannelListCache.testPropertyName();
+    }
+
     /**
      * @hide
      */
@@ -1986,10 +2118,12 @@
      * @hide
      */
     @FlaggedApi(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI)
-    public void setTypeAdjustmentForPackageState(@NonNull String pkg, boolean enabled) {
+    public void setAssistantAdjustmentKeyTypeStateForPackage(@NonNull String pkg,
+                                                             @Adjustment.Types int type,
+                                                             boolean enabled) {
         INotificationManager service = service();
         try {
-            service.setTypeAdjustmentForPackageState(pkg, enabled);
+            service.setAssistantAdjustmentKeyTypeStateForPackage(pkg, type, enabled);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/app/OWNERS b/core/java/android/app/OWNERS
index 6e4c28f..7a811a1 100644
--- a/core/java/android/app/OWNERS
+++ b/core/java/android/app/OWNERS
@@ -28,6 +28,7 @@
 per-file Service* = file:/ACTIVITY_MANAGER_OWNERS
 per-file SystemServiceRegistry.java = file:/ACTIVITY_MANAGER_OWNERS
 per-file *UserSwitchObserver* = file:/ACTIVITY_MANAGER_OWNERS
+per-file UidObserver* = file:/ACTIVITY_MANAGER_OWNERS
 
 # UI Automation
 per-file *UiAutomation* = file:/services/accessibility/OWNERS
diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java
index 5567c08..e7e9b00 100644
--- a/core/java/android/app/PropertyInvalidatedCache.java
+++ b/core/java/android/app/PropertyInvalidatedCache.java
@@ -36,6 +36,7 @@
 import android.util.Log;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
+import android.util.SystemPropertySetter;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
@@ -157,21 +158,6 @@
     public static final String MODULE_TELEPHONY = "telephony";
 
     /**
-     * Constants that affect retries when the process is unable to write the property.
-     * The first constant is the number of times the process will attempt to set the
-     * property.  The second constant is the delay between attempts.
-     */
-
-    /**
-     * Wait 200ms between retry attempts and the retry limit is 5.  That gives a total possible
-     * delay of 1s, which should be less than ANR timeouts.  The goal is to have the system crash
-     * because the property could not be set (which is a condition that is easily recognized) and
-     * not crash because of an ANR (which can be confusing to debug).
-     */
-    private static final int PROPERTY_FAILURE_RETRY_DELAY_MILLIS = 200;
-    private static final int PROPERTY_FAILURE_RETRY_LIMIT = 5;
-
-    /**
      * Construct a system property that matches the rules described above.  The module is
      * one of the permitted values above.  The API is a string that is a legal Java simple
      * identifier.  The api is modified to conform to the system property style guide by
@@ -958,37 +944,8 @@
          */
         @Override
         void setNonceInternal(long value) {
-            // Failing to set the nonce is a fatal error.  Failures setting a system property have
-            // been reported; given that the failure is probably transient, this function includes
-            // a retry.
             final String str = Long.toString(value);
-            RuntimeException failure = null;
-            for (int attempt = 0; attempt < PROPERTY_FAILURE_RETRY_LIMIT; attempt++) {
-                try {
-                    SystemProperties.set(mName, str);
-                    if (attempt > 0) {
-                        // This log is not guarded.  Based on known bug reports, it should
-                        // occur once a week or less.  The purpose of the log message is to
-                        // identify the retries as a source of delay that might be otherwise
-                        // be attributed to the cache itself.
-                        Log.w(TAG, "Nonce set after " + attempt + " tries");
-                    }
-                    return;
-                } catch (RuntimeException e) {
-                    if (failure == null) {
-                        failure = e;
-                    }
-                    try {
-                        Thread.sleep(PROPERTY_FAILURE_RETRY_DELAY_MILLIS);
-                    } catch (InterruptedException x) {
-                        // Ignore this exception.  The desired delay is only approximate and
-                        // there is no issue if the sleep sometimes terminates early.
-                    }
-                }
-            }
-            // This point is reached only if SystemProperties.set() fails at least once.
-            // Rethrow the first exception that was received.
-            throw failure;
+            SystemPropertySetter.setWithRetry(mName, str);
         }
     }
 
diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java
index 2e6f3e1..5754984 100644
--- a/core/java/android/app/UiModeManager.java
+++ b/core/java/android/app/UiModeManager.java
@@ -753,7 +753,7 @@
      * <p>
      * The mode can be one of:
      * <ul>
-     *   <li><em>{@link #MODE_NIGHT_NO}<em> sets the device into
+     *   <li><em>{@link #MODE_NIGHT_NO}</em> sets the device into
      *       {@code notnight} mode</li>
      *   <li><em>{@link #MODE_NIGHT_YES}</em> sets the device into
      *       {@code night} mode</li>
@@ -889,7 +889,7 @@
      * <p>
      * The mode can be one of:
      * <ul>
-     *   <li><em>{@link #MODE_NIGHT_NO}<em> sets the device into
+     *   <li><em>{@link #MODE_NIGHT_NO}</em> sets the device into
      *       {@code notnight} mode</li>
      *   <li><em>{@link #MODE_NIGHT_YES}</em> sets the device into
      *       {@code night} mode</li>
diff --git a/core/java/android/app/UserSwitchObserver.java b/core/java/android/app/UserSwitchObserver.java
index 727799a1..1664cfb 100644
--- a/core/java/android/app/UserSwitchObserver.java
+++ b/core/java/android/app/UserSwitchObserver.java
@@ -30,7 +30,11 @@
     }
 
     @Override
-    public void onBeforeUserSwitching(int newUserId) throws RemoteException {}
+    public void onBeforeUserSwitching(int newUserId, IRemoteCallback reply) throws RemoteException {
+        if (reply != null) {
+            reply.sendResult(null);
+        }
+    }
 
     @Override
     public void onUserSwitching(int newUserId, IRemoteCallback reply) throws RemoteException {
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 360376d..73ecc71 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -1690,7 +1690,7 @@
      * @hide
      */
     @FlaggedApi(FLAG_CUSTOMIZATION_PACKS_APIS)
-    @SystemApi
+    @TestApi
     @RequiresPermission(READ_WALLPAPER_INTERNAL)
     @NonNull
     public SparseArray<Rect> getBitmapCrops(@SetWallpaperFlags int which) {
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 84d6741..c504521 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -4354,20 +4354,24 @@
     }
 
     /**
-     * Indicates that app functions are not controlled by policy.
+     * Indicates that {@link android.app.appfunctions.AppFunctionManager} is not controlled by
+     * policy.
      *
      * <p>If no admin set this policy, it means appfunctions are enabled.
      */
     @FlaggedApi(android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER)
     public static final int APP_FUNCTIONS_NOT_CONTROLLED_BY_POLICY = 0;
 
-    /** Indicates that app functions are controlled and disabled by a policy. */
+    /** Indicates that {@link android.app.appfunctions.AppFunctionManager} is controlled and
+     * disabled by policy, i.e. no apps in the current user are allowed to expose app functions.
+     */
     @FlaggedApi(android.app.appfunctions.flags.Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER)
     public static final int APP_FUNCTIONS_DISABLED = 1;
 
     /**
-     * Indicates that app functions are controlled and disabled by a policy for cross profile
-     * interactions only.
+     * Indicates that {@link android.app.appfunctions.AppFunctionManager} is controlled and
+     * disabled by a policy for cross profile interactions only, i.e. app functions exposed by apps
+     * in the current user can only be invoked within the same user.
      *
      * <p>This is different from {@link #APP_FUNCTIONS_DISABLED} in that it only disables cross
      * profile interactions (even if the caller has permissions required to interact across users).
@@ -4388,7 +4392,9 @@
     public @interface AppFunctionsPolicy {}
 
     /**
-     * Sets the app functions policy which controls app functions operations on the device.
+     * Sets the {@link android.app.appfunctions.AppFunctionManager} policy which controls app
+     * functions operations on the device. An app function is a piece of functionality that apps
+     * expose to the system for cross-app orchestration.
      *
      * <p>This function can only be called by a device owner, a profile owner or holders of the
      * permission {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_APP_FUNCTIONS}.
@@ -4414,7 +4420,7 @@
     }
 
     /**
-     * Returns the current app functions policy.
+     * Returns the current {@link android.app.appfunctions.AppFunctionManager} policy.
      *
      * <p>The returned policy will be the current resolved policy rather than the policy set by the
      * calling admin.
@@ -4443,7 +4449,8 @@
      * disabled through this Config.
      */
     private static final IpcDataCache.Config sDpmCaches =
-            new IpcDataCache.Config(8, IpcDataCache.MODULE_SYSTEM, "DevicePolicyManagerCaches");
+            new IpcDataCache.Config(8, IpcDataCache.MODULE_SYSTEM, "DevicePolicyManagerCaches")
+            .cacheNulls(true);
 
     /** @hide */
     public static void invalidateBinderCaches() {
diff --git a/core/java/android/app/admin/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig
index af035cb..75589fa 100644
--- a/core/java/android/app/admin/flags/flags.aconfig
+++ b/core/java/android/app/admin/flags/flags.aconfig
@@ -160,16 +160,6 @@
 }
 
 flag {
-  name: "fix_race_condition_in_tie_profile_lock"
-  namespace: "enterprise"
-  description: "Fix race condition in tieProfileLockIfNecessary()"
-  bug: "355905501"
-  metadata {
-    purpose: PURPOSE_BUGFIX
-  }
-}
-
-flag {
   name: "quiet_mode_credential_bug_fix"
   namespace: "enterprise"
   description: "Guards a bugfix that ends the credential input flow if the managed user has not stopped."
diff --git a/core/java/android/app/appfunctions/AppFunctionService.java b/core/java/android/app/appfunctions/AppFunctionService.java
index 85b6ab2..8e48b4e 100644
--- a/core/java/android/app/appfunctions/AppFunctionService.java
+++ b/core/java/android/app/appfunctions/AppFunctionService.java
@@ -24,9 +24,11 @@
 import android.annotation.MainThread;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SdkConstant;
 import android.app.Service;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.SigningInfo;
 import android.os.Binder;
 import android.os.CancellationSignal;
 import android.os.IBinder;
@@ -59,6 +61,7 @@
      * service must also require the {@link BIND_APP_FUNCTION_SERVICE} permission so that other
      * applications can not abuse it.
      */
+    @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
     @NonNull
     public static final String SERVICE_INTERFACE = "android.app.appfunctions.AppFunctionService";
 
@@ -76,10 +79,10 @@
         void perform(
                 @NonNull ExecuteAppFunctionRequest request,
                 @NonNull String callingPackage,
+                @NonNull SigningInfo callingPackageSigningInfo,
                 @NonNull CancellationSignal cancellationSignal,
                 @NonNull
-                        OutcomeReceiver<ExecuteAppFunctionResponse, AppFunctionException>
-                                callback);
+                        OutcomeReceiver<ExecuteAppFunctionResponse, AppFunctionException> callback);
     }
 
     /** @hide */
@@ -91,6 +94,7 @@
             public void executeAppFunction(
                     @NonNull ExecuteAppFunctionRequest request,
                     @NonNull String callingPackage,
+                    @NonNull SigningInfo callingPackageSigningInfo,
                     @NonNull ICancellationCallback cancellationCallback,
                     @NonNull IExecuteAppFunctionCallback callback) {
                 if (context.checkCallingPermission(BIND_APP_FUNCTION_SERVICE)
@@ -103,6 +107,7 @@
                     onExecuteFunction.perform(
                             request,
                             callingPackage,
+                            callingPackageSigningInfo,
                             buildCancellationSignal(cancellationCallback),
                             new OutcomeReceiver<>() {
                                 @Override
@@ -152,15 +157,17 @@
     /**
      * 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>This method is the entry point for handling all app function requests in an app. When the
+     * system needs your AppFunctionService to perform a function, it will invoke this method.
      *
-     * <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>Each function you've registered is identified by a unique identifier. 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 is
+     * automatically generated by the AppFunctions SDK.
+     *
+     * <p>You can determine which function to execute by calling {@link
+     * ExecuteAppFunctionRequest#getFunctionIdentifier()}. This allows your service to route the
+     * incoming request to the appropriate logic for handling the specific function.
      *
      * <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
@@ -171,6 +178,8 @@
      *
      * @param request The function execution request.
      * @param callingPackage The package name of the app that is requesting the execution.
+     * @param callingPackageSigningInfo The signing information of the app that is requesting the
+     *     execution.
      * @param cancellationSignal A signal to cancel the execution.
      * @param callback A callback to report back the result or error.
      */
@@ -178,10 +187,9 @@
     public abstract void onExecuteFunction(
             @NonNull ExecuteAppFunctionRequest request,
             @NonNull String callingPackage,
+            @NonNull SigningInfo callingPackageSigningInfo,
             @NonNull CancellationSignal cancellationSignal,
-            @NonNull
-                    OutcomeReceiver<ExecuteAppFunctionResponse, AppFunctionException>
-                            callback);
+            @NonNull OutcomeReceiver<ExecuteAppFunctionResponse, AppFunctionException> callback);
 
     /**
      * Returns result codes from throwable.
diff --git a/core/java/android/app/appfunctions/IAppFunctionService.aidl b/core/java/android/app/appfunctions/IAppFunctionService.aidl
index bf935d2..78bcb7f 100644
--- a/core/java/android/app/appfunctions/IAppFunctionService.aidl
+++ b/core/java/android/app/appfunctions/IAppFunctionService.aidl
@@ -35,12 +35,15 @@
      *
      * @param request  the function execution request.
      * @param callingPackage The package name of the app that is requesting the execution.
+     * @param callingPackageSigningInfo The signing information of the app that is requesting the
+     *      execution.
      * @param cancellationCallback a callback to send back the cancellation transport.
      * @param callback a callback to report back the result.
      */
     void executeAppFunction(
         in ExecuteAppFunctionRequest request,
         in String callingPackage,
+        in android.content.pm.SigningInfo callingPackageSigningInfo,
         in ICancellationCallback cancellationCallback,
         in IExecuteAppFunctionCallback callback
     );
diff --git a/core/java/android/app/assist/AssistContent.java b/core/java/android/app/assist/AssistContent.java
index 3e3ca24..adf8c94 100644
--- a/core/java/android/app/assist/AssistContent.java
+++ b/core/java/android/app/assist/AssistContent.java
@@ -1,6 +1,7 @@
 package android.app.assist;
 
 import android.annotation.FlaggedApi;
+import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ClipData;
 import android.content.Intent;
@@ -30,31 +31,6 @@
     public static final String EXTRA_APP_FUNCTION_DATA =
             "android.app.assist.extra.APP_FUNCTION_DATA";
 
-    /**
-     * This extra can be optionally supplied in the {@link #getExtras} bundle to provide a
-     * {@link Uri} which will be utilized when transitioning a user's session to another surface.
-     *
-     * <p>If provided, instead of using the URI provided in {@link #setWebUri}, the
-     * "Open in browser" feature will use this URI to transition the current session from one
-     * surface to the other. Apps may choose to encode session or user information into this
-     * URI in order to provide a better session transfer experience.
-     *
-     * <p>Unlike {@link #setWebUri}, this URI will not be used for features where the user might
-     * accidentally share it with another user. However, developers should not encode
-     * authentication credentials into this URI, because it will be surfaced in the browser URL
-     * bar and may be copied and shared from there.
-     *
-     * <p>When providing this extra, developers should still continue to provide
-     * {@link #setWebUri} for backwards compatibility with features such as
-     * <a href="https://developer.android.com/guide/components/activities/recents#url-sharing">
-     * recents URL sharing</a> which do not benefit from a session-transfer web URI.
-     *
-     * @see android.app.Activity#requestOpenInBrowserEducation()
-     */
-    @FlaggedApi(com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB_EDUCATION)
-    public static final String EXTRA_SESSION_TRANSFER_WEB_URI =
-            "android.app.assist.extra.SESSION_TRANSFER_WEB_URI";
-
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private boolean mIsAppProvidedIntent = false;
     private boolean mIsAppProvidedWebUri = false;
@@ -66,6 +42,7 @@
     private ClipData mClipData;
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private Uri mUri;
+    private Uri mSessionTransferUri;
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     private final Bundle mExtras;
 
@@ -200,6 +177,41 @@
     }
 
     /**
+     * This method can be used to provide a {@link Uri} which will be utilized when transitioning a
+     * user's session to another surface.
+     *
+     * <p>If provided, instead of using the URI provided in {@link #setWebUri}, the
+     * "Open in browser" feature will use this URI to transition the current session from one
+     * surface to the other. Apps may choose to encode session or user information into this
+     * URI in order to provide a better session transfer experience. However, while this URI will
+     * only be available to the system and not other applications, developers should not encode
+     * authentication credentials into this URI, because it will be surfaced in the browser URL bar
+     * and may be copied and shared from there.
+     *
+     * <p>When providing this URI, developers should still continue to provide
+     * {@link #setWebUri} for backwards compatibility with features such as
+     * <a href="https://developer.android.com/guide/components/activities/recents#url-sharing">
+     * recents URL sharing</a> which facilitate link sharing with other users and would not benefit
+     * from a session-transfer URI.
+     *
+     * @see android.app.Activity#requestOpenInBrowserEducation()
+     */
+    @FlaggedApi(com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB_EDUCATION)
+    public void setSessionTransferUri(@Nullable Uri uri) {
+        mSessionTransferUri = uri;
+    }
+
+    /**
+     * Return the content's session transfer web URI as per
+     * {@link #setSessionTransferUri(android.net.Uri)}, or null if there is none.
+     */
+    @FlaggedApi(com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB_EDUCATION)
+    @Nullable
+    public Uri getSessionTransferUri() {
+        return mSessionTransferUri;
+    }
+
+    /**
      * Return Bundle for extra vendor-specific data that can be modified and examined.
      */
     public Bundle getExtras() {
@@ -218,6 +230,9 @@
             mUri = Uri.CREATOR.createFromParcel(in);
         }
         if (in.readInt() != 0) {
+            mSessionTransferUri = Uri.CREATOR.createFromParcel(in);
+        }
+        if (in.readInt() != 0) {
             mStructuredData = in.readString();
         }
         mIsAppProvidedIntent = in.readInt() == 1;
@@ -245,6 +260,12 @@
         } else {
             dest.writeInt(0);
         }
+        if (mSessionTransferUri != null) {
+            dest.writeInt(1);
+            mSessionTransferUri.writeToParcel(dest, flags);
+        } else {
+            dest.writeInt(0);
+        }
         if (mStructuredData != null) {
             dest.writeInt(1);
             dest.writeString(mStructuredData);
diff --git a/core/java/android/app/jank/AppJankStats.java b/core/java/android/app/jank/AppJankStats.java
index 6ef6a44..a8ebc383 100644
--- a/core/java/android/app/jank/AppJankStats.java
+++ b/core/java/android/app/jank/AppJankStats.java
@@ -57,6 +57,8 @@
     // Histogram of relative frame times encoded in predetermined buckets.
     private RelativeFrameTimeHistogram mRelativeFrameTimeHistogram;
 
+    // Navigation component associated to this stat.
+    private String mNavigationComponent;
 
     /** Used to indicate no widget category has been set. */
     public static final String WIDGET_CATEGORY_UNSPECIFIED = "unspecified";
@@ -158,6 +160,8 @@
      *
      * @param appUid the Uid of the App that is collecting jank stats.
      * @param widgetId the widget id that frames will be associated to.
+     * @param navigationComponent the intended navigation target within the activity, this could be
+     *                            a navigation destination, screen and/or pane.
      * @param widgetCategory a category used to organize widgets in a structured way that indicates
      *                       they serve a similar purpose or perform related functions. Must be
      *                       prefixed with WIDGET_CATEGORY_ and have a suffix of one of the
@@ -172,14 +176,14 @@
      * @param jankyFrames the total number of janky frames that were counted for this stat.
      * @param relativeFrameTimeHistogram the histogram with predefined buckets. See
      * {@link #getRelativeFrameTimeHistogram()} for details.
-     *
      */
-    public AppJankStats(int appUid, @NonNull String widgetId,
+    public AppJankStats(int appUid, @NonNull String widgetId, @Nullable String navigationComponent,
             @Nullable @WidgetCategory String widgetCategory,
             @Nullable @WidgetState String widgetState, long totalFrames, long jankyFrames,
             @NonNull RelativeFrameTimeHistogram relativeFrameTimeHistogram) {
         mUid = appUid;
         mWidgetId = widgetId;
+        mNavigationComponent = navigationComponent;
         mWidgetCategory = widgetCategory != null ? widgetCategory : WIDGET_CATEGORY_UNSPECIFIED;
         mWidgetState = widgetState != null ? widgetState : WIDGET_STATE_UNSPECIFIED;
         mTotalFrames = totalFrames;
@@ -254,4 +258,13 @@
     public @NonNull RelativeFrameTimeHistogram getRelativeFrameTimeHistogram() {
         return mRelativeFrameTimeHistogram;
     }
+
+    /**
+     * Returns the navigation component if it exists that this stat applies to.
+     *
+     * @return the navigation component if it exists that this stat applies to.
+     */
+    public @Nullable String getNavigationComponent() {
+        return mNavigationComponent;
+    }
 }
diff --git a/core/java/android/app/slice/Slice.java b/core/java/android/app/slice/Slice.java
index 5514868..382f9be 100644
--- a/core/java/android/app/slice/Slice.java
+++ b/core/java/android/app/slice/Slice.java
@@ -273,11 +273,7 @@
 
     protected Slice(Parcel in) {
         mHints = in.readStringArray();
-        int n = in.readInt();
-        mItems = new SliceItem[n];
-        for (int i = 0; i < n; i++) {
-            mItems[i] = SliceItem.CREATOR.createFromParcel(in);
-        }
+        mItems = in.createTypedArray(SliceItem.CREATOR);
         mUri = Uri.CREATOR.createFromParcel(in);
         mSpec = in.readTypedObject(SliceSpec.CREATOR);
     }
@@ -313,10 +309,7 @@
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeStringArray(mHints);
-        dest.writeInt(mItems.length);
-        for (int i = 0; i < mItems.length; i++) {
-            mItems[i].writeToParcel(dest, flags);
-        }
+        dest.writeTypedArray(mItems, flags);
         mUri.writeToParcel(dest, 0);
         dest.writeTypedObject(mSpec, flags);
     }
diff --git a/core/java/android/app/time/TimeZoneCapabilities.java b/core/java/android/app/time/TimeZoneCapabilities.java
index 4dee159..929f660 100644
--- a/core/java/android/app/time/TimeZoneCapabilities.java
+++ b/core/java/android/app/time/TimeZoneCapabilities.java
@@ -55,7 +55,8 @@
      * The user the capabilities are for. This is used for object equality and debugging but there
      * is no accessor.
      */
-    @NonNull private final UserHandle mUserHandle;
+    @NonNull
+    private final UserHandle mUserHandle;
     private final @CapabilityState int mConfigureAutoDetectionEnabledCapability;
 
     /**
@@ -69,6 +70,7 @@
 
     private final @CapabilityState int mConfigureGeoDetectionEnabledCapability;
     private final @CapabilityState int mSetManualTimeZoneCapability;
+    private final @CapabilityState int mConfigureNotificationsEnabledCapability;
 
     private TimeZoneCapabilities(@NonNull Builder builder) {
         this.mUserHandle = Objects.requireNonNull(builder.mUserHandle);
@@ -78,6 +80,8 @@
         this.mConfigureGeoDetectionEnabledCapability =
                 builder.mConfigureGeoDetectionEnabledCapability;
         this.mSetManualTimeZoneCapability = builder.mSetManualTimeZoneCapability;
+        this.mConfigureNotificationsEnabledCapability =
+                builder.mConfigureNotificationsEnabledCapability;
     }
 
     @NonNull
@@ -88,6 +92,7 @@
                 .setUseLocationEnabled(in.readBoolean())
                 .setConfigureGeoDetectionEnabledCapability(in.readInt())
                 .setSetManualTimeZoneCapability(in.readInt())
+                .setConfigureNotificationsEnabledCapability(in.readInt())
                 .build();
     }
 
@@ -98,6 +103,7 @@
         dest.writeBoolean(mUseLocationEnabled);
         dest.writeInt(mConfigureGeoDetectionEnabledCapability);
         dest.writeInt(mSetManualTimeZoneCapability);
+        dest.writeInt(mConfigureNotificationsEnabledCapability);
     }
 
     /**
@@ -117,8 +123,8 @@
      *
      * Not part of the SDK API because it is intended for use by SettingsUI, which can display
      * text about needing it to be on for location-based time zone detection.
-     * @hide
      *
+     * @hide
      */
     public boolean isUseLocationEnabled() {
         return mUseLocationEnabled;
@@ -148,6 +154,18 @@
     }
 
     /**
+     * Returns the capability state associated with the user's ability to modify the time zone
+     * notification setting. The setting can be updated via {@link
+     * TimeManager#updateTimeZoneConfiguration(TimeZoneConfiguration)}.
+     *
+     * @hide
+     */
+    @CapabilityState
+    public int getConfigureNotificationsEnabledCapability() {
+        return mConfigureNotificationsEnabledCapability;
+    }
+
+    /**
      * Tries to create a new {@link TimeZoneConfiguration} from the {@code config} and the set of
      * {@code requestedChanges}, if {@code this} capabilities allow. The new configuration is
      * returned. If the capabilities do not permit one or more of the requested changes then {@code
@@ -174,6 +192,12 @@
             newConfigBuilder.setGeoDetectionEnabled(requestedChanges.isGeoDetectionEnabled());
         }
 
+        if (requestedChanges.hasIsNotificationsEnabled()) {
+            if (this.getConfigureNotificationsEnabledCapability() < CAPABILITY_NOT_APPLICABLE) {
+                return null;
+            }
+            newConfigBuilder.setNotificationsEnabled(requestedChanges.areNotificationsEnabled());
+        }
         return newConfigBuilder.build();
     }
 
@@ -197,13 +221,16 @@
                 && mUseLocationEnabled == that.mUseLocationEnabled
                 && mConfigureGeoDetectionEnabledCapability
                 == that.mConfigureGeoDetectionEnabledCapability
-                && mSetManualTimeZoneCapability == that.mSetManualTimeZoneCapability;
+                && mSetManualTimeZoneCapability == that.mSetManualTimeZoneCapability
+                && mConfigureNotificationsEnabledCapability
+                == that.mConfigureNotificationsEnabledCapability;
     }
 
     @Override
     public int hashCode() {
         return Objects.hash(mUserHandle, mConfigureAutoDetectionEnabledCapability,
-                mConfigureGeoDetectionEnabledCapability, mSetManualTimeZoneCapability);
+                mConfigureGeoDetectionEnabledCapability, mSetManualTimeZoneCapability,
+                mConfigureNotificationsEnabledCapability);
     }
 
     @Override
@@ -216,6 +243,8 @@
                 + ", mConfigureGeoDetectionEnabledCapability="
                 + mConfigureGeoDetectionEnabledCapability
                 + ", mSetManualTimeZoneCapability=" + mSetManualTimeZoneCapability
+                + ", mConfigureNotificationsEnabledCapability="
+                + mConfigureNotificationsEnabledCapability
                 + '}';
     }
 
@@ -226,11 +255,13 @@
      */
     public static class Builder {
 
-        @NonNull private UserHandle mUserHandle;
+        @NonNull
+        private UserHandle mUserHandle;
         private @CapabilityState int mConfigureAutoDetectionEnabledCapability;
         private Boolean mUseLocationEnabled;
         private @CapabilityState int mConfigureGeoDetectionEnabledCapability;
         private @CapabilityState int mSetManualTimeZoneCapability;
+        private @CapabilityState int mConfigureNotificationsEnabledCapability;
 
         public Builder(@NonNull UserHandle userHandle) {
             mUserHandle = Objects.requireNonNull(userHandle);
@@ -240,12 +271,14 @@
             Objects.requireNonNull(capabilitiesToCopy);
             mUserHandle = capabilitiesToCopy.mUserHandle;
             mConfigureAutoDetectionEnabledCapability =
-                capabilitiesToCopy.mConfigureAutoDetectionEnabledCapability;
+                    capabilitiesToCopy.mConfigureAutoDetectionEnabledCapability;
             mUseLocationEnabled = capabilitiesToCopy.mUseLocationEnabled;
             mConfigureGeoDetectionEnabledCapability =
-                capabilitiesToCopy.mConfigureGeoDetectionEnabledCapability;
+                    capabilitiesToCopy.mConfigureGeoDetectionEnabledCapability;
             mSetManualTimeZoneCapability =
-                capabilitiesToCopy.mSetManualTimeZoneCapability;
+                    capabilitiesToCopy.mSetManualTimeZoneCapability;
+            mConfigureNotificationsEnabledCapability =
+                    capabilitiesToCopy.mConfigureNotificationsEnabledCapability;
         }
 
         /** Sets the value for the "configure automatic time zone detection enabled" capability. */
@@ -274,6 +307,14 @@
             return this;
         }
 
+        /**
+         * Sets the value for the "configure time notifications enabled" capability.
+         */
+        public Builder setConfigureNotificationsEnabledCapability(@CapabilityState int value) {
+            this.mConfigureNotificationsEnabledCapability = value;
+            return this;
+        }
+
         /** Returns the {@link TimeZoneCapabilities}. */
         @NonNull
         public TimeZoneCapabilities build() {
@@ -283,7 +324,9 @@
             verifyCapabilitySet(mConfigureGeoDetectionEnabledCapability,
                     "configureGeoDetectionEnabledCapability");
             verifyCapabilitySet(mSetManualTimeZoneCapability,
-                    "mSetManualTimeZoneCapability");
+                    "setManualTimeZoneCapability");
+            verifyCapabilitySet(mConfigureNotificationsEnabledCapability,
+                    "configureNotificationsEnabledCapability");
             return new TimeZoneCapabilities(this);
         }
 
diff --git a/core/java/android/app/time/TimeZoneConfiguration.java b/core/java/android/app/time/TimeZoneConfiguration.java
index 7403c12..68c090f 100644
--- a/core/java/android/app/time/TimeZoneConfiguration.java
+++ b/core/java/android/app/time/TimeZoneConfiguration.java
@@ -62,7 +62,8 @@
      *
      * @hide
      */
-    @StringDef({ SETTING_AUTO_DETECTION_ENABLED, SETTING_GEO_DETECTION_ENABLED })
+    @StringDef({SETTING_AUTO_DETECTION_ENABLED, SETTING_GEO_DETECTION_ENABLED,
+            SETTING_NOTIFICATIONS_ENABLED})
     @Retention(RetentionPolicy.SOURCE)
     @interface Setting {}
 
@@ -74,6 +75,10 @@
     @Setting
     private static final String SETTING_GEO_DETECTION_ENABLED = "geoDetectionEnabled";
 
+    /** See {@link TimeZoneConfiguration#areNotificationsEnabled()} for details. */
+    @Setting
+    private static final String SETTING_NOTIFICATIONS_ENABLED = "notificationsEnabled";
+
     @NonNull private final Bundle mBundle;
 
     private TimeZoneConfiguration(Builder builder) {
@@ -98,7 +103,8 @@
      */
     public boolean isComplete() {
         return hasIsAutoDetectionEnabled()
-                && hasIsGeoDetectionEnabled();
+                && hasIsGeoDetectionEnabled()
+                && hasIsNotificationsEnabled();
     }
 
     /**
@@ -128,8 +134,7 @@
     /**
      * Returns the value of the {@link #SETTING_GEO_DETECTION_ENABLED} setting. This
      * controls whether the device can use geolocation to determine time zone. This value may only
-     * be used by Android under some circumstances. For example, it is not used when
-     * {@link #isGeoDetectionEnabled()} is {@code false}.
+     * be used by Android under some circumstances.
      *
      * <p>See {@link TimeZoneCapabilities#getConfigureGeoDetectionEnabledCapability()} for how to
      * tell if the setting is meaningful for the current user at this time.
@@ -150,6 +155,32 @@
         return mBundle.containsKey(SETTING_GEO_DETECTION_ENABLED);
     }
 
+    /**
+     * Returns the value of the {@link #SETTING_NOTIFICATIONS_ENABLED} setting. This controls
+     * whether the device can send time and time zone related notifications. This value may only
+     * be used by Android under some circumstances.
+     *
+     * <p>See {@link TimeZoneCapabilities#getConfigureNotificationsEnabledCapability()} ()} for how
+     * to tell if the setting is meaningful for the current user at this time.
+     *
+     * @throws IllegalStateException if the setting is not present
+     *
+     * @hide
+     */
+    public boolean areNotificationsEnabled() {
+        enforceSettingPresent(SETTING_NOTIFICATIONS_ENABLED);
+        return mBundle.getBoolean(SETTING_NOTIFICATIONS_ENABLED);
+    }
+
+    /**
+     * Returns {@code true} if the {@link #areNotificationsEnabled()} setting is present.
+     *
+     * @hide
+     */
+    public boolean hasIsNotificationsEnabled() {
+        return mBundle.containsKey(SETTING_NOTIFICATIONS_ENABLED);
+    }
+
     @Override
     public int describeContents() {
         return 0;
@@ -244,6 +275,17 @@
             return this;
         }
 
+        /**
+         * Sets the state of the {@link #SETTING_NOTIFICATIONS_ENABLED} setting.         *
+         *
+         * @hide
+         */
+        @NonNull
+        public Builder setNotificationsEnabled(boolean enabled) {
+            this.mBundle.putBoolean(SETTING_NOTIFICATIONS_ENABLED, enabled);
+            return this;
+        }
+
         /** Returns the {@link TimeZoneConfiguration}. */
         @NonNull
         public TimeZoneConfiguration build() {
diff --git a/core/java/android/companion/virtual/flags.aconfig b/core/java/android/companion/virtual/flags/deprecated_flags_do_not_edit.aconfig
similarity index 80%
rename from core/java/android/companion/virtual/flags.aconfig
rename to core/java/android/companion/virtual/flags/deprecated_flags_do_not_edit.aconfig
index 46da4a3..eae5062 100644
--- a/core/java/android/companion/virtual/flags.aconfig
+++ b/core/java/android/companion/virtual/flags/deprecated_flags_do_not_edit.aconfig
@@ -1,24 +1,18 @@
-# Do not add new flags to this file.
+# Do not modify this file.
 #
-# Due to "virtual" keyword in the package name flags
-# added to this file cannot be accessed from C++
-# code.
+# Due to "virtual" keyword in the package name flags added to this file cannot
+# be accessed from C++ code.
 #
 # Use frameworks/base/core/java/android/companion/virtual/flags/flags.aconfig
-# instead.
+# instead for new flags.
+#
+# All of the remaining flags here have been used for API flagging and are
+# therefore exported and should not be deleted.
 
 package: "android.companion.virtual.flags"
 container: "system"
 
 flag {
-  name: "enable_native_vdm"
-  namespace: "virtual_devices"
-  description: "Enable native VDM service"
-  bug: "303535376"
-  is_fixed_read_only: true
-}
-
-flag {
   name: "dynamic_policy"
   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 de01280..84af840 100644
--- a/core/java/android/companion/virtual/flags/flags.aconfig
+++ b/core/java/android/companion/virtual/flags/flags.aconfig
@@ -1,17 +1,11 @@
+# VirtualDeviceManager flags
 #
-# Copyright (C) 2023 The Android Open Source Project
+# This file contains flags guarding features that are in development.
 #
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
+# Once a flag is launched or abandoned and there are no more references to it in
+# the codebase, it should be either:
+#  - deleted, or
+#  - moved to launched_flags.aconfig if it was launched and used for API flagging.
 
 package: "android.companion.virtualdevice.flags"
 container: "system"
diff --git a/core/java/android/companion/virtual/flags/launched_flags.aconfig b/core/java/android/companion/virtual/flags/launched_flags.aconfig
new file mode 100644
index 0000000..ee89631
--- /dev/null
+++ b/core/java/android/companion/virtual/flags/launched_flags.aconfig
@@ -0,0 +1,6 @@
+# This file contains the launched VirtualDeviceManager flags from the
+# "android.companion.virtualdevice.flags" package that cannot be deleted because
+# they have been used for API flagging.
+
+package: "android.companion.virtualdevice.flags"
+container: "system"
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 3d75423..350048d 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -12394,35 +12394,57 @@
      * @hide
      */
     public void collectExtraIntentKeys() {
+        collectExtraIntentKeys(false);
+    }
+
+    /**
+     * Collects keys in the extra bundle whose value are intents.
+     * With these keys collected on the client side, the system server would only unparcel values
+     * of these keys and create IntentCreatorToken for them.
+     * This method could also be called from the system server side as a catch all safty net in case
+     * these keys are not collected on the client side. In that case, call it with forceUnparcel set
+     * to true since everything is parceled on the system server side.
+     *
+     * @param forceUnparcel if it is true, unparcel everything to determine if an object is an
+     *                      intent. Otherwise, do not unparcel anything.
+     * @hide
+     */
+    public void collectExtraIntentKeys(boolean forceUnparcel) {
         if (preventIntentRedirect()) {
-            collectNestedIntentKeysRecur(new ArraySet<>());
+            collectNestedIntentKeysRecur(new ArraySet<>(), forceUnparcel);
         }
     }
 
-    private void collectNestedIntentKeysRecur(Set<Intent> visited) {
+    private void collectNestedIntentKeysRecur(Set<Intent> visited, boolean forceUnparcel) {
         addExtendedFlags(EXTENDED_FLAG_NESTED_INTENT_KEYS_COLLECTED);
-        if (mExtras != null && !mExtras.isEmpty()) {
+        if (mExtras != null && (forceUnparcel || !mExtras.isParcelled()) && !mExtras.isEmpty()) {
             for (String key : mExtras.keySet()) {
                 Object value;
                 try {
-                    value = mExtras.get(key);
+                    // Do not unparcel any Parcelable objects. It may cause issues for app who would
+                    // change class loader before it reads a parceled value. b/382633789.
+                    // It is okay to not collect a parceled intent since it would have been
+                    // coming from another process and collected by its containing intent already
+                    // in that process.
+                    if (forceUnparcel || !mExtras.isValueParceled(key)) {
+                        value = mExtras.get(key);
+                    } else {
+                        value = null;
+                    }
                 } catch (BadParcelableException e) {
-                    // This could happen when the key points to a LazyValue whose class cannot be
-                    // found by the classLoader - A nested object more than 1 level deeper who is
-                    // of type of a custom class could trigger this situation. In such case, we
-                    // ignore it since it is not an intent. However, it could be a custom type that
-                    // extends from Intent. If such an object is retrieved later in another
-                    // component, then trying to launch such a custom class object will fail unless
-                    // removeLaunchSecurityProtection() is called before it is launched.
+                    // This may still happen if the keys are collected on the system server side, in
+                    // which case, we will try to unparcel everything. If this happens, simply
+                    // ignore it since it is not an intent anyway.
                     value = null;
                 }
                 if (value instanceof Intent intent) {
                     handleNestedIntent(intent, visited, new NestedIntentKey(
-                            NestedIntentKey.NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL, key, 0));
+                                    NestedIntentKey.NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL, key, 0),
+                            forceUnparcel);
                 } else if (value instanceof Parcelable[] parcelables) {
-                    handleParcelableArray(parcelables, key, visited);
+                    handleParcelableArray(parcelables, key, visited, forceUnparcel);
                 } else if (value instanceof ArrayList<?> parcelables) {
-                    handleParcelableList(parcelables, key, visited);
+                    handleParcelableList(parcelables, key, visited, forceUnparcel);
                 }
             }
         }
@@ -12432,13 +12454,15 @@
                 Intent intent = mClipData.getItemAt(i).mIntent;
                 if (intent != null && !visited.contains(intent)) {
                     handleNestedIntent(intent, visited, new NestedIntentKey(
-                            NestedIntentKey.NESTED_INTENT_KEY_TYPE_CLIP_DATA, null, i));
+                                    NestedIntentKey.NESTED_INTENT_KEY_TYPE_CLIP_DATA, null, i),
+                            forceUnparcel);
                 }
             }
         }
     }
 
-    private void handleNestedIntent(Intent intent, Set<Intent> visited, NestedIntentKey key) {
+    private void handleNestedIntent(Intent intent, Set<Intent> visited, NestedIntentKey key,
+            boolean forceUnparcel) {
         if (mCreatorTokenInfo == null) {
             mCreatorTokenInfo = new CreatorTokenInfo();
         }
@@ -12448,24 +12472,28 @@
         mCreatorTokenInfo.mNestedIntentKeys.add(key);
         if (!visited.contains(intent)) {
             visited.add(intent);
-            intent.collectNestedIntentKeysRecur(visited);
+            intent.collectNestedIntentKeysRecur(visited, forceUnparcel);
         }
     }
 
-    private void handleParcelableArray(Parcelable[] parcelables, String key, Set<Intent> visited) {
+    private void handleParcelableArray(Parcelable[] parcelables, String key, Set<Intent> visited,
+            boolean forceUnparcel) {
         for (int i = 0; i < parcelables.length; i++) {
             if (parcelables[i] instanceof Intent intent && !visited.contains(intent)) {
                 handleNestedIntent(intent, visited, new NestedIntentKey(
-                        NestedIntentKey.NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL_ARRAY, key, i));
+                                NestedIntentKey.NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL_ARRAY, key, i),
+                        forceUnparcel);
             }
         }
     }
 
-    private void handleParcelableList(ArrayList<?> parcelables, String key, Set<Intent> visited) {
+    private void handleParcelableList(ArrayList<?> parcelables, String key, Set<Intent> visited,
+            boolean forceUnparcel) {
         for (int i = 0; i < parcelables.size(); i++) {
             if (parcelables.get(i) instanceof Intent intent && !visited.contains(intent)) {
                 handleNestedIntent(intent, visited, new NestedIntentKey(
-                        NestedIntentKey.NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL_LIST, key, i));
+                                NestedIntentKey.NESTED_INTENT_KEY_TYPE_EXTRA_PARCEL_LIST, key, i),
+                        forceUnparcel);
             }
         }
     }
diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java
index 0333942..9d11710 100644
--- a/core/java/android/content/pm/RegisteredServicesCache.java
+++ b/core/java/android/content/pm/RegisteredServicesCache.java
@@ -17,6 +17,7 @@
 package android.content.pm;
 
 import android.Manifest;
+import android.annotation.NonNull;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -30,6 +31,7 @@
 import android.os.Handler;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.util.ArrayMap;
 import android.util.AtomicFile;
 import android.util.AttributeSet;
 import android.util.IntArray;
@@ -45,11 +47,11 @@
 import com.android.modules.utils.TypedXmlPullParser;
 import com.android.modules.utils.TypedXmlSerializer;
 
-import libcore.io.IoUtils;
-
 import com.google.android.collect.Lists;
 import com.google.android.collect.Maps;
 
+import libcore.io.IoUtils;
+
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
@@ -94,6 +96,9 @@
     @GuardedBy("mServicesLock")
     private final SparseArray<UserServices<V>> mUserServices = new SparseArray<UserServices<V>>(2);
 
+    @GuardedBy("mServicesLock")
+    private final ArrayMap<String, ServiceInfo<V>> mServiceInfoCaches = new ArrayMap<>();
+
     private static class UserServices<V> {
         @GuardedBy("mServicesLock")
         final Map<V, Integer> persistentServices = Maps.newHashMap();
@@ -323,13 +328,16 @@
         public final ComponentName componentName;
         @UnsupportedAppUsage
         public final int uid;
+        public final long lastUpdateTime;
 
         /** @hide */
-        public ServiceInfo(V type, ComponentInfo componentInfo, ComponentName componentName) {
+        public ServiceInfo(V type, ComponentInfo componentInfo, ComponentName componentName,
+                long lastUpdateTime) {
             this.type = type;
             this.componentInfo = componentInfo;
             this.componentName = componentName;
             this.uid = (componentInfo != null) ? componentInfo.applicationInfo.uid : -1;
+            this.lastUpdateTime = lastUpdateTime;
         }
 
         @Override
@@ -490,7 +498,7 @@
         final List<ResolveInfo> resolveInfos = queryIntentServices(userId);
         for (ResolveInfo resolveInfo : resolveInfos) {
             try {
-                ServiceInfo<V> info = parseServiceInfo(resolveInfo);
+                ServiceInfo<V> info = parseServiceInfo(resolveInfo, userId);
                 if (info == null) {
                     Log.w(TAG, "Unable to load service info " + resolveInfo.toString());
                     continue;
@@ -638,13 +646,31 @@
     }
 
     @VisibleForTesting
-    protected ServiceInfo<V> parseServiceInfo(ResolveInfo service)
+    protected ServiceInfo<V> parseServiceInfo(ResolveInfo service, int userId)
             throws XmlPullParserException, IOException {
         android.content.pm.ServiceInfo si = service.serviceInfo;
         ComponentName componentName = new ComponentName(si.packageName, si.name);
 
         PackageManager pm = mContext.getPackageManager();
 
+        // Check if the service has been in the service cache.
+        long lastUpdateTime = -1;
+        if (Flags.optimizeParsingInRegisteredServicesCache()) {
+            try {
+                PackageInfo packageInfo = pm.getPackageInfoAsUser(si.packageName,
+                        PackageManager.MATCH_DIRECT_BOOT_AWARE
+                                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId);
+                lastUpdateTime = packageInfo.lastUpdateTime;
+
+                ServiceInfo<V> serviceInfo = getServiceInfoFromServiceCache(si, lastUpdateTime);
+                if (serviceInfo != null) {
+                    return serviceInfo;
+                }
+            } catch (NameNotFoundException | SecurityException e) {
+                Slog.d(TAG, "Fail to get the PackageInfo in parseServiceInfo: " + e);
+            }
+        }
+
         XmlResourceParser parser = null;
         try {
             parser = si.loadXmlMetaData(pm, mMetaDataName);
@@ -670,8 +696,13 @@
             if (v == null) {
                 return null;
             }
-            final android.content.pm.ServiceInfo serviceInfo = service.serviceInfo;
-            return new ServiceInfo<V>(v, serviceInfo, componentName);
+            ServiceInfo<V> serviceInfo = new ServiceInfo<V>(v, si, componentName, lastUpdateTime);
+            if (Flags.optimizeParsingInRegisteredServicesCache()) {
+                synchronized (mServicesLock) {
+                    mServiceInfoCaches.put(getServiceCacheKey(si), serviceInfo);
+                }
+            }
+            return serviceInfo;
         } catch (NameNotFoundException e) {
             throw new XmlPullParserException(
                     "Unable to load resources for pacakge " + si.packageName);
@@ -841,4 +872,28 @@
         mContext.unregisterReceiver(mExternalReceiver);
         mContext.unregisterReceiver(mUserRemovedReceiver);
     }
+
+    private static String getServiceCacheKey(@NonNull android.content.pm.ServiceInfo serviceInfo) {
+        StringBuilder sb = new StringBuilder(serviceInfo.packageName);
+        sb.append('-');
+        sb.append(serviceInfo.name);
+        return sb.toString();
+    }
+
+    private ServiceInfo<V> getServiceInfoFromServiceCache(
+            @NonNull android.content.pm.ServiceInfo serviceInfo, long lastUpdateTime) {
+        String serviceCacheKey = getServiceCacheKey(serviceInfo);
+        synchronized (mServicesLock) {
+            ServiceInfo<V> serviceCache = mServiceInfoCaches.get(serviceCacheKey);
+            if (serviceCache == null) {
+                return null;
+            }
+            if (serviceCache.lastUpdateTime == lastUpdateTime) {
+                return serviceCache;
+            }
+            // The service is not latest, remove it from the cache.
+            mServiceInfoCaches.remove(serviceCacheKey);
+            return null;
+        }
+    }
 }
diff --git a/media/java/android/media/quality/ParamCapability.aidl b/core/java/android/content/pm/SigningInfo.aidl
similarity index 89%
copy from media/java/android/media/quality/ParamCapability.aidl
copy to core/java/android/content/pm/SigningInfo.aidl
index b43409d..bc986d1 100644
--- a/media/java/android/media/quality/ParamCapability.aidl
+++ b/core/java/android/content/pm/SigningInfo.aidl
@@ -12,8 +12,8 @@
  * WITHOUT WARRANTIES 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.media.quality;
+package android.content.pm;
 
-parcelable ParamCapability;
+parcelable SigningInfo;
\ No newline at end of file
diff --git a/core/java/android/content/pm/SystemFeaturesCache.aidl b/core/java/android/content/pm/SystemFeaturesCache.aidl
new file mode 100644
index 0000000..18c1830
--- /dev/null
+++ b/core/java/android/content/pm/SystemFeaturesCache.aidl
@@ -0,0 +1,19 @@
+/*
+** Copyright 2025, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES 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;
+
+parcelable SystemFeaturesCache;
diff --git a/core/java/android/content/pm/SystemFeaturesCache.java b/core/java/android/content/pm/SystemFeaturesCache.java
new file mode 100644
index 0000000..c41a7ab
--- /dev/null
+++ b/core/java/android/content/pm/SystemFeaturesCache.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.ArrayMap;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.Arrays;
+import java.util.Collection;
+
+/**
+ * A simple cache for SDK-defined system feature versions.
+ *
+ * The dense representation minimizes any per-process memory impact (<1KB). The tradeoff is that
+ * custom, non-SDK defined features are not captured by the cache, for which we can rely on the
+ * usual IPC cache for related queries.
+ *
+ * @hide
+ */
+public final class SystemFeaturesCache implements Parcelable {
+
+    // Sentinel value used for SDK-declared features that are unavailable on the current device.
+    private static final int UNAVAILABLE_FEATURE_VERSION = Integer.MIN_VALUE;
+
+    // An array of versions for SDK-defined features, from [0, PackageManager.SDK_FEATURE_COUNT).
+    @NonNull
+    private final int[] mSdkFeatureVersions;
+
+    /**
+     * Populates the cache from the set of all available {@link FeatureInfo} definitions.
+     *
+     * System features declared in {@link PackageManager} will be entered into the cache based on
+     * availability in this feature set. Other custom system features will be ignored.
+     */
+    public SystemFeaturesCache(@NonNull ArrayMap<String, FeatureInfo> availableFeatures) {
+        this(availableFeatures.values());
+    }
+
+    @VisibleForTesting
+    public SystemFeaturesCache(@NonNull Collection<FeatureInfo> availableFeatures) {
+        // First set all SDK-defined features as unavailable.
+        mSdkFeatureVersions = new int[PackageManager.SDK_FEATURE_COUNT];
+        Arrays.fill(mSdkFeatureVersions, UNAVAILABLE_FEATURE_VERSION);
+
+        // Then populate SDK-defined feature versions from the full set of runtime features.
+        for (FeatureInfo fi : availableFeatures) {
+            int sdkFeatureIndex = PackageManager.maybeGetSdkFeatureIndex(fi.name);
+            if (sdkFeatureIndex >= 0) {
+                mSdkFeatureVersions[sdkFeatureIndex] = fi.version;
+            }
+        }
+    }
+
+    /** Only used by @{code CREATOR.createFromParcel(...)} */
+    private SystemFeaturesCache(@NonNull Parcel parcel) {
+        final int[] featureVersions = parcel.createIntArray();
+        if (featureVersions == null) {
+            throw new IllegalArgumentException(
+                    "Parceled SDK feature versions should never be null");
+        }
+        if (featureVersions.length != PackageManager.SDK_FEATURE_COUNT) {
+            throw new IllegalArgumentException(
+                    String.format(
+                            "Unexpected cached SDK feature count: %d (expected %d)",
+                            featureVersions.length, PackageManager.SDK_FEATURE_COUNT));
+        }
+        mSdkFeatureVersions = featureVersions;
+    }
+
+    /**
+     * @return Whether the given feature is available (for SDK-defined features), otherwise null.
+     */
+    public Boolean maybeHasFeature(@NonNull String featureName, int version) {
+        // Features defined outside of the SDK aren't cached.
+        int sdkFeatureIndex = PackageManager.maybeGetSdkFeatureIndex(featureName);
+        if (sdkFeatureIndex < 0) {
+            return null;
+        }
+
+        // As feature versions can in theory collide with our sentinel value, in the (extremely)
+        // unlikely event that the queried version matches the sentinel value, we can't distinguish
+        // between an unavailable feature and a feature with the defined sentinel value.
+        if (version == UNAVAILABLE_FEATURE_VERSION
+                && mSdkFeatureVersions[sdkFeatureIndex] == UNAVAILABLE_FEATURE_VERSION) {
+            return null;
+        }
+
+        return mSdkFeatureVersions[sdkFeatureIndex] >= version;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel parcel, int flags) {
+        parcel.writeIntArray(mSdkFeatureVersions);
+    }
+
+    @NonNull
+    public static final Parcelable.Creator<SystemFeaturesCache> CREATOR =
+            new Parcelable.Creator<SystemFeaturesCache>() {
+
+                @Override
+                public SystemFeaturesCache createFromParcel(Parcel parcel) {
+                    return new SystemFeaturesCache(parcel);
+                }
+
+                @Override
+                public SystemFeaturesCache[] newArray(int size) {
+                    return new SystemFeaturesCache[size];
+                }
+            };
+}
diff --git a/core/java/android/content/pm/flags.aconfig b/core/java/android/content/pm/flags.aconfig
index 7bba06c..e4b8c90 100644
--- a/core/java/android/content/pm/flags.aconfig
+++ b/core/java/android/content/pm/flags.aconfig
@@ -383,3 +383,11 @@
     bug: "334024639"
     description: "Feature flag to check whether a given UID can access a content provider"
 }
+
+flag {
+    name: "optimize_parsing_in_registered_services_cache"
+    namespace: "package_manager_service"
+    description: "Feature flag to optimize RegisteredServicesCache ServiceInfo parsing by using caches."
+    bug: "319137634"
+    is_fixed_read_only: true
+}
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index f29e2e8..4a579a4 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -211,6 +211,16 @@
 }
 
 flag {
+    name: "place_add_user_dialog_within_activity"
+    namespace: "multiuser"
+    description: "Display dialog within activity to make it traversable by Accessibility"
+    bug: "376815882"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
     name: "property_invalidated_cache_bypass_mismatched_uids"
     namespace: "multiuser"
     description: "Bypass the cache when the process UID does not match the binder UID."
diff --git a/core/java/android/content/res/ApkAssets.java b/core/java/android/content/res/ApkAssets.java
index 908999b..f538e9f 100644
--- a/core/java/android/content/res/ApkAssets.java
+++ b/core/java/android/content/res/ApkAssets.java
@@ -25,6 +25,7 @@
 import android.ravenwood.annotation.RavenwoodClassLoadHook;
 import android.ravenwood.annotation.RavenwoodKeepWholeClass;
 import android.text.TextUtils;
+import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
 
@@ -50,6 +51,7 @@
 @RavenwoodKeepWholeClass
 @RavenwoodClassLoadHook(RavenwoodClassLoadHook.LIBANDROID_LOADING_HOOK)
 public final class ApkAssets {
+    private static final boolean DEBUG = false;
 
     /**
      * The apk assets contains framework resource values specified by the system.
@@ -134,6 +136,17 @@
     @Nullable
     private final AssetsProvider mAssets;
 
+    @NonNull
+    private String mName;
+
+    private static final int UPTODATE_FALSE = 0;
+    private static final int UPTODATE_TRUE = 1;
+    private static final int UPTODATE_ALWAYS_TRUE = 2;
+
+    // Start with the only value that may change later and would force a native call to
+    // double check it.
+    private int mPreviousUpToDateResult = UPTODATE_TRUE;
+
     /**
      * Creates a new ApkAssets instance from the given path on disk.
      *
@@ -304,7 +317,7 @@
 
     private ApkAssets(@FormatType int format, @NonNull String path, @PropertyFlags int flags,
             @Nullable AssetsProvider assets) throws IOException {
-        this(format, flags, assets);
+        this(format, flags, assets, path);
         Objects.requireNonNull(path, "path");
         mNativePtr = nativeLoad(format, path, flags, assets);
         mStringBlock = new StringBlock(nativeGetStringBlock(mNativePtr), true /*useSparse*/);
@@ -313,7 +326,7 @@
     private ApkAssets(@FormatType int format, @NonNull FileDescriptor fd,
             @NonNull String friendlyName, @PropertyFlags int flags, @Nullable AssetsProvider assets)
             throws IOException {
-        this(format, flags, assets);
+        this(format, flags, assets, friendlyName);
         Objects.requireNonNull(fd, "fd");
         Objects.requireNonNull(friendlyName, "friendlyName");
         mNativePtr = nativeLoadFd(format, fd, friendlyName, flags, assets);
@@ -323,7 +336,7 @@
     private ApkAssets(@FormatType int format, @NonNull FileDescriptor fd,
             @NonNull String friendlyName, long offset, long length, @PropertyFlags int flags,
             @Nullable AssetsProvider assets) throws IOException {
-        this(format, flags, assets);
+        this(format, flags, assets, friendlyName);
         Objects.requireNonNull(fd, "fd");
         Objects.requireNonNull(friendlyName, "friendlyName");
         mNativePtr = nativeLoadFdOffsets(format, fd, friendlyName, offset, length, flags, assets);
@@ -331,16 +344,17 @@
     }
 
     private ApkAssets(@PropertyFlags int flags, @Nullable AssetsProvider assets) {
-        this(FORMAT_APK, flags, assets);
+        this(FORMAT_APK, flags, assets, "empty");
         mNativePtr = nativeLoadEmpty(flags, assets);
         mStringBlock = null;
     }
 
     private ApkAssets(@FormatType int format, @PropertyFlags int flags,
-            @Nullable AssetsProvider assets) {
+            @Nullable AssetsProvider assets, @NonNull String name) {
         mFlags = flags;
         mAssets = assets;
         mIsOverlay = format == FORMAT_IDMAP;
+        if (DEBUG) mName = name;
     }
 
     @UnsupportedAppUsage
@@ -353,7 +367,7 @@
     /** @hide */
     public @NonNull String getDebugName() {
         synchronized (this) {
-            return nativeGetDebugName(mNativePtr);
+            return mNativePtr == 0 ? "<destroyed>" : nativeGetDebugName(mNativePtr);
         }
     }
 
@@ -421,13 +435,41 @@
         }
     }
 
+    private static double intervalMs(long beginNs, long endNs) {
+        return (endNs - beginNs) / 1000000.0;
+    }
+
     /**
      * Returns false if the underlying APK was changed since this ApkAssets was loaded.
      */
     public boolean isUpToDate() {
-        synchronized (this) {
-            return nativeIsUpToDate(mNativePtr);
+        // This function is performance-critical - it's called multiple times on every Resources
+        // object creation, and on few other cache accesses - so it's important to avoid the native
+        // call when we know for sure what it will return (which is the case for both ALWAYS_TRUE
+        // and FALSE).
+        if (mPreviousUpToDateResult != UPTODATE_TRUE) {
+            return mPreviousUpToDateResult == UPTODATE_ALWAYS_TRUE;
         }
+        final long beforeTs, afterLockTs, afterNativeTs, afterUnlockTs;
+        if (DEBUG) beforeTs = System.nanoTime();
+        final int res;
+        synchronized (this) {
+            if (DEBUG) afterLockTs = System.nanoTime();
+            res = nativeIsUpToDate(mNativePtr);
+            if (DEBUG) afterNativeTs = System.nanoTime();
+        }
+        if (DEBUG) {
+            afterUnlockTs = System.nanoTime();
+            if (afterUnlockTs - beforeTs >= 10L * 1000000) {
+                Log.d("ApkAssets", "isUpToDate(" + mName + ") took "
+                        + intervalMs(beforeTs, afterUnlockTs)
+                        + " ms: " + intervalMs(beforeTs, afterLockTs)
+                        + " / " + intervalMs(afterLockTs, afterNativeTs)
+                        + " / " + intervalMs(afterNativeTs, afterUnlockTs));
+            }
+        }
+        mPreviousUpToDateResult = res;
+        return res != UPTODATE_FALSE;
     }
 
     public boolean isSystem() {
@@ -487,7 +529,7 @@
     private static native @NonNull String nativeGetAssetPath(long ptr);
     private static native @NonNull String nativeGetDebugName(long ptr);
     private static native long nativeGetStringBlock(long ptr);
-    @CriticalNative private static native boolean nativeIsUpToDate(long ptr);
+    @CriticalNative private static native int nativeIsUpToDate(long ptr);
     private static native long nativeOpenXml(long ptr, @NonNull String fileName) throws IOException;
     private static native @Nullable OverlayableInfo nativeGetOverlayableInfo(long ptr,
             String overlayableName) throws IOException;
diff --git a/core/java/android/content/res/ResourceTimer.java b/core/java/android/content/res/ResourceTimer.java
index d51f64c..2d1bf4d 100644
--- a/core/java/android/content/res/ResourceTimer.java
+++ b/core/java/android/content/res/ResourceTimer.java
@@ -17,13 +17,10 @@
 package android.content.res;
 
 import android.annotation.NonNull;
-import android.annotation.Nullable;
-
 import android.app.AppProtoEnums;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
-import android.os.ParcelFileDescriptor;
 import android.os.Process;
 import android.os.SystemClock;
 import android.text.TextUtils;
@@ -33,6 +30,7 @@
 import com.android.internal.util.FastPrintWriter;
 import com.android.internal.util.FrameworkStatsLog;
 
+import java.io.FileDescriptor;
 import java.io.FileOutputStream;
 import java.io.PrintWriter;
 import java.util.Arrays;
@@ -277,38 +275,40 @@
      * Update the metrics information and dump it.
      * @hide
      */
-    public static void dumpTimers(@NonNull ParcelFileDescriptor pfd, @Nullable String[] args) {
-        FileOutputStream fout = new FileOutputStream(pfd.getFileDescriptor());
-        PrintWriter pw = new FastPrintWriter(fout);
-        synchronized (sLock) {
-            if (!sEnabled || (sConfig == null)) {
+    public static void dumpTimers(@NonNull FileDescriptor fd, String... args) {
+        try (PrintWriter pw = new FastPrintWriter(new FileOutputStream(fd))) {
+            pw.println("\nDumping ResourceTimers");
+
+            final boolean enabled;
+            synchronized (sLock) {
+                enabled = sEnabled && sConfig != null;
+            }
+            if (!enabled) {
                 pw.println("  Timers are not enabled in this process");
-                pw.flush();
                 return;
             }
-        }
 
-        // Look for the --refresh switch.  If the switch is present, then sTimers is updated.
-        // Otherwise, the current value of sTimers is displayed.
-        boolean refresh = Arrays.asList(args).contains("-refresh");
+            // Look for the --refresh switch.  If the switch is present, then sTimers is updated.
+            // Otherwise, the current value of sTimers is displayed.
+            boolean refresh = Arrays.asList(args).contains("-refresh");
 
-        synchronized (sLock) {
-            update(refresh);
-            long runtime = sLastUpdated - sProcessStart;
-            pw.format("  config runtime=%d proc=%s\n", runtime, Process.myProcessName());
-            for (int i = 0; i < sTimers.length; i++) {
-                Timer t = sTimers[i];
-                if (t.count != 0) {
-                    String name = sConfig.timers[i];
-                    pw.format("  stats timer=%s cnt=%d avg=%d min=%d max=%d pval=%s "
-                        + "largest=%s\n",
-                        name, t.count, t.total / t.count, t.mintime, t.maxtime,
-                        packedString(t.percentile),
-                        packedString(t.largest));
+            synchronized (sLock) {
+                update(refresh);
+                long runtime = sLastUpdated - sProcessStart;
+                pw.format("  config runtime=%d proc=%s\n", runtime, Process.myProcessName());
+                for (int i = 0; i < sTimers.length; i++) {
+                    Timer t = sTimers[i];
+                    if (t.count != 0) {
+                        String name = sConfig.timers[i];
+                        pw.format("  stats timer=%s cnt=%d avg=%d min=%d max=%d pval=%s "
+                                        + "largest=%s\n",
+                                name, t.count, t.total / t.count, t.mintime, t.maxtime,
+                                packedString(t.percentile),
+                                packedString(t.largest));
+                    }
                 }
             }
         }
-        pw.flush();
     }
 
     // Enable (or disabled) the runtime timers.  Note that timers are disabled by default.  This
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index 2d3d252..868429c 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -16,8 +16,6 @@
 
 package android.hardware;
 
-import static android.companion.virtual.VirtualDeviceManager.ACTION_VIRTUAL_DEVICE_REMOVED;
-import static android.companion.virtual.VirtualDeviceManager.EXTRA_VIRTUAL_DEVICE_ID;
 import static android.companion.virtual.VirtualDeviceParams.DEVICE_POLICY_DEFAULT;
 import static android.companion.virtual.VirtualDeviceParams.POLICY_TYPE_SENSORS;
 import static android.content.Context.DEVICE_ID_DEFAULT;
@@ -164,11 +162,7 @@
         // initialize the sensor list
         for (int index = 0;; ++index) {
             Sensor sensor = new Sensor();
-            if (android.companion.virtual.flags.Flags.enableNativeVdm()) {
-                if (!nativeGetDefaultDeviceSensorAtIndex(mNativeInstance, sensor, index)) break;
-            } else {
-                if (!nativeGetSensorAtIndex(mNativeInstance, sensor, index)) break;
-            }
+            if (!nativeGetDefaultDeviceSensorAtIndex(mNativeInstance, sensor, index)) break;
             mFullSensorsList.add(sensor);
             mHandleToSensor.put(sensor.getHandle(), sensor);
         }
@@ -555,11 +549,7 @@
     }
 
     private List<Sensor> createRuntimeSensorListLocked(int deviceId) {
-        if (android.companion.virtual.flags.Flags.vdmPublicApis()) {
-            setupVirtualDeviceListener();
-        } else {
-            setupRuntimeSensorBroadcastReceiver();
-        }
+        setupVirtualDeviceListener();
         List<Sensor> list = new ArrayList<>();
         nativeGetRuntimeSensors(mNativeInstance, deviceId, list);
         mFullRuntimeSensorListByDevice.put(deviceId, list);
@@ -570,35 +560,6 @@
         return list;
     }
 
-    private void setupRuntimeSensorBroadcastReceiver() {
-        if (mRuntimeSensorBroadcastReceiver == null) {
-            mRuntimeSensorBroadcastReceiver = new BroadcastReceiver() {
-                @Override
-                public void onReceive(Context context, Intent intent) {
-                    if (intent.getAction().equals(ACTION_VIRTUAL_DEVICE_REMOVED)) {
-                        synchronized (mFullRuntimeSensorListByDevice) {
-                            final int deviceId = intent.getIntExtra(
-                                    EXTRA_VIRTUAL_DEVICE_ID, DEVICE_ID_DEFAULT);
-                            List<Sensor> removedSensors =
-                                    mFullRuntimeSensorListByDevice.removeReturnOld(deviceId);
-                            if (removedSensors != null) {
-                                for (Sensor s : removedSensors) {
-                                    cleanupSensorConnection(s);
-                                }
-                            }
-                            mRuntimeSensorListByDeviceByType.remove(deviceId);
-                        }
-                    }
-                }
-            };
-
-            IntentFilter filter = new IntentFilter("virtual_device_removed");
-            filter.addAction(ACTION_VIRTUAL_DEVICE_REMOVED);
-            mContext.registerReceiver(mRuntimeSensorBroadcastReceiver, filter,
-                    Context.RECEIVER_NOT_EXPORTED);
-        }
-    }
-
     private void setupVirtualDeviceListener() {
         if (mVirtualDeviceListener != null) {
             return;
diff --git a/core/java/android/hardware/contexthub/HubEndpoint.java b/core/java/android/hardware/contexthub/HubEndpoint.java
index 6c669a3..25cdc50 100644
--- a/core/java/android/hardware/contexthub/HubEndpoint.java
+++ b/core/java/android/hardware/contexthub/HubEndpoint.java
@@ -137,6 +137,8 @@
                                                 serviceDescriptor,
                                                 mLifecycleCallback.onSessionOpenRequest(
                                                         initiator, serviceDescriptor)));
+                    } else {
+                        invokeCallbackFinished();
                     }
                 }
 
@@ -163,6 +165,8 @@
                                         + result.getReason());
                         rejectSession(sessionId);
                     }
+
+                    invokeCallbackFinished();
                 }
 
                 private void acceptSession(
@@ -249,7 +253,12 @@
                     activeSession.setOpened();
                     if (mLifecycleCallback != null) {
                         mLifecycleCallbackExecutor.execute(
-                                () -> mLifecycleCallback.onSessionOpened(activeSession));
+                                () -> {
+                                    mLifecycleCallback.onSessionOpened(activeSession);
+                                    invokeCallbackFinished();
+                                });
+                    } else {
+                        invokeCallbackFinished();
                     }
                 }
 
@@ -278,7 +287,10 @@
                                     synchronized (mLock) {
                                         mActiveSessions.remove(sessionId);
                                     }
+                                    invokeCallbackFinished();
                                 });
+                    } else {
+                        invokeCallbackFinished();
                     }
                 }
 
@@ -323,8 +335,17 @@
                                         e.rethrowFromSystemServer();
                                     }
                                 }
+                                invokeCallbackFinished();
                             });
                 }
+
+                private void invokeCallbackFinished() {
+                    try {
+                        mServiceToken.onCallbackFinished();
+                    } catch (RemoteException e) {
+                        e.rethrowFromSystemServer();
+                    }
+                }
             };
 
     /** Binder returned from system service, non-null while registered. */
@@ -358,6 +379,7 @@
                     service.registerEndpoint(
                             mPendingHubEndpointInfo,
                             mServiceCallback,
+                            mPendingHubEndpointInfo.getName(),
                             mPendingHubEndpointInfo.getTag());
             mAssignedHubEndpointInfo = serviceToken.getAssignedHubEndpointInfo();
             mServiceToken = serviceToken;
@@ -514,6 +536,7 @@
         /** Create a builder for {@link HubEndpoint} */
         public Builder(@NonNull Context context) {
             mPackageName = context.getPackageName();
+            mTag = context.getAttributionTag();
             mVersion = (int) context.getApplicationInfo().longVersionCode;
             mMainExecutor = context.getMainExecutor();
         }
@@ -532,6 +555,7 @@
         /**
          * Set a tag string. The tag can be used to further identify the creator of the endpoint.
          * Endpoints created by the same package share the same name but should have different tags.
+         * The default value of the tag is retrieved from {@link Context#getAttributionTag()}.
          */
         @NonNull
         public Builder setTag(@NonNull String tag) {
diff --git a/core/java/android/hardware/contexthub/IContextHubEndpoint.aidl b/core/java/android/hardware/contexthub/IContextHubEndpoint.aidl
index 44f80c8..eb1255c 100644
--- a/core/java/android/hardware/contexthub/IContextHubEndpoint.aidl
+++ b/core/java/android/hardware/contexthub/IContextHubEndpoint.aidl
@@ -94,4 +94,10 @@
      */
     @EnforcePermission("ACCESS_CONTEXT_HUB")
     void sendMessageDeliveryStatus(int sessionId, int messageSeqNumber, byte errorCode);
+
+    /**
+     * Invoked when a callback from IContextHubEndpointCallback finishes.
+     */
+    @EnforcePermission("ACCESS_CONTEXT_HUB")
+    void onCallbackFinished();
 }
diff --git a/core/java/android/hardware/display/DisplayTopology.java b/core/java/android/hardware/display/DisplayTopology.java
index ba5dfc0..1d2f133 100644
--- a/core/java/android/hardware/display/DisplayTopology.java
+++ b/core/java/android/hardware/display/DisplayTopology.java
@@ -27,10 +27,12 @@
 import android.graphics.RectF;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.util.DisplayMetrics;
 import android.util.IndentingPrintWriter;
 import android.util.MathUtils;
 import android.util.Pair;
 import android.util.Slog;
+import android.util.SparseArray;
 import android.view.Display;
 
 import androidx.annotation.NonNull;
@@ -75,6 +77,24 @@
             };
 
     /**
+     * @param px The value in logical pixels
+     * @param dpi The logical density of the display
+     * @return The value in density-independent pixels
+     */
+    public static float pxToDp(float px, int dpi) {
+        return px * DisplayMetrics.DENSITY_DEFAULT / dpi;
+    }
+
+    /**
+     * @param dp The value in density-independent pixels
+     * @param dpi The logical density of the display
+     * @return The value in logical pixels
+     */
+    public static float dpToPx(float dp, int dpi) {
+        return dp * dpi / DisplayMetrics.DENSITY_DEFAULT;
+    }
+
+    /**
      * The topology tree
      */
     @Nullable
@@ -226,10 +246,9 @@
         // The optimal pair is the pair which has the smallest deviation. The deviation consists of
         // an x-axis component and a y-axis component, called xDeviation and yDeviation.
         //
-        // The deviations are like distances but a little different. They are calculated in two
-        // steps. The first step calculates both axes in a similar way. The next step compares the
-        // two values and chooses which axis to attach along. Depending on which axis is chosen,
-        // the deviation for one axis is updated. See below for details.
+        // The deviations are like distances but a little different. When they are calculated, each
+        // dimension is treated differently, depending on which edges (left+right or top+bottom) are
+        // attached.
         while (!needsParent.isEmpty()) {
             double bestDist = Double.POSITIVE_INFINITY;
             TreeNode bestChild = null, bestParent = null;
@@ -243,33 +262,32 @@
                     float parentRight = parentPos.x + parent.getWidth();
                     float parentBottom = parentPos.y + parent.getHeight();
 
-                    // This is the smaller of the two ranges minus the amount of overlap shared
-                    // between them. The "amount of overlap" is negative if there is no overlap, but
-                    // this does not make a parenting ineligible, because we allow for attaching at
-                    // the corner and for floating point error. The overlap is more negative the
-                    // farther apart the closest corner pair is.
-                    //
-                    // For each axis, this calculates (SmallerRange - Overlap). If one range lies
-                    // completely in the other (or they are equal), the axis' deviation will be
-                    // zero.
-                    //
-                    // The "SmallerRange," which refers to smaller of the widths of the two rects,
-                    // or smaller of the heights of the two rects, is added to the deviation so that
-                    // a maximum overlap results in a deviation of zero.
-                    float xSmallerRange = Math.min(child.getWidth(), parent.getWidth());
-                    float ySmallerRange = Math.min(child.getHeight(), parent.getHeight());
-                    float xOverlap
-                            = Math.min(parentRight, childRight)
-                            - Math.max(parentPos.x, childPos.x);
-                    float yOverlap
-                            = Math.min(parentBottom, childBottom)
-                            - Math.max(parentPos.y, childPos.y);
-                    float xDeviation = xSmallerRange - xOverlap;
-                    float yDeviation = ySmallerRange - yOverlap;
+                    // The "amount of overlap" indicates how much of one display is within the other
+                    // (considering one axis only). It's zero if they only share an edge and
+                    // negative if they're away from each other.
+                    // A zero or negative overlap does not make a parenting ineligible, because we
+                    // allow for attaching at the corner and for floating point error.
+                    float xOverlap =
+                            Math.min(parentRight, childRight) - Math.max(parentPos.x, childPos.x);
+                    float yOverlap =
+                            Math.min(parentBottom, childBottom) - Math.max(parentPos.y, childPos.y);
+                    float xDeviation, yDeviation;
 
                     float offset;
                     int pos;
-                    if (xDeviation <= yDeviation) {
+                    if (Math.abs(xOverlap) > Math.abs(yOverlap)) {
+                        // Deviation in each dimension is a penalty in the potential parenting. To
+                        // get the X deviation, overlap is subtracted from the lesser width so that
+                        // a maximum overlap results in a deviation of zero.
+                        // Note that because xOverlap is *subtracted* from the lesser width, no
+                        // overlap in X becomes a *penalty* if we are attaching on the top+bottom
+                        // edges.
+                        //
+                        // The Y deviation is simply the distance from the clamping edges.
+                        //
+                        // Treatment of the X and Y deviations are swapped for
+                        // POSITION_LEFT/POSITION_RIGHT attachments in the "else" block below.
+                        xDeviation = Math.min(child.getWidth(), parent.getWidth()) - xOverlap;
                         if (childPos.y < parentPos.y) {
                             yDeviation = childBottom - parentPos.y;
                             pos = POSITION_TOP;
@@ -279,6 +297,7 @@
                         }
                         offset = childPos.x - parentPos.x;
                     } else {
+                        yDeviation = Math.min(child.getHeight(), parent.getHeight()) - yOverlap;
                         if (childPos.x < parentPos.x) {
                             xDeviation = childRight - parentPos.x;
                             pos = POSITION_LEFT;
@@ -474,6 +493,22 @@
         return new DisplayTopology(rootCopy, mPrimaryDisplayId);
     }
 
+    /**
+     * Assign absolute bounds to each display. The top-left corner of the root is at position
+     * (0, 0).
+     * @return Map from logical display ID to the display's absolute bounds
+     */
+    public SparseArray<RectF> getAbsoluteBounds() {
+        Map<TreeNode, RectF> bounds = new HashMap<>();
+        getInfo(bounds, /* depths= */ null, /* parents= */ null, mRoot, /* x= */ 0, /* y= */ 0,
+                /* depth= */ 0);
+        SparseArray<RectF> boundsById = new SparseArray<>();
+        for (Map.Entry<TreeNode, RectF> entry : bounds.entrySet()) {
+            boundsById.append(entry.getKey().mDisplayId, entry.getValue());
+        }
+        return boundsById;
+    }
+
     @Override
     public int describeContents() {
         return 0;
@@ -575,7 +610,7 @@
     }
 
     @Nullable
-    private static TreeNode findDisplay(int displayId, TreeNode startingNode) {
+    private static TreeNode findDisplay(int displayId, @Nullable TreeNode startingNode) {
         if (startingNode == null) {
             return null;
         }
@@ -592,8 +627,8 @@
     }
 
     /**
-     * Get information about the topology that will be used for the normalization algorithm.
-     * Assigns origins to each display to compute the bounds.
+     * Get information about the topology.
+     * Assigns positions to each display to compute the bounds. The root is at position (0, 0).
      * @param bounds The map where the bounds of each display will be put
      * @param depths The map where the depths of each display in the tree will be put
      * @param parents The map where the parent of each display will be put
@@ -602,12 +637,22 @@
      * @param y The starting y position
      * @param depth The starting depth
      */
-    private static void getInfo(Map<TreeNode, RectF> bounds, Map<TreeNode, Integer> depths,
-            Map<TreeNode, TreeNode> parents, TreeNode display, float x, float y, int depth) {
-        bounds.put(display, new RectF(x, y, x + display.mWidth, y + display.mHeight));
-        depths.put(display, depth);
+    private static void getInfo(@Nullable Map<TreeNode, RectF> bounds,
+            @Nullable Map<TreeNode, Integer> depths, @Nullable Map<TreeNode, TreeNode> parents,
+            @Nullable TreeNode display, float x, float y, int depth) {
+        if (display == null) {
+            return;
+        }
+        if (bounds != null) {
+            bounds.put(display, new RectF(x, y, x + display.mWidth, y + display.mHeight));
+        }
+        if (depths != null) {
+            depths.put(display, depth);
+        }
         for (TreeNode child : display.mChildren) {
-            parents.put(child, display);
+            if (parents != null) {
+                parents.put(child, display);
+            }
             if (child.mPosition == POSITION_LEFT) {
                 getInfo(bounds, depths, parents, child, x - child.mWidth, y + child.mOffset,
                         depth + 1);
@@ -634,8 +679,7 @@
     }
 
     /**
-     * Tests whether two brightness float values are within a small enough tolerance
-     * of each other.
+     * Tests whether two float values are within a small enough tolerance of each other.
      * @param a first float to compare
      * @param b second float to compare
      * @return whether the two values are within a small enough tolerance value
@@ -662,7 +706,7 @@
      * Ensure that the offsets of all displays within the given tree are within bounds.
      * @param display The starting node
      */
-    private void clampOffsets(TreeNode display) {
+    private void clampOffsets(@Nullable TreeNode display) {
         if (display == null) {
             return;
         }
diff --git a/core/java/android/hardware/input/InputGestureData.java b/core/java/android/hardware/input/InputGestureData.java
index f41550f..75c652c 100644
--- a/core/java/android/hardware/input/InputGestureData.java
+++ b/core/java/android/hardware/input/InputGestureData.java
@@ -48,27 +48,7 @@
 
     /** Returns the trigger information for this input gesture */
     public Trigger getTrigger() {
-        switch (mInputGestureData.trigger.getTag()) {
-            case AidlInputGestureData.Trigger.Tag.key: {
-                AidlInputGestureData.KeyTrigger trigger = mInputGestureData.trigger.getKey();
-                if (trigger == null) {
-                    throw new RuntimeException("InputGestureData is corrupted, null key trigger!");
-                }
-                return createKeyTrigger(trigger.keycode, trigger.modifierState);
-            }
-            case AidlInputGestureData.Trigger.Tag.touchpadGesture: {
-                AidlInputGestureData.TouchpadGestureTrigger trigger =
-                        mInputGestureData.trigger.getTouchpadGesture();
-                if (trigger == null) {
-                    throw new RuntimeException(
-                            "InputGestureData is corrupted, null touchpad trigger!");
-                }
-                return createTouchpadTrigger(trigger.gestureType);
-            }
-            default:
-                throw new RuntimeException("InputGestureData is corrupted, invalid trigger type!");
-
-        }
+        return createTriggerFromAidlTrigger(mInputGestureData.trigger);
     }
 
     /** Returns the action to perform for this input gesture */
@@ -147,18 +127,7 @@
                         "No app launch data for system action launch application");
             }
             AidlInputGestureData data = new AidlInputGestureData();
-            data.trigger = new AidlInputGestureData.Trigger();
-            if (mTrigger instanceof KeyTrigger keyTrigger) {
-                data.trigger.setKey(new AidlInputGestureData.KeyTrigger());
-                data.trigger.getKey().keycode = keyTrigger.getKeycode();
-                data.trigger.getKey().modifierState = keyTrigger.getModifierState();
-            } else if (mTrigger instanceof TouchpadTrigger touchpadTrigger) {
-                data.trigger.setTouchpadGesture(new AidlInputGestureData.TouchpadGestureTrigger());
-                data.trigger.getTouchpadGesture().gestureType =
-                        touchpadTrigger.getTouchpadGestureType();
-            } else {
-                throw new IllegalArgumentException("Invalid trigger type!");
-            }
+            data.trigger = mTrigger.getAidlTrigger();
             data.gestureType = mKeyGestureType;
             if (mAppLaunchData != null) {
                 if (mAppLaunchData instanceof AppLaunchData.CategoryData categoryData) {
@@ -198,6 +167,7 @@
     }
 
     public interface Trigger {
+        AidlInputGestureData.Trigger getAidlTrigger();
     }
 
     /** Creates a input gesture trigger based on a key press */
@@ -210,85 +180,128 @@
         return new TouchpadTrigger(touchpadGestureType);
     }
 
+    public static Trigger createTriggerFromAidlTrigger(AidlInputGestureData.Trigger aidlTrigger) {
+        switch (aidlTrigger.getTag()) {
+            case AidlInputGestureData.Trigger.Tag.key: {
+                AidlInputGestureData.KeyTrigger trigger = aidlTrigger.getKey();
+                if (trigger == null) {
+                    throw new RuntimeException("aidlTrigger is corrupted, null key trigger!");
+                }
+                return new KeyTrigger(trigger);
+            }
+            case AidlInputGestureData.Trigger.Tag.touchpadGesture: {
+                AidlInputGestureData.TouchpadGestureTrigger trigger =
+                        aidlTrigger.getTouchpadGesture();
+                if (trigger == null) {
+                    throw new RuntimeException(
+                            "aidlTrigger is corrupted, null touchpad trigger!");
+                }
+                return new TouchpadTrigger(trigger);
+            }
+            default:
+                throw new RuntimeException("aidlTrigger is corrupted, invalid trigger type!");
+
+        }
+    }
+
     /** Key based input gesture trigger */
     public static class KeyTrigger implements Trigger {
-        private static final int SHORTCUT_META_MASK =
-                KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON | KeyEvent.META_ALT_ON
-                        | KeyEvent.META_SHIFT_ON;
-        private final int mKeycode;
-        private final int mModifierState;
+
+        AidlInputGestureData.KeyTrigger mAidlKeyTrigger;
+
+        private KeyTrigger(@NonNull AidlInputGestureData.KeyTrigger aidlKeyTrigger) {
+            mAidlKeyTrigger = aidlKeyTrigger;
+        }
 
         private KeyTrigger(int keycode, int modifierState) {
             if (keycode <= KeyEvent.KEYCODE_UNKNOWN || keycode > KeyEvent.getMaxKeyCode()) {
                 throw new IllegalArgumentException("Invalid keycode = " + keycode);
             }
-            mKeycode = keycode;
-            mModifierState = modifierState;
+            mAidlKeyTrigger = new AidlInputGestureData.KeyTrigger();
+            mAidlKeyTrigger.keycode = keycode;
+            mAidlKeyTrigger.modifierState = modifierState;
         }
 
         public int getKeycode() {
-            return mKeycode;
+            return mAidlKeyTrigger.keycode;
         }
 
         public int getModifierState() {
-            return mModifierState;
+            return mAidlKeyTrigger.modifierState;
+        }
+
+        public AidlInputGestureData.Trigger getAidlTrigger() {
+            AidlInputGestureData.Trigger trigger = new AidlInputGestureData.Trigger();
+            trigger.setKey(mAidlKeyTrigger);
+            return trigger;
         }
 
         @Override
         public boolean equals(Object o) {
             if (this == o) return true;
             if (!(o instanceof KeyTrigger that)) return false;
-            return mKeycode == that.mKeycode && mModifierState == that.mModifierState;
+            return Objects.equals(mAidlKeyTrigger, that.mAidlKeyTrigger);
         }
 
         @Override
         public int hashCode() {
-            return Objects.hash(mKeycode, mModifierState);
+            return mAidlKeyTrigger.hashCode();
         }
 
         @Override
         public String toString() {
             return "KeyTrigger{" +
-                    "mKeycode=" + KeyEvent.keyCodeToString(mKeycode) +
-                    ", mModifierState=" + mModifierState +
+                    "mKeycode=" + KeyEvent.keyCodeToString(mAidlKeyTrigger.keycode) +
+                    ", mModifierState=" + mAidlKeyTrigger.modifierState +
                     '}';
         }
     }
 
     /** Touchpad based input gesture trigger */
     public static class TouchpadTrigger implements Trigger {
-        private final int mTouchpadGestureType;
+        AidlInputGestureData.TouchpadGestureTrigger mAidlTouchpadTrigger;
+
+        private TouchpadTrigger(
+                @NonNull AidlInputGestureData.TouchpadGestureTrigger aidlTouchpadTrigger) {
+            mAidlTouchpadTrigger = aidlTouchpadTrigger;
+        }
 
         private TouchpadTrigger(int touchpadGestureType) {
             if (touchpadGestureType != TOUCHPAD_GESTURE_TYPE_THREE_FINGER_TAP) {
                 throw new IllegalArgumentException(
                         "Invalid touchpadGestureType = " + touchpadGestureType);
             }
-            mTouchpadGestureType = touchpadGestureType;
+            mAidlTouchpadTrigger = new AidlInputGestureData.TouchpadGestureTrigger();
+            mAidlTouchpadTrigger.gestureType = touchpadGestureType;
         }
 
         public int getTouchpadGestureType() {
-            return mTouchpadGestureType;
+            return mAidlTouchpadTrigger.gestureType;
+        }
+
+        public AidlInputGestureData.Trigger getAidlTrigger() {
+            AidlInputGestureData.Trigger trigger = new AidlInputGestureData.Trigger();
+            trigger.setTouchpadGesture(mAidlTouchpadTrigger);
+            return trigger;
         }
 
         @Override
         public String toString() {
             return "TouchpadTrigger{" +
-                    "mTouchpadGestureType=" + mTouchpadGestureType +
+                    "mTouchpadGestureType=" + mAidlTouchpadTrigger.gestureType +
                     '}';
         }
 
         @Override
         public boolean equals(Object o) {
             if (this == o) return true;
-            if (o == null || getClass() != o.getClass()) return false;
-            TouchpadTrigger that = (TouchpadTrigger) o;
-            return mTouchpadGestureType == that.mTouchpadGestureType;
+            if (!(o instanceof TouchpadTrigger that)) return false;
+            return Objects.equals(mAidlTouchpadTrigger, that.mAidlTouchpadTrigger);
         }
 
         @Override
         public int hashCode() {
-            return Objects.hashCode(mTouchpadGestureType);
+            return mAidlTouchpadTrigger.hashCode();
         }
     }
 
diff --git a/core/java/android/hardware/input/InputSettings.java b/core/java/android/hardware/input/InputSettings.java
index 34c88e9..b380e25 100644
--- a/core/java/android/hardware/input/InputSettings.java
+++ b/core/java/android/hardware/input/InputSettings.java
@@ -28,6 +28,7 @@
 import static com.android.hardware.input.Flags.mouseScrollingAcceleration;
 import static com.android.hardware.input.Flags.mouseReverseVerticalScrolling;
 import static com.android.hardware.input.Flags.mouseSwapPrimaryButton;
+import static com.android.hardware.input.Flags.pointerAcceleration;
 import static com.android.hardware.input.Flags.touchpadSystemGestureDisable;
 import static com.android.hardware.input.Flags.touchpadThreeFingerTapShortcut;
 import static com.android.hardware.input.Flags.touchpadVisualizer;
@@ -77,6 +78,24 @@
     public static final int DEFAULT_POINTER_SPEED = 0;
 
     /**
+     * Pointer Speed: The minimum (slowest) mouse scrolling speed (-7).
+     * @hide
+     */
+    public static final int MIN_MOUSE_SCROLLING_SPEED = -7;
+
+    /**
+     * Pointer Speed: The maximum (fastest) mouse scrolling speed (7).
+     * @hide
+     */
+    public static final int MAX_MOUSE_SCROLLING_SPEED = 7;
+
+    /**
+     * Pointer Speed: The default mouse scrolling speed (0).
+     * @hide
+     */
+    public static final int DEFAULT_MOUSE_SCROLLING_SPEED = 0;
+
+    /**
      * Bounce Keys Threshold: The default value of the threshold (500 ms).
      *
      * @hide
@@ -418,6 +437,15 @@
     }
 
     /**
+     * Returns true if the feature flag for the pointer acceleration toggle is
+     * enabled.
+     * @hide
+     */
+    public static boolean isPointerAccelerationFeatureFlagEnabled() {
+        return pointerAcceleration();
+    }
+
+    /**
      * Returns true if the touchpad visualizer is allowed to appear.
      *
      * @param context The application context.
@@ -640,6 +668,54 @@
     }
 
     /**
+     * Gets the mouse scrolling speed.
+     *
+     * The returned value only applies when mouse scrolling acceleration is not enabled.
+     *
+     * @param context The application context.
+     * @return The mouse scrolling speed as a value between {@link #MIN_MOUSE_SCROLLING_SPEED} and
+     *         {@link #MAX_MOUSE_SCROLLING_SPEED}, or the default value
+     *         {@link #DEFAULT_MOUSE_SCROLLING_SPEED}.
+     *
+     * @hide
+     */
+    public static int getMouseScrollingSpeed(@NonNull Context context) {
+        if (!isMouseScrollingAccelerationFeatureFlagEnabled()) {
+            return 0;
+        }
+
+        return Settings.System.getIntForUser(context.getContentResolver(),
+                Settings.System.MOUSE_SCROLLING_SPEED, DEFAULT_MOUSE_SCROLLING_SPEED,
+                UserHandle.USER_CURRENT);
+    }
+
+    /**
+     * Sets the mouse scrolling speed, and saves it in the settings.
+     *
+     * The new speed will only apply when mouse scrolling acceleration is not enabled.
+     *
+     * @param context The application context.
+     * @param speed The mouse scrolling speed as a value between {@link #MIN_MOUSE_SCROLLING_SPEED}
+     *              and {@link #MAX_MOUSE_SCROLLING_SPEED}, or the default value
+     *              {@link #DEFAULT_MOUSE_SCROLLING_SPEED}.
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.WRITE_SETTINGS)
+    public static void setMouseScrollingSpeed(@NonNull Context context, int speed) {
+        if (isMouseScrollingAccelerationEnabled(context)) {
+            return;
+        }
+
+        if (speed < MIN_MOUSE_SCROLLING_SPEED || speed > MAX_MOUSE_SCROLLING_SPEED) {
+            throw new IllegalArgumentException("speed out of range");
+        }
+
+        Settings.System.putIntForUser(context.getContentResolver(),
+                Settings.System.MOUSE_SCROLLING_SPEED, speed, UserHandle.USER_CURRENT);
+    }
+
+    /**
      * Whether mouse vertical scrolling is reversed. This applies only to connected mice.
      *
      * @param context The application context.
@@ -720,6 +796,47 @@
     }
 
     /**
+     * Whether cursor acceleration is enabled or not for connected mice.
+     *
+     * @param context The application context.
+     *
+     * @hide
+     */
+    public static boolean isMousePointerAccelerationEnabled(@NonNull Context context) {
+        if (!isPointerAccelerationFeatureFlagEnabled()) {
+            return false;
+        }
+
+        return Settings.System.getIntForUser(context.getContentResolver(),
+                Settings.System.MOUSE_POINTER_ACCELERATION_ENABLED, 1, UserHandle.USER_CURRENT)
+                == 1;
+    }
+
+   /**
+    * Sets whether mouse acceleration is enabled.
+    *
+    * When enabled, the mouse cursor moves farther when it is moved faster.
+    * When disabled, the mouse cursor speed becomes directly proportional to
+    * the speed at which the mouse is moved.
+    *
+    * @param context The application context.
+    * @param enabled Will enable mouse acceleration if true, disable it if
+    *                false.
+    * @hide
+    */
+    @RequiresPermission(Manifest.permission.WRITE_SETTINGS)
+    public static void setMouseAccelerationEnabled(@NonNull Context context,
+            boolean enabled) {
+        if (!isPointerAccelerationFeatureFlagEnabled()) {
+            return;
+        }
+        Settings.System.putIntForUser(context.getContentResolver(),
+                Settings.System.MOUSE_POINTER_ACCELERATION_ENABLED, enabled ? 1 : 0,
+                UserHandle.USER_CURRENT);
+    }
+
+
+    /**
      * Whether Accessibility bounce keys feature is enabled.
      *
      * <p>
diff --git a/core/java/android/hardware/input/KeyGestureEvent.java b/core/java/android/hardware/input/KeyGestureEvent.java
index 66d073f..cb1e016 100644
--- a/core/java/android/hardware/input/KeyGestureEvent.java
+++ b/core/java/android/hardware/input/KeyGestureEvent.java
@@ -129,6 +129,7 @@
     public static final int KEY_GESTURE_TYPE_MAGNIFICATION_PAN_RIGHT = 79;
     public static final int KEY_GESTURE_TYPE_MAGNIFICATION_PAN_UP = 80;
     public static final int KEY_GESTURE_TYPE_MAGNIFICATION_PAN_DOWN = 81;
+    public static final int KEY_GESTURE_TYPE_TOGGLE_VOICE_ACCESS = 82;
 
     public static final int FLAG_CANCELLED = 1;
 
@@ -225,6 +226,7 @@
             KEY_GESTURE_TYPE_MAGNIFICATION_PAN_RIGHT,
             KEY_GESTURE_TYPE_MAGNIFICATION_PAN_UP,
             KEY_GESTURE_TYPE_MAGNIFICATION_PAN_DOWN,
+            KEY_GESTURE_TYPE_TOGGLE_VOICE_ACCESS,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface KeyGestureType {
@@ -807,6 +809,8 @@
                 return "KEY_GESTURE_TYPE_MAGNIFICATION_PAN_UP";
             case KEY_GESTURE_TYPE_MAGNIFICATION_PAN_DOWN:
                 return "KEY_GESTURE_TYPE_MAGNIFICATION_PAN_DOWN";
+            case KEY_GESTURE_TYPE_TOGGLE_VOICE_ACCESS:
+                return "KEY_GESTURE_TYPE_TOGGLE_VOICE_ACCESS";
             default:
                 return Integer.toHexString(value);
         }
diff --git a/core/java/android/hardware/input/input_framework.aconfig b/core/java/android/hardware/input/input_framework.aconfig
index c4d11cd..7887c15 100644
--- a/core/java/android/hardware/input/input_framework.aconfig
+++ b/core/java/android/hardware/input/input_framework.aconfig
@@ -67,17 +67,17 @@
 }
 
 flag {
-  name: "modifier_shortcut_dump"
-  namespace: "input"
-  description: "Dump keyboard shortcuts in dumpsys window"
-  bug: "351963350"
+    name: "modifier_shortcut_dump"
+    namespace: "input"
+    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"
+    name: "modifier_shortcut_manager_refactor"
+    namespace: "input"
+    description: "Refactor ModifierShortcutManager internal representation of shortcuts."
+    bug: "358603902"
 }
 
 flag {
@@ -114,31 +114,31 @@
 }
 
 flag {
-  name: "keyboard_repeat_keys"
-  namespace: "input_native"
-  description: "Allow configurable timeout before key repeat and repeat delay rate for key repeats"
-  bug: "336585002"
+    name: "keyboard_repeat_keys"
+    namespace: "input_native"
+    description: "Allow configurable timeout before key repeat and repeat delay rate for key repeats"
+    bug: "336585002"
 }
 
 flag {
-  name: "mouse_reverse_vertical_scrolling"
-  namespace: "input"
-  description: "Controls whether external mouse vertical scrolling can be reversed"
-  bug: "352598211"
+    name: "mouse_reverse_vertical_scrolling"
+    namespace: "input"
+    description: "Controls whether external mouse vertical scrolling can be reversed"
+    bug: "352598211"
 }
 
 flag {
-  name: "mouse_swap_primary_button"
-  namespace: "input"
-  description: "Controls whether the connected mice's primary buttons, left and right, can be swapped."
-  bug: "352598211"
+    name: "mouse_swap_primary_button"
+    namespace: "input"
+    description: "Controls whether the connected mice's primary buttons, left and right, can be swapped."
+    bug: "352598211"
 }
 
 flag {
-  name: "keyboard_a11y_shortcut_control"
-  namespace: "input"
-  description: "Adds shortcuts to toggle and control a11y keyboard features"
-  bug: "373458181"
+    name: "keyboard_a11y_shortcut_control"
+    namespace: "input"
+    description: "Adds shortcuts to toggle and control a11y keyboard features"
+    bug: "373458181"
 }
 
 flag {
@@ -164,32 +164,39 @@
 }
 
 flag {
-  name: "override_power_key_behavior_in_focused_window"
-  namespace: "wallet_integration"
-  description: "Allows privileged focused windows to override the power key double tap behavior."
-  bug: "357144512"
+    name: "override_power_key_behavior_in_focused_window"
+    namespace: "wallet_integration"
+    description: "Allows privileged focused windows to override the power key double tap behavior."
+    bug: "357144512"
 }
 
 flag {
-  name: "touchpad_three_finger_tap_shortcut"
-  namespace: "input"
-  description: "Turns three-finger touchpad taps into a customizable shortcut."
-  bug: "365063048"
+    name: "touchpad_three_finger_tap_shortcut"
+    namespace: "input"
+    description: "Turns three-finger touchpad taps into a customizable shortcut."
+    bug: "365063048"
 }
 
 flag {
-  name: "enable_talkback_and_magnifier_key_gestures"
-  namespace: "input"
-  description: "Adds key gestures for talkback and magnifier"
-  bug: "375277034"
+    name: "enable_talkback_and_magnifier_key_gestures"
+    namespace: "input"
+    description: "Adds key gestures for talkback and magnifier"
+    bug: "375277034"
 }
 
 flag {
-  name: "can_window_override_power_gesture_api"
-  namespace: "wallet_integration"
-  description: "Adds new API in WindowManager class to check if the window can override the power key double tap behavior."
-  bug: "378736024"
-  }
+    name: "enable_voice_access_key_gestures"
+    namespace: "input"
+    description: "Adds key gestures for voice access"
+    bug: "383734125"
+}
+
+flag {
+    name: "can_window_override_power_gesture_api"
+    namespace: "wallet_integration"
+    description: "Adds new API in WindowManager class to check if the window can override the power key double tap behavior."
+    bug: "378736024"
+}
 
 flag {
     name: "pointer_acceleration"
@@ -206,8 +213,8 @@
 }
 
 flag {
-  name: "remove_fallback_modifiers"
-  namespace: "input"
-  description: "Removes modifiers from the original key event that activated the fallback, ensuring that only the intended fallback event is sent."
-  bug: "382545048"
+    name: "remove_fallback_modifiers"
+    namespace: "input"
+    description: "Removes modifiers from the original key event that activated the fallback, ensuring that only the intended fallback event is sent."
+    bug: "382545048"
 }
diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java
index 0cd3209..9181bd0 100644
--- a/core/java/android/hardware/location/ContextHubManager.java
+++ b/core/java/android/hardware/location/ContextHubManager.java
@@ -697,6 +697,7 @@
      *
      * @param endpointId Statically generated ID for an endpoint.
      * @return A list of {@link HubDiscoveryInfo} objects that represents the result of discovery.
+     * @throws UnsupportedOperationException If the operation is not supported.
      */
     @FlaggedApi(Flags.FLAG_OFFLOAD_API)
     @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@@ -733,6 +734,7 @@
      *     cannot be null or empty.
      * @return A list of {@link HubDiscoveryInfo} objects that represents the result of discovery.
      * @throws IllegalArgumentException if the serviceDescriptor is empty/null.
+     * @throws UnsupportedOperationException If the operation is not supported.
      */
     @FlaggedApi(Flags.FLAG_OFFLOAD_API)
     @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
diff --git a/core/java/android/hardware/location/IContextHubService.aidl b/core/java/android/hardware/location/IContextHubService.aidl
index 2a47237..d5b3fa2 100644
--- a/core/java/android/hardware/location/IContextHubService.aidl
+++ b/core/java/android/hardware/location/IContextHubService.aidl
@@ -137,7 +137,7 @@
 
     // Register an endpoint with the context hub
     @EnforcePermission("ACCESS_CONTEXT_HUB")
-    IContextHubEndpoint registerEndpoint(in HubEndpointInfo pendingEndpointInfo, in IContextHubEndpointCallback callback, String packageName);
+    IContextHubEndpoint registerEndpoint(in HubEndpointInfo pendingEndpointInfo, in IContextHubEndpointCallback callback, String packageName, String attributionTag);
 
     // Register an endpoint discovery callback (id)
     @EnforcePermission("ACCESS_CONTEXT_HUB")
diff --git a/core/java/android/inputmethodservice/InlineSuggestionSession.java b/core/java/android/inputmethodservice/InlineSuggestionSession.java
index 1cc64f9..4c90750 100644
--- a/core/java/android/inputmethodservice/InlineSuggestionSession.java
+++ b/core/java/android/inputmethodservice/InlineSuggestionSession.java
@@ -29,6 +29,7 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Log;
+import android.view.autofill.AutofillFeatureFlags;
 import android.view.autofill.AutofillId;
 import android.view.inputmethod.InlineSuggestionsRequest;
 import android.view.inputmethod.InlineSuggestionsResponse;
@@ -81,6 +82,7 @@
     @Nullable
     private Boolean mPreviousResponseIsEmpty;
 
+    private boolean mAlwaysNotifyAutofill = false;
 
     /**
      * Indicates whether {@link #makeInlineSuggestionRequestUncheck()} has been called or not,
@@ -105,6 +107,7 @@
         mResponseConsumer = responseConsumer;
         mInlineSuggestionSessionController = inlineSuggestionSessionController;
         mMainThreadHandler = mainThreadHandler;
+        mAlwaysNotifyAutofill = AutofillFeatureFlags.isImproveFillDialogEnabled();
     }
 
     @MainThread
@@ -176,6 +179,9 @@
         try {
             final InlineSuggestionsRequest request = mRequestSupplier.apply(
                     mRequestInfo.getUiExtras());
+            if (mAlwaysNotifyAutofill) {
+                mResponseCallback = new InlineSuggestionsResponseCallbackImpl(this);
+            }
             if (request == null) {
                 if (DEBUG) {
                     Log.d(TAG, "onCreateInlineSuggestionsRequest() returned null request");
@@ -184,7 +190,9 @@
             } else {
                 request.setHostInputToken(mHostInputTokenSupplier.get());
                 request.filterContentTypes();
-                mResponseCallback = new InlineSuggestionsResponseCallbackImpl(this);
+                if (!mAlwaysNotifyAutofill) {
+                    mResponseCallback = new InlineSuggestionsResponseCallbackImpl(this);
+                }
                 mCallback.onInlineSuggestionsRequest(request, mResponseCallback);
             }
         } catch (RemoteException e) {
diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java
index ecd90e4..1041041 100644
--- a/core/java/android/os/BaseBundle.java
+++ b/core/java/android/os/BaseBundle.java
@@ -386,6 +386,15 @@
     }
 
     /**
+     * return true if the value corresponding to this key is still parceled.
+     * @hide
+     */
+    public boolean isValueParceled(String key) {
+        if (mMap == null) return true;
+        int i = mMap.indexOfKey(key);
+        return (mMap.valueAt(i) instanceof BiFunction<?, ?, ?>);
+    }
+    /**
      * Returns the value for a certain position in the array map for expected return type {@code
      * clazz} (or pass {@code null} for no type check).
      *
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index c41e626..71d79bb 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -69,16 +69,19 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.text.DecimalFormat;
+import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.Date;
 import java.util.Formatter;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.TimeZone;
 
 /**
  * A class providing access to battery usage statistics, including information on
@@ -1868,6 +1871,11 @@
         @UnsupportedAppUsage
         public long time;
 
+        // Wall clock time of the event, GMT. Unlike `time`, this timestamp is affected
+        // by changes in the clock setting.  When the wall clock is adjusted, BatteryHistory
+        // records an event of type `CMD_CURRENT_TIME` or `CMD_RESET`.
+        public long currentTime;
+
         @UnsupportedAppUsage
         public static final byte CMD_UPDATE = 0;        // These can be written as deltas
         public static final byte CMD_NULL = -1;
@@ -2108,9 +2116,6 @@
         public int eventCode;
         public HistoryTag eventTag;
 
-        // Only set for CMD_CURRENT_TIME or CMD_RESET, as per System.currentTimeMillis().
-        public long currentTime;
-
         // Meta-data when reading.
         public int numReadInts;
 
@@ -6890,7 +6895,9 @@
                                 || wakelockTag.poolIdx == HistoryTag.HISTORY_TAG_POOL_OVERFLOW) {
                             UserHandle.formatUid(sb, wakelockTag.uid);
                             sb.append(":\"");
-                            sb.append(wakelockTag.string.replace("\"", "\"\""));
+                            if (wakelockTag.string != null) {
+                                sb.append(wakelockTag.string.replace("\"", "\"\""));
+                            }
                             sb.append("\"");
                         } else {
                             sb.append(wakelockTag.poolIdx);
@@ -6926,6 +6933,23 @@
     }
 
     public static class HistoryPrinter {
+        private static final int FORMAT_LEGACY = 1;
+
+        // This constant MUST be incremented whenever the history dump format changes.
+        private static final int FORMAT_VERSION = 2;
+
+        private final SimpleDateFormat mHistoryItemTimestampFormat =
+                new SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US);
+        private final SimpleDateFormat mCurrentTimeEventTimeFormat =
+                new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.US);
+
+        // This API is error prone, but we are making an exception here to avoid excessive
+        // object allocations.
+        @SuppressWarnings("JavaUtilDate")
+        private final Date mDate = new Date();
+
+        private final int mFormatVersion;
+
         int oldState = 0;
         int oldState2 = 0;
         int oldLevel = -1;
@@ -6939,6 +6963,22 @@
         double oldWifiRailChargeMah = -1;
         long lastTime = -1;
 
+        public HistoryPrinter() {
+            this(TimeZone.getDefault());
+        }
+
+        public HistoryPrinter(TimeZone timeZone) {
+            this(com.android.server.power.optimization.Flags
+                    .extendedBatteryHistoryContinuousCollectionEnabled()
+                    ? FORMAT_VERSION : FORMAT_LEGACY, timeZone);
+        }
+
+        private HistoryPrinter(int formatVersion, TimeZone timeZone) {
+            mFormatVersion = formatVersion;
+            mHistoryItemTimestampFormat.getCalendar().setTimeZone(timeZone);
+            mCurrentTimeEventTimeFormat.getCalendar().setTimeZone(timeZone);
+        }
+
         void reset() {
             oldState = oldState2 = 0;
             oldLevel = -1;
@@ -6966,16 +7006,22 @@
             }
         }
 
+        @SuppressWarnings("JavaUtilDate")
         private String printNextItem(HistoryItem rec, long baseTime, boolean checkin,
                 boolean verbose) {
             StringBuilder item = new StringBuilder();
             if (!checkin) {
                 item.append("  ");
-                TimeUtils.formatDuration(
-                        rec.time - baseTime, item, TimeUtils.HUNDRED_DAY_FIELD_LEN);
-                item.append(" (");
-                item.append(rec.numReadInts);
-                item.append(") ");
+                if (mFormatVersion == FORMAT_LEGACY) {
+                    TimeUtils.formatDuration(
+                            rec.time - baseTime, item, TimeUtils.HUNDRED_DAY_FIELD_LEN);
+                    item.append(" (");
+                    item.append(rec.numReadInts);
+                    item.append(") ");
+                } else {
+                    mDate.setTime(rec.currentTime);
+                    item.append(mHistoryItemTimestampFormat.format(mDate)).append(' ');
+                }
             } else {
                 item.append(BATTERY_STATS_CHECKIN_VERSION); item.append(',');
                 item.append(HISTORY_DATA); item.append(',');
@@ -7007,8 +7053,8 @@
                     item.append("\n");
                 } else {
                     item.append(" ");
-                    item.append(DateFormat.format("yyyy-MM-dd-HH-mm-ss",
-                            rec.currentTime).toString());
+                    mDate.setTime(rec.currentTime);
+                    item.append(mCurrentTimeEventTimeFormat.format(mDate));
                     item.append("\n");
                 }
             } else if (rec.cmd == HistoryItem.CMD_SHUTDOWN) {
@@ -7529,11 +7575,31 @@
     public static final int DUMP_DEVICE_WIFI_ONLY = 1<<6;
 
     private void dumpHistory(PrintWriter pw, int flags, long histStart, boolean checkin) {
+        final HistoryPrinter hprinter = new HistoryPrinter();
         synchronized (this) {
-            dumpHistoryTagPoolLocked(pw, checkin);
+            if (!checkin) {
+                final long historyTotalSize = getHistoryTotalSize();
+                final long historyUsedSize = getHistoryUsedSize();
+                pw.print("Battery History");
+                if (hprinter.mFormatVersion != HistoryPrinter.FORMAT_LEGACY) {
+                    pw.print(" [Format: " + hprinter.mFormatVersion + "]");
+                }
+                pw.print(" (");
+                pw.print((100 * historyUsedSize) / historyTotalSize);
+                pw.print("% used, ");
+                printSizeValue(pw, historyUsedSize);
+                pw.print(" used of ");
+                printSizeValue(pw, historyTotalSize);
+                pw.print(", ");
+                pw.print(getHistoryStringPoolSize());
+                pw.print(" strings using ");
+                printSizeValue(pw, getHistoryStringPoolBytes());
+                pw.println("):");
+            } else {
+                dumpHistoryTagPoolLocked(pw, checkin);
+            }
         }
 
-        final HistoryPrinter hprinter = new HistoryPrinter();
         long lastTime = -1;
         long baseTime = -1;
         boolean printed = false;
@@ -7645,20 +7711,6 @@
                 pw.print("\"");
                 pw.println();
             }
-        } else {
-            final long historyTotalSize = getHistoryTotalSize();
-            final long historyUsedSize = getHistoryUsedSize();
-            pw.print("Battery History (");
-            pw.print((100 * historyUsedSize) / historyTotalSize);
-            pw.print("% used, ");
-            printSizeValue(pw, historyUsedSize);
-            pw.print(" used of ");
-            printSizeValue(pw, historyTotalSize);
-            pw.print(", ");
-            pw.print(getHistoryStringPoolSize());
-            pw.print(" strings using ");
-            printSizeValue(pw, getHistoryStringPoolBytes());
-            pw.println("):");
         }
     }
 
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index ed75491..ee62dea 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -313,7 +313,10 @@
      * If the current thread is not currently executing an incoming transaction,
      * then its own PID is returned.
      *
-     * Warning: oneway transactions do not receive PID. Even if you expect
+     * Warning do not use this as a security identifier! PID is unreliable
+     * as it may be re-used. This should mostly be used for debugging.
+     *
+     * oneway transactions do not receive PID. Even if you expect
      * a transaction to be synchronous, a misbehaving client could send it
      * as a asynchronous call and result in a 0 PID here. Additionally, if
      * there is a race and the calling process dies, the PID may still be
diff --git a/core/java/android/os/CombinedMessageQueue/MessageQueue.java b/core/java/android/os/CombinedMessageQueue/MessageQueue.java
index 230fa3f..d9969d8 100644
--- a/core/java/android/os/CombinedMessageQueue/MessageQueue.java
+++ b/core/java/android/os/CombinedMessageQueue/MessageQueue.java
@@ -40,6 +40,7 @@
 import com.android.internal.ravenwood.RavenwoodEnvironment;
 
 import dalvik.annotation.optimization.NeverCompile;
+import dalvik.annotation.optimization.NeverInline;
 
 import java.io.FileDescriptor;
 import java.lang.annotation.Retention;
@@ -237,6 +238,46 @@
     }
     private final MatchDeliverableMessages mMatchDeliverableMessages =
             new MatchDeliverableMessages();
+
+    @NeverInline
+    private boolean isIdleConcurrent() {
+        final long now = SystemClock.uptimeMillis();
+
+        if (stackHasMessages(null, 0, null, null, now, mMatchDeliverableMessages, false)) {
+            return false;
+        }
+
+        MessageNode msgNode = null;
+        MessageNode asyncMsgNode = null;
+
+        if (!mPriorityQueue.isEmpty()) {
+            try {
+                msgNode = mPriorityQueue.first();
+            } catch (NoSuchElementException e) { }
+        }
+
+        if (!mAsyncPriorityQueue.isEmpty()) {
+            try {
+                asyncMsgNode = mAsyncPriorityQueue.first();
+            } catch (NoSuchElementException e) { }
+        }
+
+        if ((msgNode != null && msgNode.getWhen() <= now)
+                || (asyncMsgNode != null && asyncMsgNode.getWhen() <= now)) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @NeverInline
+    private boolean isIdleLegacy() {
+        synchronized (this) {
+            final long now = SystemClock.uptimeMillis();
+            return mMessages == null || now < mMessages.when;
+        }
+    }
+
     /**
      * Returns true if the looper has no pending messages which are due to be processed.
      *
@@ -246,38 +287,23 @@
      */
     public boolean isIdle() {
         if (mUseConcurrent) {
-            final long now = SystemClock.uptimeMillis();
-
-            if (stackHasMessages(null, 0, null, null, now, mMatchDeliverableMessages, false)) {
-                return false;
-            }
-
-            MessageNode msgNode = null;
-            MessageNode asyncMsgNode = null;
-
-            if (!mPriorityQueue.isEmpty()) {
-                try {
-                    msgNode = mPriorityQueue.first();
-                } catch (NoSuchElementException e) { }
-            }
-
-            if (!mAsyncPriorityQueue.isEmpty()) {
-                try {
-                    asyncMsgNode = mAsyncPriorityQueue.first();
-                } catch (NoSuchElementException e) { }
-            }
-
-            if ((msgNode != null && msgNode.getWhen() <= now)
-                    || (asyncMsgNode != null && asyncMsgNode.getWhen() <= now)) {
-                return false;
-            }
-
-            return true;
+            return isIdleConcurrent();
         } else {
-            synchronized (this) {
-                final long now = SystemClock.uptimeMillis();
-                return mMessages == null || now < mMessages.when;
-            }
+            return isIdleLegacy();
+        }
+    }
+
+    @NeverInline
+    private void addIdleHandlerConcurrent(@NonNull IdleHandler handler) {
+        synchronized (mIdleHandlersLock) {
+            mIdleHandlers.add(handler);
+        }
+    }
+
+    @NeverInline
+    private void addIdleHandlerLegacy(@NonNull IdleHandler handler) {
+        synchronized (this) {
+            mIdleHandlers.add(handler);
         }
     }
 
@@ -296,13 +322,23 @@
             throw new NullPointerException("Can't add a null IdleHandler");
         }
         if (mUseConcurrent) {
-            synchronized (mIdleHandlersLock) {
-                mIdleHandlers.add(handler);
-            }
+            addIdleHandlerConcurrent(handler);
         } else {
-            synchronized (this) {
-                mIdleHandlers.add(handler);
-            }
+            addIdleHandlerLegacy(handler);
+        }
+    }
+
+    @NeverInline
+    private void removeIdleHandlerConcurrent(@NonNull IdleHandler handler) {
+        synchronized (mIdleHandlersLock) {
+            mIdleHandlers.remove(handler);
+        }
+    }
+
+    @NeverInline
+    private void removeIdleHandlerLegacy(@NonNull IdleHandler handler) {
+        synchronized (this) {
+            mIdleHandlers.remove(handler);
         }
     }
 
@@ -317,13 +353,23 @@
      */
     public void removeIdleHandler(@NonNull IdleHandler handler) {
         if (mUseConcurrent) {
-            synchronized (mIdleHandlersLock) {
-                mIdleHandlers.remove(handler);
-            }
+            removeIdleHandlerConcurrent(handler);
         } else {
-            synchronized (this) {
-                mIdleHandlers.remove(handler);
-            }
+            removeIdleHandlerLegacy(handler);
+        }
+    }
+
+    @NeverInline
+    private boolean isPollingConcurrent() {
+        // If the loop is quitting then it must not be idling.
+        // We can assume mPtr != 0 when sQuitting is false.
+        return !((boolean) sQuitting.getVolatile(this)) && nativeIsPolling(mPtr);
+    }
+
+    @NeverInline
+    private boolean isPollingLegacy() {
+        synchronized (this) {
+            return isPollingLocked();
         }
     }
 
@@ -340,13 +386,9 @@
      */
     public boolean isPolling() {
         if (mUseConcurrent) {
-            // If the loop is quitting then it must not be idling.
-            // We can assume mPtr != 0 when sQuitting is false.
-            return !((boolean) sQuitting.getVolatile(this)) && nativeIsPolling(mPtr);
+            return isPollingConcurrent();
         } else {
-            synchronized (this) {
-                return isPollingLocked();
-            }
+            return isPollingLegacy();
         }
     }
 
@@ -355,6 +397,23 @@
         // We can assume mPtr != 0 when mQuitting is false.
         return !mQuitting && nativeIsPolling(mPtr);
     }
+    @NeverInline
+    private void addOnFileDescriptorEventListenerConcurrent(@NonNull FileDescriptor fd,
+            @OnFileDescriptorEventListener.Events int events,
+            @NonNull OnFileDescriptorEventListener listener) {
+        synchronized (mFileDescriptorRecordsLock) {
+            updateOnFileDescriptorEventListenerLocked(fd, events, listener);
+        }
+    }
+
+    @NeverInline
+    private void addOnFileDescriptorEventListenerLegacy(@NonNull FileDescriptor fd,
+            @OnFileDescriptorEventListener.Events int events,
+            @NonNull OnFileDescriptorEventListener listener) {
+        synchronized (this) {
+            updateOnFileDescriptorEventListenerLocked(fd, events, listener);
+        }
+    }
 
     /**
      * Adds a file descriptor listener to receive notification when file descriptor
@@ -391,13 +450,23 @@
         }
 
         if (mUseConcurrent) {
-            synchronized (mFileDescriptorRecordsLock) {
-                updateOnFileDescriptorEventListenerLocked(fd, events, listener);
-            }
+            addOnFileDescriptorEventListenerConcurrent(fd, events, listener);
         } else {
-            synchronized (this) {
-                updateOnFileDescriptorEventListenerLocked(fd, events, listener);
-            }
+            addOnFileDescriptorEventListenerLegacy(fd, events, listener);
+        }
+    }
+
+    @NeverInline
+    private void removeOnFileDescriptorEventListenerConcurrent(@NonNull FileDescriptor fd) {
+        synchronized (mFileDescriptorRecordsLock) {
+            updateOnFileDescriptorEventListenerLocked(fd, 0, null);
+        }
+    }
+
+    @NeverInline
+    private void removeOnFileDescriptorEventListenerLegacy(@NonNull FileDescriptor fd) {
+        synchronized (this) {
+            updateOnFileDescriptorEventListenerLocked(fd, 0, null);
         }
     }
 
@@ -419,13 +488,9 @@
             throw new IllegalArgumentException("fd must not be null");
         }
         if (mUseConcurrent) {
-            synchronized (mFileDescriptorRecordsLock) {
-                updateOnFileDescriptorEventListenerLocked(fd, 0, null);
-            }
+            removeOnFileDescriptorEventListenerConcurrent(fd);
         } else {
-            synchronized (this) {
-                updateOnFileDescriptorEventListenerLocked(fd, 0, null);
-            }
+            removeOnFileDescriptorEventListenerLegacy(fd);
         }
     }
 
@@ -732,6 +797,7 @@
         }
     }
 
+    @NeverInline
     private Message nextConcurrent() {
         final long ptr = mPtr;
         if (ptr == 0) {
@@ -806,12 +872,8 @@
         }
     }
 
-    @UnsupportedAppUsage
-    Message next() {
-        if (mUseConcurrent) {
-            return nextConcurrent();
-        }
-
+    @NeverInline
+    private Message nextLegacy() {
         // Return here if the message loop has already quit and been disposed.
         // This can happen if the application tries to restart a looper after quit
         // which is not supported.
@@ -929,6 +991,15 @@
         }
     }
 
+    @UnsupportedAppUsage
+    Message next() {
+        if (mUseConcurrent) {
+            return nextConcurrent();
+        } else {
+            return nextLegacy();
+        }
+    }
+
     void quit(boolean safe) {
         if (!mQuitAllowed) {
             throw new IllegalStateException("Main thread not allowed to quit.");
@@ -966,6 +1037,17 @@
         }
     }
 
+    @NeverInline
+    private int postSyncBarrierConcurrent() {
+        return postSyncBarrier(SystemClock.uptimeMillis());
+
+    }
+
+    @NeverInline
+    private int postSyncBarrierLegacy() {
+        return postSyncBarrier(SystemClock.uptimeMillis());
+    }
+
     /**
      * Posts a synchronization barrier to the Looper's message queue.
      *
@@ -992,7 +1074,11 @@
     @UnsupportedAppUsage
     @TestApi
     public int postSyncBarrier() {
-        return postSyncBarrier(SystemClock.uptimeMillis());
+        if (mUseConcurrent) {
+            return postSyncBarrierConcurrent();
+        } else {
+            return postSyncBarrierLegacy();
+        }
     }
 
     private int postSyncBarrier(long when) {
@@ -1077,48 +1163,35 @@
         }
     }
 
-    /**
-     * Removes a synchronization barrier.
-     *
-     * @param token The synchronization barrier token that was returned by
-     * {@link #postSyncBarrier}.
-     *
-     * @throws IllegalStateException if the barrier was not found.
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage
-    @TestApi
-    public void removeSyncBarrier(int token) {
-        // Remove a sync barrier token from the queue.
-        // If the queue is no longer stalled by a barrier then wake it.
-        if (mUseConcurrent) {
-            boolean removed;
-            MessageNode first;
-            final MatchBarrierToken matchBarrierToken = new MatchBarrierToken(token);
+    @NeverInline
+    private void removeSyncBarrierConcurrent(int token) {
+        boolean removed;
+        MessageNode first;
+        final MatchBarrierToken matchBarrierToken = new MatchBarrierToken(token);
 
-            try {
-                /* Retain the first element to see if we are currently stuck on a barrier. */
-                first = mPriorityQueue.first();
-            } catch (NoSuchElementException e) {
-                /* The queue is empty */
-                first = null;
-            }
-
-            removed = findOrRemoveMessages(null, 0, null, null, 0, matchBarrierToken, true);
-            if (removed && first != null) {
-                Message m = first.mMessage;
-                if (m.target == null && m.arg1 == token) {
-                    /* Wake up next() in case it was sleeping on this barrier. */
-                    nativeWake(mPtr);
-                }
-            } else if (!removed) {
-                throw new IllegalStateException("The specified message queue synchronization "
-                        + " barrier token has not been posted or has already been removed.");
-            }
-            return;
+        try {
+            /* Retain the first element to see if we are currently stuck on a barrier. */
+            first = mPriorityQueue.first();
+        } catch (NoSuchElementException e) {
+            /* The queue is empty */
+            first = null;
         }
 
+        removed = findOrRemoveMessages(null, 0, null, null, 0, matchBarrierToken, true);
+        if (removed && first != null) {
+            Message m = first.mMessage;
+            if (m.target == null && m.arg1 == token) {
+                /* Wake up next() in case it was sleeping on this barrier. */
+                nativeWake(mPtr);
+            }
+        } else if (!removed) {
+            throw new IllegalStateException("The specified message queue synchronization "
+                    + " barrier token has not been posted or has already been removed.");
+        }
+    }
+
+    @NeverInline
+    private void removeSyncBarrierLegacy(int token) {
         synchronized (this) {
             Message prev = null;
             Message p = mMessages;
@@ -1154,19 +1227,40 @@
         }
     }
 
-    boolean enqueueMessage(Message msg, long when) {
-        if (msg.target == null) {
-            throw new IllegalArgumentException("Message must have a target.");
-        }
-
+    /**
+     * Removes a synchronization barrier.
+     *
+     * @param token The synchronization barrier token that was returned by
+     * {@link #postSyncBarrier}.
+     *
+     * @throws IllegalStateException if the barrier was not found.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    @TestApi
+    public void removeSyncBarrier(int token) {
+        // Remove a sync barrier token from the queue.
+        // If the queue is no longer stalled by a barrier then wake it.
         if (mUseConcurrent) {
-            if (msg.isInUse()) {
-                throw new IllegalStateException(msg + " This message is already in use.");
-            }
-
-            return enqueueMessageUnchecked(msg, when);
+            removeSyncBarrierConcurrent(token);
+        } else {
+            removeSyncBarrierLegacy(token);
         }
 
+    }
+
+    @NeverInline
+    private boolean enqueueMessageConcurrent(Message msg, long when) {
+        if (msg.isInUse()) {
+            throw new IllegalStateException(msg + " This message is already in use.");
+        }
+
+        return enqueueMessageUnchecked(msg, when);
+    }
+
+    @NeverInline
+    private boolean enqueueMessageLegacy(Message msg, long when) {
         synchronized (this) {
             if (msg.isInUse()) {
                 throw new IllegalStateException(msg + " This message is already in use.");
@@ -1272,6 +1366,18 @@
         return true;
     }
 
+    boolean enqueueMessage(Message msg, long when) {
+        if (msg.target == null) {
+            throw new IllegalArgumentException("Message must have a target.");
+        }
+
+        if (mUseConcurrent) {
+            return enqueueMessageConcurrent(msg, when);
+        } else {
+            return enqueueMessageLegacy(msg, when);
+        }
+    }
+
     private Message legacyPeekOrPoll(boolean peek) {
         synchronized (this) {
             // Try to retrieve the next message.  Return if found.
@@ -1432,14 +1538,15 @@
     }
     private final MatchHandlerWhatAndObject mMatchHandlerWhatAndObject =
             new MatchHandlerWhatAndObject();
-    boolean hasMessages(Handler h, int what, Object object) {
-        if (h == null) {
-            return false;
-        }
-        if (mUseConcurrent) {
-            return findOrRemoveMessages(h, what, object, null, 0, mMatchHandlerWhatAndObject,
-                    false);
-        }
+
+    @NeverInline
+    private boolean hasMessagesConcurrent(Handler h, int what, Object object) {
+        return findOrRemoveMessages(h, what, object, null, 0, mMatchHandlerWhatAndObject,
+                false);
+    }
+
+    @NeverInline
+    private boolean hasMessagesLegacy(Handler h, int what, Object object) {
         synchronized (this) {
             Message p = mMessages;
             while (p != null) {
@@ -1452,6 +1559,17 @@
         }
     }
 
+    boolean hasMessages(Handler h, int what, Object object) {
+        if (h == null) {
+            return false;
+        }
+        if (mUseConcurrent) {
+            return hasMessagesConcurrent(h, what, object);
+        } else {
+            return hasMessagesLegacy(h, what, object);
+        }
+    }
+
     private static final class MatchHandlerWhatAndObjectEquals extends MessageCompare {
         @Override
         public boolean compareMessage(MessageNode n, Handler h, int what, Object object, Runnable r,
@@ -1465,15 +1583,15 @@
     }
     private final MatchHandlerWhatAndObjectEquals mMatchHandlerWhatAndObjectEquals =
             new MatchHandlerWhatAndObjectEquals();
-    boolean hasEqualMessages(Handler h, int what, Object object) {
-        if (h == null) {
-            return false;
-        }
-        if (mUseConcurrent) {
-            return findOrRemoveMessages(h, what, object, null, 0, mMatchHandlerWhatAndObjectEquals,
-                    false);
 
-        }
+    @NeverInline
+    private boolean hasEqualMessagesConcurrent(Handler h, int what, Object object) {
+        return findOrRemoveMessages(h, what, object, null, 0, mMatchHandlerWhatAndObjectEquals,
+                false);
+    }
+
+    @NeverInline
+    private boolean hasEqualMessagesLegacy(Handler h, int what, Object object) {
         synchronized (this) {
             Message p = mMessages;
             while (p != null) {
@@ -1486,6 +1604,17 @@
         }
     }
 
+    boolean hasEqualMessages(Handler h, int what, Object object) {
+        if (h == null) {
+            return false;
+        }
+        if (mUseConcurrent) {
+            return hasEqualMessagesConcurrent(h, what, object);
+        } else {
+            return hasEqualMessagesLegacy(h, what, object);
+        }
+    }
+
     private static final class MatchHandlerRunnableAndObject extends MessageCompare {
         @Override
         public boolean compareMessage(MessageNode n, Handler h, int what, Object object, Runnable r,
@@ -1499,16 +1628,15 @@
     }
     private final MatchHandlerRunnableAndObject mMatchHandlerRunnableAndObject =
             new MatchHandlerRunnableAndObject();
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
-    boolean hasMessages(Handler h, Runnable r, Object object) {
-        if (h == null) {
-            return false;
-        }
-        if (mUseConcurrent) {
-            return findOrRemoveMessages(h, -1, object, r, 0, mMatchHandlerRunnableAndObject,
-                    false);
-        }
 
+    @NeverInline
+    private boolean hasMessagesConcurrent(Handler h, Runnable r, Object object) {
+        return findOrRemoveMessages(h, -1, object, r, 0, mMatchHandlerRunnableAndObject,
+                false);
+    }
+
+    @NeverInline
+    private boolean hasMessagesLegacy(Handler h, Runnable r, Object object) {
         synchronized (this) {
             Message p = mMessages;
             while (p != null) {
@@ -1521,6 +1649,18 @@
         }
     }
 
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    boolean hasMessages(Handler h, Runnable r, Object object) {
+        if (h == null) {
+            return false;
+        }
+        if (mUseConcurrent) {
+            return hasMessagesConcurrent(h, r, object);
+        } else {
+            return hasMessagesLegacy(h, r, object);
+        }
+    }
+
     private static final class MatchHandler extends MessageCompare {
         @Override
         public boolean compareMessage(MessageNode n, Handler h, int what, Object object, Runnable r,
@@ -1529,13 +1669,14 @@
         }
     }
     private final MatchHandler mMatchHandler = new MatchHandler();
-    boolean hasMessages(Handler h) {
-        if (h == null) {
-            return false;
-        }
-        if (mUseConcurrent) {
-            return findOrRemoveMessages(h, -1, null, null, 0, mMatchHandler, false);
-        }
+
+    @NeverInline
+    private boolean hasMessagesConcurrent(Handler h) {
+        return findOrRemoveMessages(h, -1, null, null, 0, mMatchHandler, false);
+    }
+
+    @NeverInline
+    private boolean hasMessagesLegacy(Handler h) {
         synchronized (this) {
             Message p = mMessages;
             while (p != null) {
@@ -1548,14 +1689,24 @@
         }
     }
 
-    void removeMessages(Handler h, int what, Object object) {
+    boolean hasMessages(Handler h) {
         if (h == null) {
-            return;
+            return false;
         }
         if (mUseConcurrent) {
-            findOrRemoveMessages(h, what, object, null, 0, mMatchHandlerWhatAndObject, true);
-            return;
+            return hasMessagesConcurrent(h);
+        } else {
+            return hasMessagesLegacy(h);
         }
+    }
+
+    @NeverInline
+    private void removeMessagesConcurrent(Handler h, int what, Object object) {
+        findOrRemoveMessages(h, what, object, null, 0, mMatchHandlerWhatAndObject, true);
+    }
+
+    @NeverInline
+    private void removeMessagesLegacy(Handler h, int what, Object object) {
         synchronized (this) {
             Message p = mMessages;
 
@@ -1598,67 +1749,85 @@
         }
     }
 
+    void removeMessages(Handler h, int what, Object object) {
+        if (h == null) {
+            return;
+        }
+        if (mUseConcurrent) {
+            removeMessagesConcurrent(h, what, object);
+        } else {
+            removeMessagesLegacy(h, what, object);
+        }
+    }
+
+    @NeverInline
+    private void removeEqualMessagesConcurrent(Handler h, int what, Object object) {
+            findOrRemoveMessages(h, what, object, null, 0, mMatchHandlerWhatAndObjectEquals, true);
+    }
+
+    @NeverInline
+    private void removeEqualMessagesLegacy(Handler h, int what, Object object) {
+        synchronized (this) {
+            Message p = mMessages;
+
+            // Remove all messages at front.
+            while (p != null && p.target == h && p.what == what
+                   && (object == null || object.equals(p.obj))) {
+                Message n = p.next;
+                mMessages = n;
+                if (p.isAsynchronous()) {
+                    mAsyncMessageCount--;
+                }
+                p.recycleUnchecked();
+                p = n;
+            }
+
+            if (p == null) {
+                mLast = mMessages;
+            }
+
+            // Remove all messages after front.
+            while (p != null) {
+                Message n = p.next;
+                if (n != null) {
+                    if (n.target == h && n.what == what
+                            && (object == null || object.equals(n.obj))) {
+                        Message nn = n.next;
+                        if (n.isAsynchronous()) {
+                            mAsyncMessageCount--;
+                        }
+                        n.recycleUnchecked();
+                        p.next = nn;
+                        if (p.next == null) {
+                            mLast = p;
+                        }
+                        continue;
+                    }
+                }
+                p = n;
+            }
+        }
+    }
+
     void removeEqualMessages(Handler h, int what, Object object) {
         if (h == null) {
             return;
         }
 
         if (mUseConcurrent) {
-            findOrRemoveMessages(h, what, object, null, 0, mMatchHandlerWhatAndObjectEquals, true);
-            return;
-        }
-
-        synchronized (this) {
-            Message p = mMessages;
-
-            // Remove all messages at front.
-            while (p != null && p.target == h && p.what == what
-                   && (object == null || object.equals(p.obj))) {
-                Message n = p.next;
-                mMessages = n;
-                if (p.isAsynchronous()) {
-                    mAsyncMessageCount--;
-                }
-                p.recycleUnchecked();
-                p = n;
-            }
-
-            if (p == null) {
-                mLast = mMessages;
-            }
-
-            // Remove all messages after front.
-            while (p != null) {
-                Message n = p.next;
-                if (n != null) {
-                    if (n.target == h && n.what == what
-                            && (object == null || object.equals(n.obj))) {
-                        Message nn = n.next;
-                        if (n.isAsynchronous()) {
-                            mAsyncMessageCount--;
-                        }
-                        n.recycleUnchecked();
-                        p.next = nn;
-                        if (p.next == null) {
-                            mLast = p;
-                        }
-                        continue;
-                    }
-                }
-                p = n;
-            }
+            removeEqualMessagesConcurrent(h, what, object);
+        } else {
+            removeEqualMessagesLegacy(h, what, object);
         }
     }
 
-    void removeMessages(Handler h, Runnable r, Object object) {
-        if (h == null || r == null) {
-            return;
-        }
+    @NeverInline
+    private void removeMessagesConcurrent(Handler h, Runnable r, Object object) {
+        findOrRemoveMessages(h, -1, object, r, 0, mMatchHandlerRunnableAndObject, true);
+    }
 
-        if (mUseConcurrent) {
-            findOrRemoveMessages(h, -1, object, r, 0, mMatchHandlerRunnableAndObject, true);
-            return;
-        }
+    @NeverInline
+    private void removeMessagesLegacy(Handler h, Runnable r, Object object) {
         synchronized (this) {
             Message p = mMessages;
 
@@ -1701,6 +1870,18 @@
         }
     }
 
+    void removeMessages(Handler h, Runnable r, Object object) {
+        if (h == null || r == null) {
+            return;
+        }
+
+        if (mUseConcurrent) {
+            removeMessagesConcurrent(h, r, object);
+        } else {
+            removeMessagesLegacy(h, r, object);
+        }
+    }
+
     private static final class MatchHandlerRunnableAndObjectEquals extends MessageCompare {
         @Override
         public boolean compareMessage(MessageNode n, Handler h, int what, Object object, Runnable r,
@@ -1714,15 +1895,14 @@
     }
     private final MatchHandlerRunnableAndObjectEquals mMatchHandlerRunnableAndObjectEquals =
             new MatchHandlerRunnableAndObjectEquals();
-    void removeEqualMessages(Handler h, Runnable r, Object object) {
-        if (h == null || r == null) {
-            return;
-        }
 
-        if (mUseConcurrent) {
-            findOrRemoveMessages(h, -1, object, r, 0, mMatchHandlerRunnableAndObjectEquals, true);
-            return;
-        }
+    @NeverInline
+    private void removeEqualMessagesConcurrent(Handler h, Runnable r, Object object) {
+        findOrRemoveMessages(h, -1, object, r, 0, mMatchHandlerRunnableAndObjectEquals, true);
+    }
+
+    @NeverInline
+    private void removeEqualMessagesLegacy(Handler h, Runnable r, Object object) {
         synchronized (this) {
             Message p = mMessages;
 
@@ -1765,6 +1945,18 @@
         }
     }
 
+    void removeEqualMessages(Handler h, Runnable r, Object object) {
+        if (h == null || r == null) {
+            return;
+        }
+
+        if (mUseConcurrent) {
+            removeEqualMessagesConcurrent(h, r, object);
+        } else {
+            removeEqualMessagesLegacy(h, r, object);
+        }
+    }
+
     private static final class MatchHandlerAndObject extends MessageCompare {
         @Override
         public boolean compareMessage(MessageNode n, Handler h, int what, Object object, Runnable r,
@@ -1777,15 +1969,14 @@
         }
     }
     private final MatchHandlerAndObject mMatchHandlerAndObject = new MatchHandlerAndObject();
-    void removeCallbacksAndMessages(Handler h, Object object) {
-        if (h == null) {
-            return;
-        }
 
-        if (mUseConcurrent) {
+    @NeverInline
+    private void removeCallbacksAndMessagesConcurrent(Handler h, Object object) {
             findOrRemoveMessages(h, -1, object, null, 0, mMatchHandlerAndObject, true);
-            return;
-        }
+    }
+
+    @NeverInline
+    private void removeCallbacksAndMessagesLegacy(Handler h, Object object) {
         synchronized (this) {
             Message p = mMessages;
 
@@ -1827,6 +2018,18 @@
         }
     }
 
+    void removeCallbacksAndMessages(Handler h, Object object) {
+        if (h == null) {
+            return;
+        }
+
+        if (mUseConcurrent) {
+            removeCallbacksAndMessagesConcurrent(h, object);
+        } else {
+            removeCallbacksAndMessagesLegacy(h, object);
+        }
+    }
+
     private static final class MatchHandlerAndObjectEquals extends MessageCompare {
         @Override
         public boolean compareMessage(MessageNode n, Handler h, int what, Object object, Runnable r,
@@ -1840,15 +2043,14 @@
     }
     private final MatchHandlerAndObjectEquals mMatchHandlerAndObjectEquals =
             new MatchHandlerAndObjectEquals();
-    void removeCallbacksAndEqualMessages(Handler h, Object object) {
-        if (h == null) {
-            return;
-        }
 
-        if (mUseConcurrent) {
-            findOrRemoveMessages(h, -1, object, null, 0, mMatchHandlerAndObjectEquals, true);
-            return;
-        }
+    @NeverInline
+    void removeCallbacksAndEqualMessagesConcurrent(Handler h, Object object) {
+        findOrRemoveMessages(h, -1, object, null, 0, mMatchHandlerAndObjectEquals, true);
+    }
+
+    @NeverInline
+    void removeCallbacksAndEqualMessagesLegacy(Handler h, Object object) {
         synchronized (this) {
             Message p = mMessages;
 
@@ -1890,6 +2092,18 @@
         }
     }
 
+    void removeCallbacksAndEqualMessages(Handler h, Object object) {
+        if (h == null) {
+            return;
+        }
+
+        if (mUseConcurrent) {
+            removeCallbacksAndEqualMessagesConcurrent(h, object);
+        } else {
+            removeCallbacksAndEqualMessagesLegacy(h, object);
+        }
+    }
+
     private void removeAllMessagesLocked() {
         Message p = mMessages;
         while (p != null) {
diff --git a/core/java/android/os/CpuHeadroomParams.java b/core/java/android/os/CpuHeadroomParams.java
index 072c012..e511976 100644
--- a/core/java/android/os/CpuHeadroomParams.java
+++ b/core/java/android/os/CpuHeadroomParams.java
@@ -28,15 +28,11 @@
 
 /**
  * Headroom request params used by {@link SystemHealthManager#getCpuHeadroom(CpuHeadroomParams)}.
+ *
+ * <p>This class is immutable and one should use the {@link Builder} to build a new instance.
  */
 @FlaggedApi(Flags.FLAG_CPU_GPU_HEADROOMS)
 public final class CpuHeadroomParams {
-    final CpuHeadroomParamsInternal mInternal;
-
-    public CpuHeadroomParams() {
-        mInternal = new CpuHeadroomParamsInternal();
-    }
-
     /** @hide */
     @IntDef(flag = false, prefix = {"CPU_HEADROOM_CALCULATION_TYPE_"}, value = {
             CPU_HEADROOM_CALCULATION_TYPE_MIN, // 0
@@ -47,107 +43,188 @@
     }
 
     /**
-     * Calculates the headroom based on minimum value over a device-defined window.
+     * The headroom calculation type bases on minimum value over a specified window.
      */
     public static final int CPU_HEADROOM_CALCULATION_TYPE_MIN = 0;
 
     /**
-     * Calculates the headroom based on average value over a device-defined window.
+     * The headroom calculation type bases on average value over a specified window.
      */
     public static final int CPU_HEADROOM_CALCULATION_TYPE_AVERAGE = 1;
 
-    private static final int CALCULATION_WINDOW_MILLIS_MIN = 50;
-    private static final int CALCULATION_WINDOW_MILLIS_MAX = 10000;
-    private static final int MAX_TID_COUNT = 5;
+    /** @hide */
+    public final CpuHeadroomParamsInternal mInternal;
+
+    private CpuHeadroomParams() {
+        mInternal = new CpuHeadroomParamsInternal();
+    }
+
+    public static final class Builder {
+        private int mCalculationType = -1;
+        private int mCalculationWindowMillis = -1;
+        private int[] mTids = null;
+
+        public Builder() {
+        }
+
+        /**
+         * Returns a new builder copy with the same values as the params.
+         */
+        public Builder(@NonNull CpuHeadroomParams params) {
+            if (params.mInternal.calculationType >= 0) {
+                mCalculationType = params.mInternal.calculationType;
+            }
+            if (params.mInternal.calculationWindowMillis >= 0) {
+                mCalculationWindowMillis = params.mInternal.calculationWindowMillis;
+            }
+            if (params.mInternal.tids != null) {
+                mTids = Arrays.copyOf(params.mInternal.tids, params.mInternal.tids.length);
+            }
+        }
+
+        /**
+         * Sets the headroom calculation type.
+         * <p>
+         *
+         * @throws IllegalArgumentException if the type is invalid.
+         */
+        @NonNull
+        public Builder setCalculationType(
+                @CpuHeadroomCalculationType int calculationType) {
+            switch (calculationType) {
+                case CPU_HEADROOM_CALCULATION_TYPE_MIN:
+                case CPU_HEADROOM_CALCULATION_TYPE_AVERAGE: {
+                    mCalculationType = calculationType;
+                    return this;
+                }
+            }
+            throw new IllegalArgumentException("Invalid calculation type: " + calculationType);
+        }
+
+        /**
+         * Sets the headroom calculation window size in milliseconds.
+         * <p>
+         *
+         * @param windowMillis the window size in milliseconds ranges from
+         *                     {@link SystemHealthManager#getCpuHeadroomCalculationWindowRange()}.
+         *                     The smaller the window size, the larger fluctuation in the headroom
+         *                     value should be expected. The default value can be retrieved from
+         *                     the {@link CpuHeadroomParams#getCalculationWindowMillis}. The device
+         *                     will try to use the closest feasible window size to this param.
+         * @throws IllegalArgumentException if the window is invalid.
+         */
+        @NonNull
+        public Builder setCalculationWindowMillis(@IntRange(from = 1) int windowMillis) {
+            if (windowMillis <= 0) {
+                throw new IllegalArgumentException("Invalid calculation window: " + windowMillis);
+            }
+            mCalculationWindowMillis = windowMillis;
+            return this;
+        }
+
+        /**
+         * Sets the thread TIDs to track.
+         * <p>
+         * The TIDs should belong to the same of the process that will make the headroom call. And
+         * they should not have different core affinity.
+         * <p>
+         * If not set or set to empty, the headroom will be based on the PID of the process making
+         * the call.
+         *
+         * @param tids non-null list of TIDs, where maximum size can be read from
+         *             {@link SystemHealthManager#getMaxCpuHeadroomTidsSize()}.
+         * @throws IllegalArgumentException if the TID is not positive.
+         */
+        @NonNull
+        public Builder setTids(@NonNull int... tids) {
+            for (int tid : tids) {
+                if (tid <= 0) {
+                    throw new IllegalArgumentException("Invalid TID: " + tid);
+                }
+            }
+            mTids = tids;
+            return this;
+        }
+
+        /**
+         * Builds the {@link CpuHeadroomParams} object.
+         */
+        @NonNull
+        public CpuHeadroomParams build() {
+            CpuHeadroomParams params = new CpuHeadroomParams();
+            if (mCalculationType >= 0) {
+                params.mInternal.calculationType = (byte) mCalculationType;
+            }
+            if (mCalculationWindowMillis >= 0) {
+                params.mInternal.calculationWindowMillis = mCalculationWindowMillis;
+            }
+            if (mTids != null) {
+                params.mInternal.tids = mTids;
+            }
+            return params;
+        }
+    }
 
     /**
-     * Sets the headroom calculation type.
-     * <p>
-     *
-     * @throws IllegalArgumentException if the type is invalid.
+     * Returns a new builder with the same values as this object.
      */
-    public void setCalculationType(@CpuHeadroomCalculationType int calculationType) {
-        switch (calculationType) {
-            case CPU_HEADROOM_CALCULATION_TYPE_MIN:
-            case CPU_HEADROOM_CALCULATION_TYPE_AVERAGE:
-                mInternal.calculationType = (byte) calculationType;
-                return;
-        }
-        throw new IllegalArgumentException("Invalid calculation type: " + calculationType);
+    @NonNull
+    public Builder toBuilder() {
+        return new Builder(this);
     }
 
     /**
      * Gets the headroom calculation type.
-     * Default to {@link #CPU_HEADROOM_CALCULATION_TYPE_MIN} if not set.
+     * <p>
+     * This will return the default value chosen by the device if not set.
      */
     public @CpuHeadroomCalculationType int getCalculationType() {
         @CpuHeadroomCalculationType int validatedType = switch ((int) mInternal.calculationType) {
-            case CPU_HEADROOM_CALCULATION_TYPE_MIN, CPU_HEADROOM_CALCULATION_TYPE_AVERAGE ->
-                    mInternal.calculationType;
+            case CPU_HEADROOM_CALCULATION_TYPE_MIN,
+                 CPU_HEADROOM_CALCULATION_TYPE_AVERAGE -> mInternal.calculationType;
             default -> CPU_HEADROOM_CALCULATION_TYPE_MIN;
         };
         return validatedType;
     }
 
     /**
-     * Sets the headroom calculation window size in milliseconds.
-     * <p>
-     *
-     * @param windowMillis the window size in milliseconds ranges from [50, 10000]. The smaller the
-     *                     window size, the larger fluctuation in the headroom value should be
-     *                     expected. The default value can be retrieved from the
-     *                     {@link #getCalculationWindowMillis}. The device will try to use the
-     *                     closest feasible window size to this param.
-     * @throws IllegalArgumentException if the window size is not in allowed range.
-     */
-    public void setCalculationWindowMillis(
-            @IntRange(from = CALCULATION_WINDOW_MILLIS_MIN, to =
-                    CALCULATION_WINDOW_MILLIS_MAX) int windowMillis) {
-        if (windowMillis < CALCULATION_WINDOW_MILLIS_MIN
-                || windowMillis > CALCULATION_WINDOW_MILLIS_MAX) {
-            throw new IllegalArgumentException("Invalid calculation window: " + windowMillis);
-        }
-        mInternal.calculationWindowMillis = windowMillis;
-    }
-
-    /**
      * Gets the headroom calculation window size in milliseconds.
      * <p>
-     * This will return the default value chosen by the device if the params is not set.
+     * This will return the default value chosen by the device if not set.
      */
-    public @IntRange(from = CALCULATION_WINDOW_MILLIS_MIN, to =
-            CALCULATION_WINDOW_MILLIS_MAX) long getCalculationWindowMillis() {
+    public long getCalculationWindowMillis() {
         return mInternal.calculationWindowMillis;
     }
 
     /**
-     * Sets the thread TIDs to track.
+     * Gets the TIDs to track.
      * <p>
-     * The TIDs should belong to the same of the process that will the headroom call. And they
-     * should not have different core affinity.
-     * <p>
-     * If not set, the headroom will be based on the PID of the process making the call.
-     *
-     * @param tids non-empty list of TIDs, maximum 5.
-     * @throws IllegalArgumentException if the list size is not in allowed range or TID is not
-     *                                  positive.
+     * This will return a copy of the TIDs in the params, or null if the params is not set.
      */
-    public void setTids(@NonNull int... tids) {
-        if (tids.length == 0 || tids.length > MAX_TID_COUNT) {
-            throw new IllegalArgumentException("Invalid number of TIDs: " + tids.length);
-        }
-        for (int tid : tids) {
-            if (tid <= 0) {
-                throw new IllegalArgumentException("Invalid TID: " + tid);
-            }
-        }
-        mInternal.tids = Arrays.copyOf(tids, tids.length);
+    @NonNull
+    public int[] getTids() {
+        return mInternal.tids == null ? null : Arrays.copyOf(mInternal.tids, mInternal.tids.length);
     }
 
-    /**
-     * @hide
-     */
-    public CpuHeadroomParamsInternal getInternal() {
-        return mInternal;
+    @Override
+    public String toString() {
+        return "CpuHeadroomParams{"
+                + "calculationType=" + mInternal.calculationType
+                + ", calculationWindowMillis=" + mInternal.calculationWindowMillis
+                + ", tids=" + Arrays.toString(mInternal.tids)
+                + '}';
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        CpuHeadroomParams that = (CpuHeadroomParams) o;
+        return mInternal.equals(that.mInternal);
+    }
+
+    @Override
+    public int hashCode() {
+        return mInternal.hashCode();
     }
 }
diff --git a/core/java/android/os/GpuHeadroomParams.java b/core/java/android/os/GpuHeadroomParams.java
index 126ee8c..5c5e7bb 100644
--- a/core/java/android/os/GpuHeadroomParams.java
+++ b/core/java/android/os/GpuHeadroomParams.java
@@ -19,6 +19,7 @@
 import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.IntRange;
+import android.annotation.NonNull;
 import android.os.health.SystemHealthManager;
 
 import java.lang.annotation.Retention;
@@ -26,15 +27,11 @@
 
 /**
  * Headroom request params used by {@link SystemHealthManager#getGpuHeadroom(GpuHeadroomParams)}.
+ *
+ * <p>This class is immutable and one should use the {@link Builder} to build a new instance.
  */
 @FlaggedApi(Flags.FLAG_CPU_GPU_HEADROOMS)
 public final class GpuHeadroomParams {
-    final GpuHeadroomParamsInternal mInternal;
-
-    public GpuHeadroomParams() {
-        mInternal = new GpuHeadroomParamsInternal();
-    }
-
     /** @hide */
     @IntDef(flag = false, prefix = {"GPU_HEADROOM_CALCULATION_TYPE_"}, value = {
             GPU_HEADROOM_CALCULATION_TYPE_MIN, // 0
@@ -45,82 +42,153 @@
     }
 
     /**
-     * Calculates the headroom based on minimum value over a device-defined window.
+     * The headroom calculation type bases on minimum value over a specified window.
      */
     public static final int GPU_HEADROOM_CALCULATION_TYPE_MIN = 0;
 
     /**
-     * Calculates the headroom based on average value over a device-defined window.
+     * The headroom calculation type bases on average value over a specified window.
      */
     public static final int GPU_HEADROOM_CALCULATION_TYPE_AVERAGE = 1;
 
-    private static final int CALCULATION_WINDOW_MILLIS_MIN = 50;
-    private static final int CALCULATION_WINDOW_MILLIS_MAX = 10000;
+    /**
+     * The minimum size of the window to compute the headroom over.
+     */
+    public static final int GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN = 50;
 
     /**
-     * Sets the headroom calculation type.
-     * <p>
-     *
-     * @throws IllegalArgumentException if the type is invalid.
+     * The maximum size of the window to compute the headroom over.
      */
-    public void setCalculationType(@GpuHeadroomCalculationType int calculationType) {
-        switch (calculationType) {
-            case GPU_HEADROOM_CALCULATION_TYPE_MIN:
-            case GPU_HEADROOM_CALCULATION_TYPE_AVERAGE:
-                mInternal.calculationType = (byte) calculationType;
-                return;
+    public static final int GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX = 10000;
+
+    /**
+     * @hide
+     */
+    public final GpuHeadroomParamsInternal mInternal;
+
+    /**
+     * @hide
+     */
+    private GpuHeadroomParams() {
+        mInternal = new GpuHeadroomParamsInternal();
+    }
+
+    public static final class Builder {
+        private int mCalculationType = -1;
+        private int mCalculationWindowMillis = -1;
+
+        public Builder() {
         }
-        throw new IllegalArgumentException("Invalid calculation type: " + calculationType);
+
+        /**
+         * Returns a new builder with the same values as this object.
+         */
+        public Builder(@NonNull GpuHeadroomParams params) {
+            if (params.mInternal.calculationType >= 0) {
+                mCalculationType = params.mInternal.calculationType;
+            }
+            if (params.mInternal.calculationWindowMillis >= 0) {
+                mCalculationWindowMillis = params.mInternal.calculationWindowMillis;
+            }
+        }
+
+        /**
+         * Sets the headroom calculation type.
+         * <p>
+         *
+         * @throws IllegalArgumentException if the type is invalid.
+         */
+        @NonNull
+        public Builder setCalculationType(
+                @GpuHeadroomCalculationType int calculationType) {
+            switch (calculationType) {
+                case GPU_HEADROOM_CALCULATION_TYPE_MIN:
+                case GPU_HEADROOM_CALCULATION_TYPE_AVERAGE: {
+                    mCalculationType = calculationType;
+                    return this;
+                }
+            }
+            throw new IllegalArgumentException("Invalid calculation type: " + calculationType);
+        }
+
+        /**
+         * Sets the headroom calculation window size in milliseconds.
+         * <p>
+         *
+         * @param windowMillis the window size in milliseconds ranges from
+         *                     {@link SystemHealthManager#getGpuHeadroomCalculationWindowRange()}.
+         *                     The smaller the window size, the larger fluctuation in the headroom
+         *                     value should be expected. The default value can be retrieved from
+         *                     the {@link GpuHeadroomParams#getCalculationWindowMillis}. The device
+         *                     will try to use the closest feasible window size to this param.
+         * @throws IllegalArgumentException if the window is invalid.
+         */
+        @NonNull
+        public Builder setCalculationWindowMillis(@IntRange(from = 1) int windowMillis) {
+            if (windowMillis <= 0) {
+                throw new IllegalArgumentException("Invalid calculation window: " + windowMillis);
+            }
+            mCalculationWindowMillis = windowMillis;
+            return this;
+        }
+
+        /**
+         * Builds the {@link GpuHeadroomParams} object.
+         */
+        @NonNull
+        public GpuHeadroomParams build() {
+            GpuHeadroomParams params = new GpuHeadroomParams();
+            if (mCalculationType >= 0) {
+                params.mInternal.calculationType = (byte) mCalculationType;
+            }
+            if (mCalculationWindowMillis >= 0) {
+                params.mInternal.calculationWindowMillis = mCalculationWindowMillis;
+            }
+            return params;
+        }
     }
 
     /**
      * Gets the headroom calculation type.
-     * Default to {@link #GPU_HEADROOM_CALCULATION_TYPE_MIN} if the params is not set.
+     * <p>
+     * This will return the default value chosen by the device if not set.
      */
     public @GpuHeadroomCalculationType int getCalculationType() {
         @GpuHeadroomCalculationType int validatedType = switch ((int) mInternal.calculationType) {
-            case GPU_HEADROOM_CALCULATION_TYPE_MIN, GPU_HEADROOM_CALCULATION_TYPE_AVERAGE ->
-                    mInternal.calculationType;
+            case GPU_HEADROOM_CALCULATION_TYPE_MIN,
+                 GPU_HEADROOM_CALCULATION_TYPE_AVERAGE -> mInternal.calculationType;
             default -> GPU_HEADROOM_CALCULATION_TYPE_MIN;
         };
         return validatedType;
     }
 
     /**
-     * Sets the headroom calculation window size in milliseconds.
-     * <p>
-     *
-     * @param windowMillis the window size in milliseconds ranges from [50, 10000]. The smaller the
-     *                     window size, the larger fluctuation in the headroom value should be
-     *                     expected. The default value can be retrieved from the
-     *                     {@link #getCalculationWindowMillis}. The device will try to use the
-     *                     closest feasible window size to this param.
-     * @throws IllegalArgumentException if the window is invalid.
-     */
-    public void setCalculationWindowMillis(
-            @IntRange(from = CALCULATION_WINDOW_MILLIS_MIN, to =
-                    CALCULATION_WINDOW_MILLIS_MAX) int windowMillis) {
-        if (windowMillis < CALCULATION_WINDOW_MILLIS_MIN
-                || windowMillis > CALCULATION_WINDOW_MILLIS_MAX) {
-            throw new IllegalArgumentException("Invalid calculation window: " + windowMillis);
-        }
-        mInternal.calculationWindowMillis = windowMillis;
-    }
-
-    /**
      * Gets the headroom calculation window size in milliseconds.
      * <p>
      * This will return the default value chosen by the device if not set.
      */
-    public @IntRange(from = CALCULATION_WINDOW_MILLIS_MIN, to =
-            CALCULATION_WINDOW_MILLIS_MAX) int getCalculationWindowMillis() {
+    public int getCalculationWindowMillis() {
         return mInternal.calculationWindowMillis;
     }
 
-    /**
-     * @hide
-     */
-    public GpuHeadroomParamsInternal getInternal() {
-        return mInternal;
+    @Override
+    public String toString() {
+        return "GpuHeadroomParams{"
+                + "calculationType=" + mInternal.calculationType
+                + ", calculationWindowMillis=" + mInternal.calculationWindowMillis
+                + '}';
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        GpuHeadroomParams that = (GpuHeadroomParams) o;
+        return mInternal.equals(that.mInternal);
+    }
+
+    @Override
+    public int hashCode() {
+        return mInternal.hashCode();
     }
 }
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index 8f6a508..12080ca 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -78,6 +78,9 @@
     private static final String PROPERTY_GFX_DRIVER_PRERELEASE = "ro.gfx.driver.1";
     private static final String PROPERTY_GFX_DRIVER_BUILD_TIME = "ro.gfx.driver_build_time";
 
+    /// System properties related to EGL
+    private static final String PROPERTY_RO_HARDWARE_EGL = "ro.hardware.egl";
+
     // Metadata flags within the <application> tag in the AndroidManifest.xml file.
     private static final String METADATA_DRIVER_BUILD_TIME =
             "com.android.graphics.driver.build_time";
@@ -504,9 +507,11 @@
 
         final List<ResolveInfo> resolveInfos =
                 pm.queryIntentActivities(intent, PackageManager.MATCH_SYSTEM_ONLY);
-        if (resolveInfos.size() != 1) {
-            Log.v(TAG, "Invalid number of ANGLE packages. Required: 1, Found: "
-                    + resolveInfos.size());
+        if (resolveInfos.isEmpty()) {
+            Log.v(TAG, "No ANGLE packages installed.");
+            return "";
+        } else if (resolveInfos.size() > 1) {
+            Log.v(TAG, "Too many ANGLE packages found: " + resolveInfos.size());
             if (DEBUG) {
                 for (ResolveInfo resolveInfo : resolveInfos) {
                     Log.d(TAG, "Found ANGLE package: " + resolveInfo.activityInfo.packageName);
@@ -516,7 +521,7 @@
         }
 
         // Must be exactly 1 ANGLE PKG found to get here.
-        return resolveInfos.get(0).activityInfo.packageName;
+        return resolveInfos.getFirst().activityInfo.packageName;
     }
 
     /**
@@ -545,10 +550,12 @@
     }
 
     /**
-     * Determine whether ANGLE should be used, attempt to set up from apk first, if ANGLE can be
-     * set up from apk, pass ANGLE details down to the C++ GraphicsEnv class via
-     * GraphicsEnv::setAngleInfo(). If apk setup fails, attempt to set up to use system ANGLE.
-     * Return false if both fail.
+     * If ANGLE is not the system driver, determine whether ANGLE should be used, and if so, pass
+     * down the necessary details to the C++ GraphicsEnv class via GraphicsEnv::setAngleInfo().
+     * <p>
+     * If ANGLE is the system driver or the various flags indicate it should be used, attempt to
+     * set up ANGLE from the APK first, so the updatable libraries are used. If APK setup fails,
+     * attempt to set up the system ANGLE. Return false if both fail.
      *
      * @param context - Context of the application.
      * @param bundle - Bundle of the application.
@@ -559,15 +566,26 @@
      */
     private boolean setupAngle(Context context, Bundle bundle, PackageManager packageManager,
             String packageName) {
-        final String angleChoice = queryAngleChoice(context, bundle, packageName);
-        if (angleChoice.equals(ANGLE_GL_DRIVER_CHOICE_DEFAULT)) {
-            return false;
-        }
-        if (angleChoice.equals(ANGLE_GL_DRIVER_CHOICE_NATIVE)) {
-            nativeSetAngleInfo("", true, packageName, null);
-            return false;
+        final String eglDriverName = SystemProperties.get(PROPERTY_RO_HARDWARE_EGL);
+
+        // The ANGLE choice only makes sense if ANGLE is not the system driver.
+        if (!eglDriverName.equals(ANGLE_DRIVER_NAME)) {
+            final String angleChoice = queryAngleChoice(context, bundle, packageName);
+            if (angleChoice.equals(ANGLE_GL_DRIVER_CHOICE_DEFAULT)) {
+                return false;
+            }
+            if (angleChoice.equals(ANGLE_GL_DRIVER_CHOICE_NATIVE)) {
+                nativeSetAngleInfo("", true, packageName, null);
+                return false;
+            }
         }
 
+        // If we reach here, it means either:
+        // 1. system driver is not ANGLE, but ANGLE is requested.
+        // 2. system driver is ANGLE.
+        // In both cases, setup ANGLE info. We attempt to setup the APK first, so
+        // updated/development libraries are used if the APK is present, falling back to the system
+        // libraries otherwise.
         return setupAngleFromApk(context, bundle, packageManager, packageName)
                 || setupAngleFromSystem(context, bundle, packageName);
     }
@@ -605,7 +623,6 @@
         if (angleInfo == null) {
             anglePkgName = getAnglePackageName(packageManager);
             if (TextUtils.isEmpty(anglePkgName)) {
-                Log.v(TAG, "Failed to find ANGLE package.");
                 return false;
             }
 
diff --git a/core/java/android/os/IHintManager.aidl b/core/java/android/os/IHintManager.aidl
index 5128e91..2432545 100644
--- a/core/java/android/os/IHintManager.aidl
+++ b/core/java/android/os/IHintManager.aidl
@@ -72,6 +72,7 @@
     parcelable HintManagerClientData {
         int powerHalVersion;
         int maxGraphicsPipelineThreads;
+        int maxCpuHeadroomThreads;
         long preferredRateNanos;
         SupportInfo supportInfo;
     }
@@ -88,4 +89,6 @@
      * passing back a bundle of support and configuration information.
      */
     HintManagerClientData registerClient(in IHintManagerClient client);
+
+    HintManagerClientData getClientData();
 }
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 5ba6553..0879118 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -3668,6 +3668,7 @@
         int length = readInt();
         if (length >= 0)
         {
+            ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, length);
             array = new CharSequence[length];
 
             for (int i = 0 ; i < length ; i++)
@@ -3689,6 +3690,7 @@
 
         int length = readInt();
         if (length >= 0) {
+            ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, length);
             array = new ArrayList<CharSequence>(length);
 
             for (int i = 0 ; i < length ; i++) {
@@ -3831,6 +3833,7 @@
         if (N < 0) {
             return null;
         }
+        ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, N);
         SparseBooleanArray sa = new SparseBooleanArray(N);
         readSparseBooleanArrayInternal(sa, N);
         return sa;
@@ -3847,6 +3850,7 @@
         if (N < 0) {
             return null;
         }
+        ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, N);
         SparseIntArray sa = new SparseIntArray(N);
         readSparseIntArrayInternal(sa, N);
         return sa;
@@ -3892,6 +3896,7 @@
     public final <T> void readTypedList(@NonNull List<T> list, @NonNull Parcelable.Creator<T> c) {
         int M = list.size();
         int N = readInt();
+        ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, N);
         int i = 0;
         for (; i < M && i < N; i++) {
             list.set(i, readTypedObject(c));
@@ -4050,6 +4055,7 @@
     public final void readStringList(@NonNull List<String> list) {
         int M = list.size();
         int N = readInt();
+        ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, N);
         int i = 0;
         for (; i < M && i < N; i++) {
             list.set(i, readString());
@@ -4071,6 +4077,7 @@
     public final void readBinderList(@NonNull List<IBinder> list) {
         int M = list.size();
         int N = readInt();
+        ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, N);
         int i = 0;
         for (; i < M && i < N; i++) {
             list.set(i, readStrongBinder());
@@ -4093,6 +4100,7 @@
             @NonNull Function<IBinder, T> asInterface) {
         int M = list.size();
         int N = readInt();
+        ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, N);
         int i = 0;
         for (; i < M && i < N; i++) {
             list.set(i, asInterface.apply(readStrongBinder()));
@@ -4159,6 +4167,7 @@
             list.clear();
             return list;
         }
+        ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, n);
 
         final int m = list.size();
         int i = 0;
@@ -5540,6 +5549,7 @@
         if (n < 0) {
             return null;
         }
+        ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, n);
         HashMap<K, V> map = new HashMap<>(n);
         readMapInternal(map, n, loader, clazzKey, clazzValue);
         return map;
@@ -5555,6 +5565,8 @@
     private <K, V> void readMapInternal(@NonNull Map<? super K, ? super V> outVal, int n,
             @Nullable ClassLoader loader, @Nullable Class<K> clazzKey,
             @Nullable Class<V> clazzValue) {
+        ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, n);
+        // TODO: move all reservation of map size here, not all reserves?
         while (n > 0) {
             K key = readValue(loader, clazzKey);
             V value = readValue(loader, clazzValue);
@@ -5580,6 +5592,7 @@
      */
     void readArrayMap(ArrayMap<? super String, Object> map, int size, boolean sorted,
             boolean lazy, @Nullable ClassLoaderProvider loaderProvider, int[] lazyValueCount) {
+        ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, size);
         while (size > 0) {
             String key = readString();
             Object value = (lazy) ? readLazyValue(loaderProvider) : readValue(
@@ -5625,6 +5638,7 @@
         if (size < 0) {
             return null;
         }
+        ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, size);
         ArraySet<Object> result = new ArraySet<>(size);
         for (int i = 0; i < size; i++) {
             Object value = readValue(loader);
@@ -5646,6 +5660,8 @@
      */
     private <T> void readListInternal(@NonNull List<? super T> outVal, int n,
             @Nullable ClassLoader loader, @Nullable Class<T> clazz) {
+        ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, n);
+        // TODO: move all size reservations here, instead of code that calls this. Not all reserves.
         while (n > 0) {
             T value = readValue(loader, clazz);
             //Log.d(TAG, "Unmarshalling value=" + value);
@@ -5665,6 +5681,7 @@
         if (n < 0) {
             return null;
         }
+        ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, n);
         ArrayList<T> l = new ArrayList<>(n);
         readListInternal(l, n, loader, clazz);
         return l;
@@ -5707,6 +5724,7 @@
      */
     private void readSparseArrayInternal(@NonNull SparseArray outVal, int N,
             @Nullable ClassLoader loader) {
+        ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, N);
         while (N > 0) {
             int key = readInt();
             Object value = readValue(loader);
@@ -5725,6 +5743,7 @@
         if (n < 0) {
             return null;
         }
+        ensureWithinMemoryLimit(SIZE_COMPLEX_TYPE, n);
         SparseArray<T> outVal = new SparseArray<>(n);
 
         while (n > 0) {
diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java
index 1801df0..2a5666c 100644
--- a/core/java/android/os/PowerManager.java
+++ b/core/java/android/os/PowerManager.java
@@ -615,6 +615,7 @@
             WAKE_REASON_WAKE_KEY,
             WAKE_REASON_WAKE_MOTION,
             WAKE_REASON_HDMI,
+            WAKE_REASON_LID,
             WAKE_REASON_DISPLAY_GROUP_ADDED,
             WAKE_REASON_DISPLAY_GROUP_TURNED_ON,
             WAKE_REASON_UNFOLD_DEVICE,
diff --git a/core/java/android/os/health/SystemHealthManager.java b/core/java/android/os/health/SystemHealthManager.java
index cd79e41..a1e9cf2 100644
--- a/core/java/android/os/health/SystemHealthManager.java
+++ b/core/java/android/os/health/SystemHealthManager.java
@@ -18,6 +18,7 @@
 
 import android.annotation.FlaggedApi;
 import android.annotation.FloatRange;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemService;
@@ -42,6 +43,8 @@
 import android.os.ResultReceiver;
 import android.os.ServiceManager;
 import android.os.SynchronousResultReceiver;
+import android.util.Pair;
+import android.util.Slog;
 
 import com.android.internal.app.IBatteryStats;
 import com.android.server.power.optimization.Flags;
@@ -69,15 +72,41 @@
  * plugged in (e.g. using {@link android.app.job.JobScheduler JobScheduler}), and
  * while that can affect charging rates, it is still preferable to actually draining
  * the battery.
+ * <p>
+ * <b>CPU/GPU Usage</b><br>
+ * CPU/GPU headroom APIs are designed to be best used by applications with consistent and intense
+ * workload such as games to query the remaining capacity headroom over a short period and perform
+ * optimization accordingly. Due to the nature of the fast job scheduling and frequency scaling of
+ * CPU and GPU, the headroom by nature will have "TOCTOU" problem which makes it less suitable for
+ * apps with inconsistent or low workload to take any useful action but simply monitoring. And to
+ * avoid oscillation it's not recommended to adjust workload too frequent (on each polling request)
+ * or too aggressively. As the headroom calculation is more based on reflecting past history usage
+ * than predicting future capacity. Take game as an example, if the API returns CPU headroom of 0 in
+ * one scenario (especially if it's constant across multiple calls), or some value significantly
+ * smaller than other scenarios, then it can reason that the recent performance result is more CPU
+ * bottlenecked. Then reducing the CPU workload intensity can help reserve some headroom to handle
+ * the load variance better, which can result in less frame drops or smooth FPS value. On the other
+ * hand, if the API returns large CPU headroom constantly, the app can be more confident to increase
+ * the workload and expect higher possibility of device meeting its performance expectation.
+ * App can also use thermal APIs to read the current thermal status and headroom first, then poll
+ * the CPU and GPU headroom if the device is (about to) getting thermal throttled. If the CPU/GPU
+ * headrooms provide enough significance such as one valued at 0 while the other at 100, then it can
+ * be used to infer that reducing CPU workload could be more efficient to cool down the device.
+ * There is a caveat that the power controller may scale down the frequency of the CPU and GPU due
+ * to thermal and other reasons, which can result in a higher than usual percentage usage of the
+ * capacity.
  */
 @SystemService(Context.SYSTEM_HEALTH_SERVICE)
 public class SystemHealthManager {
+    private static final String TAG = "SystemHealthManager";
     @NonNull
     private final IBatteryStats mBatteryStats;
     @Nullable
     private final IPowerStatsService mPowerStats;
     @Nullable
     private final IHintManager mHintManager;
+    @Nullable
+    private final IHintManager.HintManagerClientData mHintManagerClientData;
     private List<PowerMonitor> mPowerMonitorsInfo;
     private final Object mPowerMonitorsLock = new Object();
     private static final long TAKE_UID_SNAPSHOT_TIMEOUT_MILLIS = 10_000;
@@ -109,29 +138,72 @@
         mBatteryStats = batteryStats;
         mPowerStats = powerStats;
         mHintManager = hintManager;
+        IHintManager.HintManagerClientData data = null;
+        if (mHintManager != null) {
+            try {
+                data = mHintManager.getClientData();
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to get hint manager client data", e);
+            }
+        }
+        mHintManagerClientData = data;
     }
 
     /**
-     * Provides an estimate of global available CPU headroom.
+     * Provides an estimate of available CPU capacity headroom of the device.
      * <p>
+     * The value can be used by the calling application to determine if the workload was CPU bound
+     * and then take action accordingly to ensure that the workload can be completed smoothly. It
+     * can also be used with the thermal status and headroom to determine if reducing the CPU bound
+     * workload can help reduce the device temperature to avoid thermal throttling.
+     * <p>
+     * If the params are valid, each call will perform at least one synchronous binder transaction
+     * that can take more than 1ms. So it's not recommended to call or wait for this on critical
+     * threads. Some devices may implement this as an on-demand API with lazy initialization, so the
+     * caller should expect higher latency when making the first call (especially with non-default
+     * params) since app starts or after changing params, as the device may need to change its data
+     * collection.
      *
-     * @param  params params to customize the CPU headroom calculation, null to use default params.
-     * @return a single value headroom or a {@code Float.NaN} if it's temporarily unavailable.
-     *         A valid value is ranged from [0, 100], where 0 indicates no more CPU resources can be
-     *         granted.
+     * @param  params params to customize the CPU headroom calculation, or null to use default.
+     * @return a single value headroom or a {@code Float.NaN} if it's temporarily unavailable due to
+     *         server error or not enough user CPU workload.
+     *         Each valid value ranges from [0, 100], where 0 indicates no more cpu resources can be
+     *         granted
      * @throws UnsupportedOperationException if the API is unsupported.
+     * @throws IllegalArgumentException if the params are invalid.
      * @throws SecurityException if the TIDs of the params don't belong to the same process.
      * @throws IllegalStateException if the TIDs of the params don't have the same affinity setting.
      */
     @FlaggedApi(android.os.Flags.FLAG_CPU_GPU_HEADROOMS)
     public @FloatRange(from = 0f, to = 100f) float getCpuHeadroom(
             @Nullable CpuHeadroomParams params) {
-        if (mHintManager == null) {
+        if (mHintManager == null || mHintManagerClientData == null
+                || !mHintManagerClientData.supportInfo.headroom.isCpuSupported) {
             throw new UnsupportedOperationException();
         }
+        if (params != null) {
+            if (params.mInternal.tids != null && (params.mInternal.tids.length == 0
+                    || params.mInternal.tids.length
+                    > mHintManagerClientData.maxCpuHeadroomThreads)) {
+                throw new IllegalArgumentException(
+                        "Invalid number of TIDs: " + params.mInternal.tids.length);
+            }
+            if (params.mInternal.calculationWindowMillis
+                    < mHintManagerClientData.supportInfo.headroom.cpuMinCalculationWindowMillis
+                    || params.mInternal.calculationWindowMillis
+                    > mHintManagerClientData.supportInfo.headroom.cpuMaxCalculationWindowMillis) {
+                throw new IllegalArgumentException(
+                        "Invalid calculation window: "
+                        + params.mInternal.calculationWindowMillis + ", expect range: ["
+                        + mHintManagerClientData.supportInfo.headroom.cpuMinCalculationWindowMillis
+                        + ", "
+                        + mHintManagerClientData.supportInfo.headroom.cpuMaxCalculationWindowMillis
+                        + "]");
+            }
+        }
         try {
             final CpuHeadroomResult ret = mHintManager.getCpuHeadroom(
-                    params != null ? params.getInternal() : new CpuHeadroomParamsInternal());
+                    params != null ? params.mInternal : new CpuHeadroomParamsInternal());
             if (ret == null || ret.getTag() != CpuHeadroomResult.globalHeadroom) {
                 return Float.NaN;
             }
@@ -141,27 +213,69 @@
         }
     }
 
-
+    /**
+     * Gets the maximum number of TIDs this device supports for getting CPU headroom.
+     * <p>
+     * See {@link CpuHeadroomParams#setTids(int...)}.
+     *
+     * @return the maximum size of TIDs supported
+     * @throws UnsupportedOperationException if the CPU headroom API is unsupported.
+     */
+    @FlaggedApi(android.os.Flags.FLAG_CPU_GPU_HEADROOMS)
+    public @IntRange(from = 1) int getMaxCpuHeadroomTidsSize() {
+        if (mHintManager == null || mHintManagerClientData == null
+                || !mHintManagerClientData.supportInfo.headroom.isCpuSupported) {
+            throw new UnsupportedOperationException();
+        }
+        return mHintManagerClientData.maxCpuHeadroomThreads;
+    }
 
     /**
-     * Provides an estimate of global available GPU headroom of the device.
+     * Provides an estimate of available GPU capacity headroom of the device.
      * <p>
+     * The value can be used by the calling application to determine if the workload was GPU bound
+     * and then take action accordingly to ensure that the workload can be completed smoothly. It
+     * can also be used with the thermal status and headroom to determine if reducing the GPU bound
+     * workload can help reduce the device temperature to avoid thermal throttling.
+     * <p>
+     * If the params are valid, each call will perform at least one synchronous binder transaction
+     * that can take more than 1ms. So it's not recommended to call or wait for this on critical
+     * threads. Some devices may implement this as an on-demand API with lazy initialization, so the
+     * caller should expect higher latency when making the first call (especially with non-default
+     * params) since app starts or after changing params, as the device may need to change its data
+     * collection.
      *
-     * @param  params params to customize the GPU headroom calculation, null to use default params.
+     * @param  params params to customize the GPU headroom calculation, or null to use default.
      * @return a single value headroom or a {@code Float.NaN} if it's temporarily unavailable.
-     *         A valid value is ranged from [0, 100], where 0 indicates no more GPU resources can be
+     *         Each valid value ranges from [0, 100], where 0 indicates no more cpu resources can be
      *         granted.
      * @throws UnsupportedOperationException if the API is unsupported.
+     * @throws IllegalArgumentException if the params are invalid.
      */
     @FlaggedApi(android.os.Flags.FLAG_CPU_GPU_HEADROOMS)
     public @FloatRange(from = 0f, to = 100f) float getGpuHeadroom(
             @Nullable GpuHeadroomParams params) {
-        if (mHintManager == null) {
+        if (mHintManager == null || mHintManagerClientData == null
+                || !mHintManagerClientData.supportInfo.headroom.isGpuSupported) {
             throw new UnsupportedOperationException();
         }
+        if (params != null) {
+            if (params.mInternal.calculationWindowMillis
+                    < mHintManagerClientData.supportInfo.headroom.gpuMinCalculationWindowMillis
+                    || params.mInternal.calculationWindowMillis
+                    > mHintManagerClientData.supportInfo.headroom.gpuMaxCalculationWindowMillis) {
+                throw new IllegalArgumentException(
+                        "Invalid calculation window: "
+                        + params.mInternal.calculationWindowMillis + ", expect range: ["
+                        + mHintManagerClientData.supportInfo.headroom.gpuMinCalculationWindowMillis
+                        + ", "
+                        + mHintManagerClientData.supportInfo.headroom.gpuMaxCalculationWindowMillis
+                        + "]");
+            }
+        }
         try {
             final GpuHeadroomResult ret = mHintManager.getGpuHeadroom(
-                    params != null ? params.getInternal() : new GpuHeadroomParamsInternal());
+                    params != null ? params.mInternal : new GpuHeadroomParamsInternal());
             if (ret == null || ret.getTag() != GpuHeadroomResult.globalHeadroom) {
                 return Float.NaN;
             }
@@ -172,7 +286,51 @@
     }
 
     /**
-     * Minimum polling interval for calling {@link #getCpuHeadroom(CpuHeadroomParams)} in
+     * Gets the range of the calculation window size for CPU headroom.
+     * <p>
+     * In API version 36, the range will be a superset of [50, 10000].
+     * <p>
+     * See {@link CpuHeadroomParams#setCalculationWindowMillis(int)}.
+     *
+     * @return the range of the calculation window size supported in milliseconds.
+     * @throws UnsupportedOperationException if the CPU headroom API is unsupported.
+     */
+    @FlaggedApi(android.os.Flags.FLAG_CPU_GPU_HEADROOMS)
+    @NonNull
+    public Pair<Integer, Integer> getCpuHeadroomCalculationWindowRange() {
+        if (mHintManager == null || mHintManagerClientData == null
+                || !mHintManagerClientData.supportInfo.headroom.isCpuSupported) {
+            throw new UnsupportedOperationException();
+        }
+        return new Pair<>(
+                mHintManagerClientData.supportInfo.headroom.cpuMinCalculationWindowMillis,
+                mHintManagerClientData.supportInfo.headroom.cpuMaxCalculationWindowMillis);
+    }
+
+    /**
+     * Gets the range of the calculation window size for GPU headroom.
+     * <p>
+     * In API version 36, the range will be a superset of [50, 10000].
+     * <p>
+     * See {@link GpuHeadroomParams#setCalculationWindowMillis(int)}.
+     *
+     * @return the range of the calculation window size supported in milliseconds.
+     * @throws UnsupportedOperationException if the GPU headroom API is unsupported.
+     */
+    @FlaggedApi(android.os.Flags.FLAG_CPU_GPU_HEADROOMS)
+    @NonNull
+    public Pair<Integer, Integer> getGpuHeadroomCalculationWindowRange() {
+        if (mHintManager == null || mHintManagerClientData == null
+                || !mHintManagerClientData.supportInfo.headroom.isGpuSupported) {
+            throw new UnsupportedOperationException();
+        }
+        return new Pair<>(
+                mHintManagerClientData.supportInfo.headroom.gpuMinCalculationWindowMillis,
+                mHintManagerClientData.supportInfo.headroom.gpuMaxCalculationWindowMillis);
+    }
+
+    /**
+     * Gets minimum polling interval for calling {@link #getCpuHeadroom(CpuHeadroomParams)} in
      * milliseconds.
      * <p>
      * The {@link #getCpuHeadroom(CpuHeadroomParams)} API may return cached result if called more
@@ -182,7 +340,8 @@
      */
     @FlaggedApi(android.os.Flags.FLAG_CPU_GPU_HEADROOMS)
     public long getCpuHeadroomMinIntervalMillis() {
-        if (mHintManager == null) {
+        if (mHintManager == null || mHintManagerClientData == null
+                || !mHintManagerClientData.supportInfo.headroom.isCpuSupported) {
             throw new UnsupportedOperationException();
         }
         try {
@@ -193,7 +352,7 @@
     }
 
     /**
-     * Minimum polling interval for calling {@link #getGpuHeadroom(GpuHeadroomParams)} in
+     * Gets minimum polling interval for calling {@link #getGpuHeadroom(GpuHeadroomParams)} in
      * milliseconds.
      * <p>
      * The {@link #getGpuHeadroom(GpuHeadroomParams)} API may return cached result if called more
@@ -203,7 +362,8 @@
      */
     @FlaggedApi(android.os.Flags.FLAG_CPU_GPU_HEADROOMS)
     public long getGpuHeadroomMinIntervalMillis() {
-        if (mHintManager == null) {
+        if (mHintManager == null || mHintManagerClientData == null
+                || !mHintManagerClientData.supportInfo.headroom.isGpuSupported) {
             throw new UnsupportedOperationException();
         }
         try {
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index bdf8d23..343d752 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -1716,20 +1716,14 @@
 
     private static int checkPermissionUncached(@Nullable String permission, int pid, int uid,
             int deviceId) {
+        final int appId = UserHandle.getAppId(uid);
+        if (appId == Process.ROOT_UID || appId == Process.SYSTEM_UID) {
+            return PackageManager.PERMISSION_GRANTED;
+        }
         final IActivityManager am = ActivityManager.getService();
         if (am == null) {
-            // Well this is super awkward; we somehow don't have an active ActivityManager
-            // instance. If we're testing a root or system UID, then they totally have whatever
-            // permission this is.
-            final int appId = UserHandle.getAppId(uid);
-            if (appId == Process.ROOT_UID || appId == Process.SYSTEM_UID) {
-                if (sShouldWarnMissingActivityManager) {
-                    Slog.w(LOG_TAG, "Missing ActivityManager; assuming " + uid + " holds "
-                            + permission);
-                    sShouldWarnMissingActivityManager = false;
-                }
-                return PackageManager.PERMISSION_GRANTED;
-            }
+            // We don't have an active ActivityManager instance and the calling UID is not root or
+            // system, so we don't grant this permission.
             Slog.w(LOG_TAG, "Missing ActivityManager; assuming " + uid + " does not hold "
                     + permission);
             return PackageManager.PERMISSION_DENIED;
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 2ad6669..11dddfb 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -1256,12 +1256,12 @@
     /**
      * Activity Action: Show numbering system configuration settings.
      * <p>
-     * In some cases, a matching Activity may not exist, so ensure you
-     * safeguard against this.
-     * <p>
      * Input: Nothing.
      * <p>
-     * Output: Nothing.
+     * Output: After calling {@link android.app.Activity#startActivityForResult}, the callback
+     * {@code onActivityResult} will have resultCode {@link android.app.Activity#RESULT_OK} if
+     * the numbering system settings page is suitable to show on the UI. Otherwise, the result is
+     * set to {@link android.app.Activity#RESULT_CANCELED}.
      */
     @FlaggedApi(Flags.FLAG_SYSTEM_REGIONAL_PREFERENCES_API_ENABLED)
     @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
@@ -6362,6 +6362,29 @@
         public static final String MOUSE_SCROLLING_ACCELERATION = "mouse_scrolling_acceleration";
 
         /**
+         * Whether mouse acceleration is enabled.
+         *
+         * When enabled, the mouse cursor will accelerate as the mouse moves faster.
+         *
+         * @hide
+         */
+        public static final String MOUSE_POINTER_ACCELERATION_ENABLED =
+                "mouse_pointer_acceleration_enabled";
+
+        /**
+         * Mouse scrolling speed setting.
+         *
+         * This is an integer value in a range between -7 and +7, so there are 15 possible values.
+         * The setting only applies when mouse scrolling acceleration is not enabled.
+         *   -7 = slowest
+         *    0 = default speed
+         *   +7 = fastest
+         *
+         * @hide
+         */
+        public static final String MOUSE_SCROLLING_SPEED = "mouse_scrolling_speed";
+
+        /**
          * Pointer fill style, specified by
          * {@link android.view.PointerIcon.PointerIconVectorStyleFill} constants.
          *
@@ -6610,8 +6633,10 @@
             PRIVATE_SETTINGS.add(DEFAULT_DEVICE_FONT_SCALE);
             PRIVATE_SETTINGS.add(MOUSE_REVERSE_VERTICAL_SCROLLING);
             PRIVATE_SETTINGS.add(MOUSE_SWAP_PRIMARY_BUTTON);
+            PRIVATE_SETTINGS.add(MOUSE_POINTER_ACCELERATION_ENABLED);
             PRIVATE_SETTINGS.add(PREFERRED_REGION);
             PRIVATE_SETTINGS.add(MOUSE_SCROLLING_ACCELERATION);
+            PRIVATE_SETTINGS.add(MOUSE_SCROLLING_SPEED);
         }
 
         /**
@@ -9294,6 +9319,16 @@
                 "accessibility_autoclick_delay";
 
         /**
+         * Integer setting specifying the autoclick cursor area size (the radius of the autoclick
+         * ring indicator) when {@link #ACCESSIBILITY_AUTOCLICK_ENABLED} is set.
+         *
+         * @see #ACCESSIBILITY_AUTOCLICK_ENABLED
+         * @hide
+         */
+        public static final String ACCESSIBILITY_AUTOCLICK_CURSOR_AREA_SIZE =
+                "accessibility_autoclick_cursor_area_size";
+
+        /**
          * Whether or not larger size icons are used for the pointer of mouse/trackpad for
          * accessibility.
          * (0 = false, 1 = true)
@@ -12212,49 +12247,6 @@
                 "back_gesture_inset_scale_right";
 
         /**
-         * Indicates whether the trackpad back gesture is enabled.
-         * <p>Type: int (0 for false, 1 for true)
-         *
-         * @hide
-         */
-        public static final String TRACKPAD_GESTURE_BACK_ENABLED = "trackpad_gesture_back_enabled";
-
-        /**
-         * Indicates whether the trackpad home gesture is enabled.
-         * <p>Type: int (0 for false, 1 for true)
-         *
-         * @hide
-         */
-        public static final String TRACKPAD_GESTURE_HOME_ENABLED = "trackpad_gesture_home_enabled";
-
-        /**
-         * Indicates whether the trackpad overview gesture is enabled.
-         * <p>Type: int (0 for false, 1 for true)
-         *
-         * @hide
-         */
-        public static final String TRACKPAD_GESTURE_OVERVIEW_ENABLED =
-                "trackpad_gesture_overview_enabled";
-
-        /**
-         * Indicates whether the trackpad notification gesture is enabled.
-         * <p>Type: int (0 for false, 1 for true)
-         *
-         * @hide
-         */
-        public static final String TRACKPAD_GESTURE_NOTIFICATION_ENABLED =
-                "trackpad_gesture_notification_enabled";
-
-        /**
-         * Indicates whether the trackpad quick switch gesture is enabled.
-         * <p>Type: int (0 for false, 1 for true)
-         *
-         * @hide
-         */
-        public static final String TRACKPAD_GESTURE_QUICK_SWITCH_ENABLED =
-                "trackpad_gesture_quick_switch_enabled";
-
-        /**
          * Current provider of proximity-based sharing services.
          * Default value in @string/config_defaultNearbySharingComponent.
          * No VALIDATOR as this setting will not be backed up.
@@ -13336,6 +13328,16 @@
         public static final String AUTO_TIME_ZONE_EXPLICIT = "auto_time_zone_explicit";
 
         /**
+         * Value to specify if the device should send notifications when {@link #AUTO_TIME_ZONE} is
+         * on and the device's time zone changes.
+         *
+         * <p>1=yes, 0=no.
+         *
+         * @hide
+         */
+        public static final String TIME_ZONE_NOTIFICATIONS = "time_zone_notifications";
+
+        /**
          * URI for the car dock "in" event sound.
          * @hide
          */
@@ -17427,13 +17429,6 @@
 
 
         /**
-         * Whether back preview animations are played when user does a back gesture or presses
-         * the back button.
-         * @hide
-         */
-        public static final String ENABLE_BACK_ANIMATION = "enable_back_animation";
-
-        /**
          * An allow list of packages for which the user has granted the permission to communicate
          * across profiles.
          *
diff --git a/core/java/android/service/quickaccesswallet/flags.aconfig b/core/java/android/service/quickaccesswallet/flags.aconfig
index 9736345..2b40f34 100644
--- a/core/java/android/service/quickaccesswallet/flags.aconfig
+++ b/core/java/android/service/quickaccesswallet/flags.aconfig
@@ -14,4 +14,14 @@
     namespace: "wallet_integration"
     description: "When the wallet QS tile is tapped, launch the selected card pending intent instead of the home screen pending intent."
     bug: "378469025"
+}
+
+flag {
+    name: "launch_wallet_via_sysui_callbacks"
+    namespace: "wallet_integration"
+    description: "Refactor Wallet double press power launch to be handled by SysUI instead of core. This allows handling dismissing the keyguard before launch."
+    bug: "384938619"
+    metadata {
+    purpose: PURPOSE_BUGFIX
+    }
 }
\ No newline at end of file
diff --git a/core/java/android/telephony/TelephonyCallback.java b/core/java/android/telephony/TelephonyCallback.java
index e8b32ce..d05fa9e 100644
--- a/core/java/android/telephony/TelephonyCallback.java
+++ b/core/java/android/telephony/TelephonyCallback.java
@@ -1009,7 +1009,7 @@
      */
     public interface CellInfoListener {
         /**
-         * Callback invoked when a observed cell info has changed or new cells have been added
+         * Callback invoked when an observed cell info has changed or new cells have been added
          * or removed on the registered subscription.
          * Note, the registration subscription ID s from {@link TelephonyManager} object
          * which registers TelephonyCallback by
@@ -1819,7 +1819,7 @@
      */
     @SystemApi
     @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
-    public interface CarrierRoamingNtnModeListener {
+    public interface CarrierRoamingNtnListener {
         /**
          * Callback invoked when carrier roaming non-terrestrial network mode changes.
          *
@@ -2332,8 +2332,8 @@
         public void onCarrierRoamingNtnModeChanged(boolean active) {
             if (!Flags.carrierEnabledSatelliteFlag()) return;
 
-            CarrierRoamingNtnModeListener listener =
-                    (CarrierRoamingNtnModeListener) mTelephonyCallbackWeakRef.get();
+            CarrierRoamingNtnListener listener =
+                    (CarrierRoamingNtnListener) mTelephonyCallbackWeakRef.get();
             if (listener == null) return;
 
             Binder.withCleanCallingIdentity(
@@ -2343,8 +2343,8 @@
         public void onCarrierRoamingNtnEligibleStateChanged(boolean eligible) {
             if (!Flags.carrierRoamingNbIotNtn()) return;
 
-            CarrierRoamingNtnModeListener listener =
-                    (CarrierRoamingNtnModeListener) mTelephonyCallbackWeakRef.get();
+            CarrierRoamingNtnListener listener =
+                    (CarrierRoamingNtnListener) mTelephonyCallbackWeakRef.get();
             if (listener == null) return;
 
             Binder.withCleanCallingIdentity(() -> mExecutor.execute(
@@ -2355,8 +2355,8 @@
                 @NetworkRegistrationInfo.ServiceType int[] availableServices) {
             if (!Flags.carrierRoamingNbIotNtn()) return;
 
-            CarrierRoamingNtnModeListener listener =
-                    (CarrierRoamingNtnModeListener) mTelephonyCallbackWeakRef.get();
+            CarrierRoamingNtnListener listener =
+                    (CarrierRoamingNtnListener) mTelephonyCallbackWeakRef.get();
             if (listener == null) return;
 
             Binder.withCleanCallingIdentity(() -> mExecutor.execute(
@@ -2367,8 +2367,8 @@
                 @NonNull NtnSignalStrength ntnSignalStrength) {
             if (!Flags.carrierRoamingNbIotNtn()) return;
 
-            CarrierRoamingNtnModeListener listener =
-                    (CarrierRoamingNtnModeListener) mTelephonyCallbackWeakRef.get();
+            CarrierRoamingNtnListener listener =
+                    (CarrierRoamingNtnListener) mTelephonyCallbackWeakRef.get();
             if (listener == null) return;
 
             Binder.withCleanCallingIdentity(() -> mExecutor.execute(
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index 4ec429d..fa44fcf 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -1341,7 +1341,7 @@
                     TelephonyCallback.EVENT_SIMULTANEOUS_CELLULAR_CALLING_SUBSCRIPTIONS_CHANGED);
         }
 
-        if (telephonyCallback instanceof TelephonyCallback.CarrierRoamingNtnModeListener) {
+        if (telephonyCallback instanceof TelephonyCallback.CarrierRoamingNtnListener) {
             eventList.add(TelephonyCallback.EVENT_CARRIER_ROAMING_NTN_MODE_CHANGED);
             eventList.add(TelephonyCallback.EVENT_CARRIER_ROAMING_NTN_ELIGIBLE_STATE_CHANGED);
             eventList.add(TelephonyCallback.EVENT_CARRIER_ROAMING_NTN_AVAILABLE_SERVICES_CHANGED);
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index e254bf3..d53b98c 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -77,7 +77,7 @@
     private static final float HIGH_CONTRAST_TEXT_BORDER_WIDTH_FACTOR = 0f;
     private static final float HIGH_CONTRAST_TEXT_BACKGROUND_CORNER_RADIUS_DP = 5f;
     // since we're not using soft light yet, this needs to be much lower than the spec'd 0.8
-    private static final float HIGH_CONTRAST_TEXT_BACKGROUND_ALPHA_PERCENTAGE = 0.5f;
+    private static final float HIGH_CONTRAST_TEXT_BACKGROUND_ALPHA_PERCENTAGE = 0.7f;
 
     /** @hide */
     @IntDef(prefix = { "BREAK_STRATEGY_" }, value = {
diff --git a/core/java/android/util/AtomicFileBufferedOutputStream.java b/core/java/android/util/AtomicFileBufferedOutputStream.java
new file mode 100644
index 0000000..659ac07
--- /dev/null
+++ b/core/java/android/util/AtomicFileBufferedOutputStream.java
@@ -0,0 +1,77 @@
+/*
+ * 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.util;
+
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+
+/**
+ * {@link BufferedOutputStream} for {@link AtomicFile}.
+ * Allows user-code to write into file output stream backed by {@link AtomicFile}.
+ * In order to "commit" the new content to the file, call {@link #markSuccess()} then
+ * {@link #close()}. Calling{@link #markSuccess()} alone won't update the file.
+ * This class does not confer any file locking semantics. Do not use this class when the file may be
+ * accessed or modified concurrently by multiple threads or processes. The caller is responsible for
+ * ensuring appropriate mutual exclusion invariants whenever it accesses the file.
+ * @hide
+ */
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
+public class AtomicFileBufferedOutputStream extends BufferedOutputStream implements AutoCloseable {
+    private static final String TAG = "AtomicFileBufferedOutputStream";
+    private final AtomicFileOutputStream mAtomicFileOutputStream;
+
+    /**
+     * See {@link AtomicFileOutputStream#AtomicFileOutputStream(AtomicFile)}.
+     */
+    public AtomicFileBufferedOutputStream(AtomicFile file) throws IOException {
+        this(new AtomicFileOutputStream(file));
+    }
+
+    private AtomicFileBufferedOutputStream(AtomicFileOutputStream atomicFileOutputStream) {
+        super(atomicFileOutputStream);
+        mAtomicFileOutputStream = atomicFileOutputStream;
+    }
+
+    /**
+     * See {@link AtomicFile#startWrite()} with specific buffer size.
+     */
+    public AtomicFileBufferedOutputStream(AtomicFile file, int bufferSize) throws IOException {
+        this(new AtomicFileOutputStream(file), bufferSize);
+    }
+
+    private AtomicFileBufferedOutputStream(AtomicFileOutputStream atomicFileOutputStream,
+            int bufferSize) {
+        super(atomicFileOutputStream, bufferSize);
+        mAtomicFileOutputStream = atomicFileOutputStream;
+    }
+
+    /**
+     * Flushes output stream and marks the writing as finished.
+     */
+    public void markSuccess() throws IOException {
+        flush();
+        mAtomicFileOutputStream.markSuccess();
+    }
+
+    /**
+     * Creates string representation of the object.
+     */
+    @Override
+    public String toString() {
+        return TAG + "[" + mAtomicFileOutputStream + "]";
+    }
+}
diff --git a/core/java/android/util/AtomicFileBufferedPrintWriter.java b/core/java/android/util/AtomicFileBufferedPrintWriter.java
new file mode 100644
index 0000000..c28b599
--- /dev/null
+++ b/core/java/android/util/AtomicFileBufferedPrintWriter.java
@@ -0,0 +1,88 @@
+/*
+ * 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.util;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.nio.charset.Charset;
+
+
+/**
+ * Buffered {@link PrintWriter} for {@link AtomicFile}.
+ * In order to "commit" the new content to the file, call {@link #markSuccess()} then
+ * {@link #close()}. Calling{@link #markSuccess()} alone won't update the file.
+ * This class does not confer any file locking semantics. Do not use this class when the file may be
+ * accessed or modified concurrently by multiple threads or processes. The caller is responsible for
+ * ensuring appropriate mutual exclusion invariants whenever it accesses the file.
+ * @hide
+ */
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
+public class AtomicFileBufferedPrintWriter extends PrintWriter {
+    private final AtomicFileOutputStream mAtomicFileOutStream;
+
+    /**
+     * Construct from {@link AtomicFile} with {@link BufferedWriter} default buffer size.
+     */
+    public AtomicFileBufferedPrintWriter(AtomicFile atomicFile, Charset charset)
+            throws IOException {
+        this(new AtomicFileOutputStream(atomicFile), charset);
+    }
+
+    /**
+     * Create from {@link AtomicFileOutputStream} with {@link BufferedWriter} default buffer size.
+     */
+    public AtomicFileBufferedPrintWriter(AtomicFileOutputStream outStream, Charset charset) {
+        super(new BufferedWriter(new OutputStreamWriter(outStream, charset)));
+        mAtomicFileOutStream = outStream;
+    }
+
+    /**
+     * Construct from {@link AtomicFile} with the specific buffer size.
+     */
+    public AtomicFileBufferedPrintWriter(AtomicFile atomicFile, Charset charset, int bufferSize)
+            throws IOException {
+        this(new AtomicFileOutputStream(atomicFile), charset, bufferSize);
+    }
+
+    /**
+     * Construct from {@link AtomicFileOutputStream} with the specific buffer size.
+     */
+    public AtomicFileBufferedPrintWriter(AtomicFileOutputStream outStream, Charset charset,
+            int bufferSize) {
+        super(new BufferedWriter(new OutputStreamWriter(outStream, charset), bufferSize));
+        mAtomicFileOutStream = outStream;
+    }
+
+    /**
+     * When write is successful this needs to be called to flush the buffer and mark the writing as
+     * successful.
+     */
+    public void markSuccess() throws IOException {
+        flush();
+        mAtomicFileOutStream.markSuccess();
+    }
+
+    /**
+     * Creates string representation of the object.
+     */
+    @Override
+    public String toString() {
+        return "AtomicFileBufferedPrintWriter[" + mAtomicFileOutStream + "]";
+    }
+}
diff --git a/core/java/android/util/AtomicFileOutputStream.java b/core/java/android/util/AtomicFileOutputStream.java
new file mode 100644
index 0000000..dbc3ad2
--- /dev/null
+++ b/core/java/android/util/AtomicFileOutputStream.java
@@ -0,0 +1,100 @@
+/*
+ * 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.util;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+/**
+ * {@link FileOutputStream} for {@link AtomicFile}.
+ * Allows user-code to write into file output stream backed by {@link AtomicFile}.
+ * In order to "commit" the new content to the file, call {@link #markSuccess()} then
+ * {@link #close()}. Calling{@link #markSuccess()} alone won't update the file.
+ * This class does not confer any file locking semantics. Do not use this class when the file may be
+ * accessed or modified concurrently by multiple threads or processes. The caller is responsible for
+ * ensuring appropriate mutual exclusion invariants whenever it accesses the file.
+ * @hide
+ */
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
+public class AtomicFileOutputStream extends FileOutputStream implements AutoCloseable {
+    private static final String TAG = "AtomicFileOutputStream";
+    private final AtomicFile mFile;
+    private final FileOutputStream mOutStream;
+    private boolean mWritingSuccessful;
+    private boolean mClosed;
+
+    /**
+     * See {@link AtomicFile#startWrite()}.
+     */
+    public AtomicFileOutputStream(AtomicFile file) throws IOException {
+        this(file, file.startWrite());
+    }
+
+    private AtomicFileOutputStream(AtomicFile file, FileOutputStream oStream) throws IOException {
+        super(oStream.getFD());
+        mFile = file;
+        mOutStream = oStream;
+    }
+
+    /**
+     * Marks the writing as successful.
+     */
+    public void markSuccess() {
+        if (mWritingSuccessful) {
+            throw new IllegalStateException(TAG + " success is already marked");
+        }
+        mWritingSuccessful = true;
+    }
+
+    /**
+     * Finishes writing to {@link #mFile}, see {@link AtomicFile#finishWrite(FileOutputStream)}
+     * and {@link AtomicFile#failWrite(FileOutputStream)}. Closes {@link #mOutStream} which
+     * is the owner of the file descriptor.
+     */
+    @Override
+    public void close() throws IOException {
+        super.close();
+        synchronized (mOutStream) {
+            if (mClosed) {
+                // FileOutputStream#finalize() may call this #close() method.
+                // We don't want to throw exceptions in this case.
+                // CloseGuard#warnIfOpen() also called there, so no need to log warnings in
+                // AtomicFileOutputStream#finalize().
+                return;
+            }
+            mClosed = true;
+        }
+
+        if (mWritingSuccessful) {
+            mFile.finishWrite(mOutStream);
+        } else {
+            mFile.failWrite(mOutStream);
+        }
+    }
+
+    /**
+     * Creates string representation of the object.
+     */
+    @Override
+    public String toString() {
+        return TAG + "["
+                + "mFile=" + mFile
+                + ", mWritingSuccessful=" + mWritingSuccessful
+                + ", mClosed=" + mClosed
+                + "]";
+    }
+}
diff --git a/core/java/android/util/AtomicFilePrintWriter.java b/core/java/android/util/AtomicFilePrintWriter.java
new file mode 100644
index 0000000..f8fde18
--- /dev/null
+++ b/core/java/android/util/AtomicFilePrintWriter.java
@@ -0,0 +1,71 @@
+/*
+ * 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.util;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.nio.charset.Charset;
+
+
+/**
+ * {@link PrintWriter} for {@link AtomicFile}
+ * In order to "commit" the new content to the file, call {@link #markSuccess()} then
+ * {@link #close()}. Calling{@link #markSuccess()} alone won't update the file.
+ * This class does not confer any file locking semantics. Do not use this class when the file may be
+ * accessed or modified concurrently by multiple threads or processes. The caller is responsible for
+ * ensuring appropriate mutual exclusion invariants whenever it accesses the file.
+ * @hide
+ */
+@android.ravenwood.annotation.RavenwoodKeepWholeClass
+public class AtomicFilePrintWriter extends PrintWriter {
+    private final AtomicFileOutputStream mAtomicFileOutStream;
+
+    /**
+     * Construct from {@link AtomicFile} with {@link BufferedWriter} default buffer size.
+     */
+    public AtomicFilePrintWriter(AtomicFile atomicFile, Charset charset)
+            throws IOException {
+        this(new AtomicFileOutputStream(atomicFile), charset);
+    }
+
+    /**
+     * Create from {@link AtomicFileOutputStream}.
+     */
+    public AtomicFilePrintWriter(AtomicFileOutputStream outStream, Charset charset) {
+        super(new OutputStreamWriter(outStream, charset));
+        mAtomicFileOutStream = outStream;
+    }
+
+    /**
+     * When write is successful this needs to be called to flush the buffer and mark the writing as
+     * successful.
+     */
+    public void markSuccess() throws IOException {
+        flush();
+        mAtomicFileOutStream.markSuccess();
+    }
+
+    /**
+     * Creates string representation of the object.
+     */
+    @Override
+    public String toString() {
+        return "AtomicFilePrintWriter[" + mAtomicFileOutStream + "]";
+    }
+}
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index d8a88b8..d313fc9 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -100,13 +100,6 @@
     public static final String SETTINGS_NEW_KEYBOARD_TRACKPAD = "settings_new_keyboard_trackpad";
 
     /**
-     * Enable trackpad gesture settings UI
-     * @hide
-     */
-    public static final String SETTINGS_NEW_KEYBOARD_TRACKPAD_GESTURE =
-            "settings_new_keyboard_trackpad_gesture";
-
-    /**
      * Enable the new pages which is implemented with SPA.
      * @hide
      */
@@ -211,7 +204,6 @@
         DEFAULT_FLAGS.put(SETTINGS_NEED_CONNECTED_BLE_DEVICE_FOR_BROADCAST, "true");
         DEFAULT_FLAGS.put(SETTINGS_NEW_KEYBOARD_MODIFIER_KEY, "true");
         DEFAULT_FLAGS.put(SETTINGS_NEW_KEYBOARD_TRACKPAD, "true");
-        DEFAULT_FLAGS.put(SETTINGS_NEW_KEYBOARD_TRACKPAD_GESTURE, "false");
         DEFAULT_FLAGS.put(SETTINGS_ENABLE_SPA, "true");
         DEFAULT_FLAGS.put(SETTINGS_ENABLE_SPA_PHASE2, "false");
         DEFAULT_FLAGS.put(SETTINGS_ENABLE_SPA_METRICS, "true");
@@ -237,7 +229,6 @@
         PERSISTENT_FLAGS.add(SETTINGS_APP_ALLOW_DARK_THEME_ACTIVATION_AT_BEDTIME);
         PERSISTENT_FLAGS.add(SETTINGS_NEW_KEYBOARD_MODIFIER_KEY);
         PERSISTENT_FLAGS.add(SETTINGS_NEW_KEYBOARD_TRACKPAD);
-        PERSISTENT_FLAGS.add(SETTINGS_NEW_KEYBOARD_TRACKPAD_GESTURE);
         PERSISTENT_FLAGS.add(SETTINGS_ENABLE_SPA);
         PERSISTENT_FLAGS.add(SETTINGS_ENABLE_SPA_PHASE2);
         PERSISTENT_FLAGS.add(SETTINGS_PREFER_ACCESSIBILITY_MENU_IN_SYSTEM);
diff --git a/core/java/android/util/SystemPropertySetter.java b/core/java/android/util/SystemPropertySetter.java
new file mode 100644
index 0000000..bf18f75
--- /dev/null
+++ b/core/java/android/util/SystemPropertySetter.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.SystemProperties;
+
+/**
+ * This class provides a single, static function to set a system property.  The function retries on
+ * error.  System properties should be reliable but there have been reports of failures in the set()
+ * command on lower-end devices.  Clients may want to use this method instead of calling
+ * {@link SystemProperties.set} directly.
+ * @hide
+ */
+public class SystemPropertySetter {
+
+    /**
+     * The default retryDelayMs for {@link #setWithRetry}.  This value has been found to give
+     * reasonable behavior in the field.
+     */
+    public static final int PROPERTY_FAILURE_RETRY_DELAY_MILLIS = 200;
+
+    /**
+     * The default retryLimit for {@link #setWithRetry}.  This value has been found to give
+     * reasonable behavior in the field.
+     */
+    public static final int PROPERTY_FAILURE_RETRY_LIMIT = 5;
+
+    /**
+     * Set the value for the given {@code key} to {@code val}.  This method retries using the
+     * standard parameters, above, if the native method throws a RuntimeException.
+     *
+     * @param key The name of the property to be set.
+     * @param val The new string value of the property.
+     * @throws IllegalArgumentException for non read-only properties if the {@code val} exceeds
+     * 91 characters.
+     * @throws RuntimeException if the property cannot be set, for example, if it was blocked by
+     * SELinux. libc will log the underlying reason.
+     */
+    public static void setWithRetry(@NonNull String key, @Nullable String val) {
+        setWithRetry(key, val,PROPERTY_FAILURE_RETRY_DELAY_MILLIS, PROPERTY_FAILURE_RETRY_LIMIT);
+    }
+
+    /**
+     * Set the value for the given {@code key} to {@code val}.  This method retries if the native
+     * method throws a RuntimeException.  If the {@code maxRetry} count is exceeded, the method
+     * throws the first RuntimeException that was seen.
+     *
+     * @param key The name of the property to be set.
+     * @param val The new string value of the property.
+     * @param maxRetry The maximum number of times; must be non-negative.
+     * @param retryDelayMs The number of milliseconds to wait between retries; must be positive.
+     * @throws IllegalArgumentException for non read-only properties if the {@code val} exceeds
+     * 91 characters, or if the retry parameters are invalid.
+     * @throws RuntimeException if the property cannot be set, for example, if it was blocked by
+     * SELinux. libc will log the underlying reason.
+     */
+    public static void setWithRetry(@NonNull String key, @Nullable String val, int maxRetry,
+            long retryDelayMs) {
+        if (maxRetry < 0) {
+            throw new IllegalArgumentException("invalid retry count: " + maxRetry);
+        }
+        if (retryDelayMs <= 0) {
+            throw new IllegalArgumentException("invalid retry delay: " + retryDelayMs);
+        }
+
+        RuntimeException failure = null;
+        for (int attempt = 0; attempt < maxRetry; attempt++) {
+            try {
+                SystemProperties.set(key, val);
+                return;
+            } catch (RuntimeException e) {
+                if (failure == null) {
+                    failure = e;
+                }
+                try {
+                    Thread.sleep(retryDelayMs);
+                } catch (InterruptedException x) {
+                    // Ignore this exception.  The desired delay is only approximate and
+                    // there is no issue if the sleep sometimes terminates early.
+                }
+            }
+        }
+        // This point is reached only if SystemProperties.set() fails at least once.
+        // Rethrow the first exception that was received.
+        throw failure;
+    }
+}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 2b40874..0280433 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -70,6 +70,7 @@
 import android.view.displayhash.DisplayHash;
 import android.view.displayhash.VerifiedDisplayHash;
 import android.window.AddToSurfaceSyncGroupResult;
+import android.window.ConfigurationChangeSetting;
 import android.window.IGlobalDragListener;
 import android.window.IScreenRecordingCallback;
 import android.window.ISurfaceSyncGroupCompletedListener;
@@ -136,6 +137,24 @@
     void setForcedDisplayDensityForUser(int displayId, int density, int userId);
     @EnforcePermission("WRITE_SECURE_SETTINGS")
     void clearForcedDisplayDensityForUser(int displayId, int userId);
+
+    /**
+     * Sets settings for a specific user in a batch to minimize configuration updates.
+     *
+     * <p>This method allows for applying multiple settings changes as a batch, which can
+     * help avoid multiple configuration updates.
+     *
+     * @param settings list of {@link android.window.ConfigurationChangeSetting} objects
+     *                 representing the settings to be applied.
+     * @param userId   the ID of the user whose settings should be applied.
+     * @throws SecurityException if the caller does not have the {@link WRITE_SECURE_SETTINGS}
+     *                           permission.
+     * @hide
+     */
+    @EnforcePermission("WRITE_SECURE_SETTINGS")
+    void setConfigurationChangeSettingsForUser(
+            in List<ConfigurationChangeSetting> settings, int userId);
+
     @EnforcePermission("WRITE_SECURE_SETTINGS")
     void setForcedDisplayScalingMode(int displayId, int mode); // 0 = auto, 1 = disable
 
diff --git a/core/java/android/view/ImeInsetsSourceConsumer.java b/core/java/android/view/ImeInsetsSourceConsumer.java
index 4f74198..880622a 100644
--- a/core/java/android/view/ImeInsetsSourceConsumer.java
+++ b/core/java/android/view/ImeInsetsSourceConsumer.java
@@ -221,13 +221,13 @@
 
     @Override
     public boolean setControl(@Nullable InsetsSourceControl control, int[] showTypes,
-            int[] hideTypes, int[] cancelTypes) {
+            int[] hideTypes, int[] cancelTypes, int[] transientTypes) {
         if (Flags.refactorInsetsController()) {
-            return super.setControl(control, showTypes, hideTypes, cancelTypes);
+            return super.setControl(control, showTypes, hideTypes, cancelTypes, transientTypes);
         } else {
             ImeTracing.getInstance().triggerClientDump("ImeInsetsSourceConsumer#setControl",
                     mController.getHost().getInputMethodManager(), null /* icProto */);
-            if (!super.setControl(control, showTypes, hideTypes, cancelTypes)) {
+            if (!super.setControl(control, showTypes, hideTypes, cancelTypes, transientTypes)) {
                 return false;
             }
             if (control == null && !mIsRequestedVisibleAwaitingLeash) {
diff --git a/core/java/android/view/InputEventConsistencyVerifier.java b/core/java/android/view/InputEventConsistencyVerifier.java
index 5c38a15..195896d 100644
--- a/core/java/android/view/InputEventConsistencyVerifier.java
+++ b/core/java/android/view/InputEventConsistencyVerifier.java
@@ -81,7 +81,7 @@
 
     // Bitfield of pointer ids that are currently down.
     // Assumes that the largest possible pointer id is 31, which is potentially subject to change.
-    // (See MAX_POINTER_ID in frameworks/base/include/ui/Input.h)
+    // (See MAX_POINTER_ID in frameworks/native/include/input/input.h)
     private int mTouchEventStreamPointers;
 
     // The device id and source of the current stream of touch events.
diff --git a/core/java/android/view/InputWindowHandle.java b/core/java/android/view/InputWindowHandle.java
index 6cd4a40..3e529cc 100644
--- a/core/java/android/view/InputWindowHandle.java
+++ b/core/java/android/view/InputWindowHandle.java
@@ -57,7 +57,7 @@
             InputConfig.NO_INPUT_CHANNEL,
             InputConfig.NOT_FOCUSABLE,
             InputConfig.NOT_TOUCHABLE,
-            InputConfig.PREVENT_SPLITTING,
+            InputConfig.DEPRECATED_PREVENT_SPLITTING,
             InputConfig.DUPLICATE_TOUCH_TO_WALLPAPER,
             InputConfig.IS_WALLPAPER,
             InputConfig.PAUSE_DISPATCHING,
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index b0813f3..c174fbe 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -959,6 +959,7 @@
         final @InsetsType int[] showTypes = new int[1];
         final @InsetsType int[] hideTypes = new int[1];
         final @InsetsType int[] cancelTypes = new int[1];
+        final @InsetsType int[] transientTypes = new int[1];
         ImeTracker.Token statsToken = null;
 
         // Ensure to update all existing source consumers
@@ -984,7 +985,7 @@
 
             // control may be null, but we still need to update the control to null if it got
             // revoked.
-            consumer.setControl(control, showTypes, hideTypes, cancelTypes);
+            consumer.setControl(control, showTypes, hideTypes, cancelTypes, transientTypes);
         }
 
         // Ensure to create source consumers if not available yet.
@@ -992,7 +993,7 @@
             for (int i = mTmpControlArray.size() - 1; i >= 0; i--) {
                 final InsetsSourceControl control = mTmpControlArray.valueAt(i);
                 getSourceConsumer(control.getId(), control.getType())
-                        .setControl(control, showTypes, hideTypes, cancelTypes);
+                        .setControl(control, showTypes, hideTypes, cancelTypes, transientTypes);
             }
         }
 
@@ -1020,10 +1021,16 @@
                 handlePendingControlRequest(statsToken);
             } else {
                 if (showTypes[0] != 0) {
-                    applyAnimation(showTypes[0], true /* show */, false /* fromIme */, statsToken);
+                    applyAnimation(showTypes[0], true /* show */, false /* fromIme */,
+                            false /* skipsCallbacks */, statsToken);
                 }
                 if (hideTypes[0] != 0) {
-                    applyAnimation(hideTypes[0], false /* show */, false /* fromIme */, statsToken);
+                    applyAnimation(hideTypes[0], false /* show */, false /* fromIme */,
+                            // The animation of hiding transient types shouldn't be detected by the
+                            // app. Otherwise, it might be able to react to the callbacks and cause
+                            // flickering.
+                            (hideTypes[0] & ~transientTypes[0]) == 0 /* skipsCallbacks */,
+                            statsToken);
                 }
             }
         } else {
@@ -1033,7 +1040,8 @@
                                 ImeTracker.TYPE_SHOW, ImeTracker.ORIGIN_CLIENT,
                                 SoftInputShowHideReason.CONTROLS_CHANGED,
                                 mHost.isHandlingPointerEvent() /* fromUser */);
-                applyAnimation(showTypes[0], true /* show */, false /* fromIme */, newStatsToken);
+                applyAnimation(showTypes[0], true /* show */, false /* fromIme */,
+                        false /* skipsCallbacks */, newStatsToken);
             }
             if (hideTypes[0] != 0) {
                 final var newStatsToken =
@@ -1041,7 +1049,12 @@
                                 ImeTracker.TYPE_HIDE, ImeTracker.ORIGIN_CLIENT,
                                 SoftInputShowHideReason.CONTROLS_CHANGED,
                                 mHost.isHandlingPointerEvent() /* fromUser */);
-                applyAnimation(hideTypes[0], false /* show */, false /* fromIme */, newStatsToken);
+                applyAnimation(hideTypes[0], false /* show */, false /* fromIme */,
+                        // The animation of hiding transient types shouldn't be detected by the app.
+                        // Otherwise, it might be able to react to the callbacks and cause
+                        // flickering.
+                        (hideTypes[0] & ~transientTypes[0]) == 0 /* skipsCallbacks */,
+                        newStatsToken);
             }
         }
 
@@ -1174,7 +1187,8 @@
             // TODO(b/353463205) check if this is needed here
             ImeTracker.forLatency().onShown(statsToken, ActivityThread::currentApplication);
         }
-        applyAnimation(typesReady, true /* show */, fromIme, statsToken);
+        applyAnimation(typesReady, true /* show */, fromIme, false /* skipsCallbacks */,
+                statsToken);
     }
 
     /**
@@ -1287,7 +1301,8 @@
             handlePendingControlRequest(statsToken);
             getImeSourceConsumer().removeSurface();
         }
-        applyAnimation(typesReady, false /* show */, fromIme, statsToken);
+        applyAnimation(typesReady, false /* show */, fromIme, false /* skipsCallbacks */,
+                statsToken);
     }
 
     @Override
@@ -2007,24 +2022,24 @@
 
     @VisibleForTesting
     public void applyAnimation(@InsetsType final int types, boolean show, boolean fromIme,
-            @Nullable ImeTracker.Token statsToken) {
+            boolean skipsCallbacks, @Nullable ImeTracker.Token statsToken) {
         // TODO(b/166736352): We should only skip the animation of specific types, not all types.
-        boolean skipAnim = false;
+        boolean skipsAnim = false;
         if ((types & ime()) != 0) {
             final InsetsSourceControl imeControl = mImeSourceConsumer.getControl();
             // Skip showing animation once that made by system for some reason.
             // (e.g. starting window with IME snapshot)
             if (imeControl != null) {
-                skipAnim = imeControl.getAndClearSkipAnimationOnce() && show
+                skipsAnim = imeControl.getAndClearSkipAnimationOnce() && show
                         && mImeSourceConsumer.hasViewFocusWhenWindowFocusGain();
             }
         }
-        applyAnimation(types, show, fromIme, skipAnim, statsToken);
+        applyAnimation(types, show, fromIme, skipsAnim, skipsCallbacks, statsToken);
     }
 
     @VisibleForTesting
     public void applyAnimation(@InsetsType final int types, boolean show, boolean fromIme,
-            boolean skipAnim, @Nullable ImeTracker.Token statsToken) {
+            boolean skipsAnim, boolean skipsCallbacks, @Nullable ImeTracker.Token statsToken) {
         if (types == 0) {
             // nothing to animate.
             if (DEBUG) Log.d(TAG, "applyAnimation, nothing to animate. Stopping here");
@@ -2040,7 +2055,7 @@
         boolean hasAnimationCallbacks = mHost.hasAnimationCallbacks();
         final InternalAnimationControlListener listener = new InternalAnimationControlListener(
                 show, hasAnimationCallbacks, types, mHost.getSystemBarsBehavior(),
-                skipAnim || mAnimationsDisabled, mHost.dipToPx(FLOATING_IME_BOTTOM_INSET_DP),
+                skipsAnim || mAnimationsDisabled, mHost.dipToPx(FLOATING_IME_BOTTOM_INSET_DP),
                 mLoggingListener, mJankContext);
 
         // We are about to playing the default animation (show/hide). Passing a null frame indicates
@@ -2050,7 +2065,7 @@
                 listener /* insetsAnimationSpec */,
                 show ? ANIMATION_TYPE_SHOW : ANIMATION_TYPE_HIDE,
                 show ? LAYOUT_INSETS_DURING_ANIMATION_SHOWN : LAYOUT_INSETS_DURING_ANIMATION_HIDDEN,
-                !hasAnimationCallbacks /* useInsetsAnimationThread */, statsToken,
+                !hasAnimationCallbacks || skipsCallbacks /* useInsetsAnimationThread */, statsToken,
                 false /* fromPredictiveBack */);
     }
 
@@ -2173,12 +2188,12 @@
                         new InsetsSourceControl(ID_IME_CAPTION_BAR, captionBar(),
                                 null /* leash */, false /* initialVisible */,
                                 new Point(), Insets.NONE),
-                        new int[1], new int[1], new int[1]);
+                        new int[1], new int[1], new int[1], new int[1]);
             } else {
                 mState.removeSource(ID_IME_CAPTION_BAR);
                 InsetsSourceConsumer sourceConsumer = mSourceConsumers.get(ID_IME_CAPTION_BAR);
                 if (sourceConsumer != null) {
-                    sourceConsumer.setControl(null, new int[1], new int[1], new int[1]);
+                    sourceConsumer.setControl(null, new int[1], new int[1], new int[1], new int[1]);
                 }
             }
             mHost.notifyInsetsChanged();
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index 17f33c1..e8e6621 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -130,7 +130,10 @@
      * @return Whether the control has changed from the server
      */
     public boolean setControl(@Nullable InsetsSourceControl control,
-            @InsetsType int[] showTypes, @InsetsType int[] hideTypes, int[] cancelTypes) {
+            @InsetsType int[] showTypes,
+            @InsetsType int[] hideTypes,
+            @InsetsType int[] cancelTypes,
+            @InsetsType int[] transientTypes) {
         if (Objects.equals(mSourceControl, control)) {
             if (mSourceControl != null && mSourceControl != control) {
                 mSourceControl.release(SurfaceControl::release);
@@ -185,6 +188,9 @@
                 } else {
                     hideTypes[0] |= mType;
                 }
+                if (lastControl != null && lastControl.isFake()) {
+                    transientTypes[0] |= mType;
+                }
             } else {
                 // We are gaining control, but don't need to run an animation.
                 // However make sure that the leash visibility is still up to date.
diff --git a/core/java/android/view/InsetsSourceControl.java b/core/java/android/view/InsetsSourceControl.java
index acbd95bf..7f2f0e8 100644
--- a/core/java/android/view/InsetsSourceControl.java
+++ b/core/java/android/view/InsetsSourceControl.java
@@ -142,6 +142,10 @@
         return mInsetsHint;
     }
 
+    public boolean isFake() {
+        return mLeash == null && Insets.NONE.equals(mInsetsHint);
+    }
+
     public void setSkipAnimationOnce(boolean skipAnimation) {
         mSkipAnimationOnce = skipAnimation;
     }
diff --git a/core/java/android/view/NotificationHeaderView.java b/core/java/android/view/NotificationHeaderView.java
index 42b798c..3a0e6f1 100644
--- a/core/java/android/view/NotificationHeaderView.java
+++ b/core/java/android/view/NotificationHeaderView.java
@@ -16,6 +16,10 @@
 
 package android.view;
 
+import static android.app.Flags.notificationsRedesignTemplates;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -27,6 +31,7 @@
 import android.graphics.drawable.Drawable;
 import android.os.Build;
 import android.util.AttributeSet;
+import android.widget.FrameLayout;
 import android.widget.RelativeLayout;
 import android.widget.RemoteViews;
 import android.widget.TextView;
@@ -174,6 +179,33 @@
     }
 
     /**
+     * Center top line  and expand button vertically.
+     */
+    @RemotableViewMethod
+    public void centerTopLine(boolean center) {
+        if (notificationsRedesignTemplates()) {
+            // The content of the top line view is already center-aligned, but since the height
+            // matches the content by default, it looks top-aligned. If the height matches the
+            // parent instead, the text ends up correctly centered in the parent.
+            ViewGroup.LayoutParams lp = mTopLineView.getLayoutParams();
+            lp.height = center ? MATCH_PARENT : WRAP_CONTENT;
+            mTopLineView.setLayoutParams(lp);
+
+            centerExpandButton(center);
+        }
+    }
+
+    /** Center expand button vertically. */
+    private void centerExpandButton(boolean center) {
+        ViewGroup.LayoutParams lp = mExpandButton.getLayoutParams();
+        lp.height = center ? MATCH_PARENT : WRAP_CONTENT;
+        if (lp instanceof FrameLayout.LayoutParams flp) {
+            flp.gravity = center ? Gravity.CENTER : (Gravity.TOP | Gravity.END);
+        }
+        mExpandButton.setLayoutParams(lp);
+    }
+
+    /**
      * This is used to make the low-priority header show the bolded text of a title.
      *
      * @param styleTextAsTitle true if this header's text is to have the style of a title
diff --git a/core/java/android/view/NotificationTopLineView.java b/core/java/android/view/NotificationTopLineView.java
index a2919f5..e567414 100644
--- a/core/java/android/view/NotificationTopLineView.java
+++ b/core/java/android/view/NotificationTopLineView.java
@@ -16,6 +16,8 @@
 
 package android.view;
 
+import static android.app.Flags.notificationsRedesignTemplates;
+
 import android.annotation.Nullable;
 import android.content.Context;
 import android.content.res.Resources;
@@ -112,7 +114,9 @@
         final int givenHeight = MeasureSpec.getSize(heightMeasureSpec);
         final boolean wrapHeight = MeasureSpec.getMode(heightMeasureSpec) == MeasureSpec.AT_MOST;
         int wrapContentWidthSpec = MeasureSpec.makeMeasureSpec(givenWidth, MeasureSpec.AT_MOST);
-        int heightSpec = MeasureSpec.makeMeasureSpec(givenHeight, MeasureSpec.AT_MOST);
+        int heightSpec = notificationsRedesignTemplates()
+                ? MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)
+                : MeasureSpec.makeMeasureSpec(givenHeight, MeasureSpec.AT_MOST);
         int totalWidth = getPaddingStart();
         int maxChildHeight = -1;
         mMaxAscent = -1;
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 833f2d9..e665c08 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -160,6 +160,10 @@
             float l, float t, float r, float b);
     private static native void nativeSetCornerRadius(long transactionObj, long nativeObject,
             float cornerRadius);
+    private static native void nativeSetClientDrawnCornerRadius(long transactionObj,
+            long nativeObject, float clientDrawnCornerRadius);
+    private static native void nativeSetClientDrawnShadows(long transactionObj,
+            long nativeObject, float clientDrawnShadows);
     private static native void nativeSetBackgroundBlurRadius(long transactionObj, long nativeObject,
             int blurRadius);
     private static native void nativeSetLayerStack(long transactionObj, long nativeObject,
@@ -3654,6 +3658,66 @@
             return this;
         }
 
+
+        /**
+         * Disables corner radius of a {@link SurfaceControl}. When the radius set by
+         * {@link Transaction#setCornerRadius(SurfaceControl, float)} is equal to
+         * clientDrawnCornerRadius the corner radius drawn by SurfaceFlinger is disabled.
+         *
+         * @param sc SurfaceControl
+         * @param clientDrawnCornerRadius Corner radius drawn by the client
+         * @return Itself.
+         * @hide
+         */
+        @NonNull
+        public Transaction setClientDrawnCornerRadius(@NonNull SurfaceControl sc,
+                                                            float clientDrawnCornerRadius) {
+            checkPreconditions(sc);
+            if (SurfaceControlRegistry.sCallStackDebuggingEnabled) {
+                SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging(
+                        "setClientDrawnCornerRadius", this, sc, "clientDrawnCornerRadius="
+                        + clientDrawnCornerRadius);
+            }
+            if (Flags.ignoreCornerRadiusAndShadows()) {
+                nativeSetClientDrawnCornerRadius(mNativeObject, sc.mNativeObject,
+                                                                clientDrawnCornerRadius);
+            } else {
+                Log.w(TAG, "setClientDrawnCornerRadius was called but"
+                            + "ignore_corner_radius_and_shadows flag is disabled");
+            }
+
+            return this;
+        }
+
+        /**
+         * Disables shadows of a {@link SurfaceControl}. When the radius set by
+         * {@link Transaction#setClientDrawnShadows(SurfaceControl, float)} is equal to
+         * clientDrawnShadowRadius the shadows drawn by SurfaceFlinger is disabled.
+         *
+         * @param sc SurfaceControl
+         * @param clientDrawnShadowRadius Shadow radius drawn by the client
+         * @return Itself.
+         * @hide
+         */
+        @NonNull
+        public Transaction setClientDrawnShadows(@NonNull SurfaceControl sc,
+                                                        float clientDrawnShadowRadius) {
+            checkPreconditions(sc);
+            if (SurfaceControlRegistry.sCallStackDebuggingEnabled) {
+                SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging(
+                        "setClientDrawnShadows", this, sc,
+                        "clientDrawnShadowRadius=" + clientDrawnShadowRadius);
+            }
+            if (Flags.ignoreCornerRadiusAndShadows()) {
+                nativeSetClientDrawnShadows(mNativeObject, sc.mNativeObject,
+                                                        clientDrawnShadowRadius);
+            } else {
+                Log.w(TAG, "setClientDrawnShadows was called but"
+                            + "ignore_corner_radius_and_shadows flag is disabled");
+            }
+            return this;
+        }
+
         /**
          * Sets the background blur radius of the {@link SurfaceControl}.
          *
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 16cdb64..8ef0b0e 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -10527,28 +10527,7 @@
 
         @Override
         public void onInputEvent(InputEvent event) {
-            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "processInputEventForCompatibility");
-            List<InputEvent> processedEvents;
-            try {
-                processedEvents =
-                    mInputCompatProcessor.processInputEventForCompatibility(event);
-            } finally {
-                Trace.traceEnd(Trace.TRACE_TAG_VIEW);
-            }
-            if (processedEvents != null) {
-                if (processedEvents.isEmpty()) {
-                    // InputEvent consumed by mInputCompatProcessor
-                    finishInputEvent(event, true);
-                } else {
-                    for (int i = 0; i < processedEvents.size(); i++) {
-                        enqueueInputEvent(
-                                processedEvents.get(i), this,
-                                QueuedInputEvent.FLAG_MODIFIED_FOR_COMPATIBILITY, true);
-                    }
-                }
-            } else {
-                enqueueInputEvent(event, this, 0, true);
-            }
+            processRawInputEvent(event);
         }
 
         @Override
@@ -10758,6 +10737,42 @@
     final InvalidateOnAnimationRunnable mInvalidateOnAnimationRunnable =
             new InvalidateOnAnimationRunnable();
 
+    /**
+     * Handle the incoming event.
+     *
+     * <p>The event will be first sent to the compatibility processor, which could choose to handle
+     * it. The compat processor could also choose to produce more synthetic events in response to
+     * the incoming one. Events that are not consumed by the compat processor are added to the
+     * {@link ViewRootImpl}'s queue for further processing inside ViewRootImpl.
+     *
+     * @hide
+     */
+    @VisibleForTesting
+    public void processRawInputEvent(InputEvent event) {
+        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "processInputEventForCompatibility");
+        List<InputEvent> processedEvents;
+        try {
+            processedEvents =
+                    mInputCompatProcessor.processInputEventForCompatibility(event);
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+        }
+        if (processedEvents != null) {
+            if (processedEvents.isEmpty()) {
+                // InputEvent consumed by mInputCompatProcessor
+                mInputEventReceiver.finishInputEvent(event, true);
+            } else {
+                for (int i = 0; i < processedEvents.size(); i++) {
+                    enqueueInputEvent(
+                            processedEvents.get(i), mInputEventReceiver,
+                            QueuedInputEvent.FLAG_MODIFIED_FOR_COMPATIBILITY, true);
+                }
+            }
+        } else {
+            enqueueInputEvent(event, mInputEventReceiver, 0, true);
+        }
+    }
+
     public void dispatchInvalidateDelayed(View view, long delayMilliseconds) {
         Message msg = mHandler.obtainMessage(MSG_INVALIDATE, view);
         mHandler.sendMessageDelayed(msg, delayMilliseconds);
diff --git a/core/java/android/view/WindowInsetsController.java b/core/java/android/view/WindowInsetsController.java
index 19c98a2..cdfa7c81 100644
--- a/core/java/android/view/WindowInsetsController.java
+++ b/core/java/android/view/WindowInsetsController.java
@@ -34,6 +34,9 @@
 
 /**
  * Interface to control windows that generate insets.
+ *
+ * For guidance, see <a href="https://developer.android.com/develop/ui/views/layout/immersive">
+ * Hide system bars for immersive mode</a>.
  */
 public interface WindowInsetsController {
 
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index f50ea91..25bd713 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -453,6 +453,7 @@
             try {
                 root.setView(view, wparams, panelParentView, userId);
             } catch (RuntimeException e) {
+                Log.e(TAG, "Couldn't add view: " + view, e);
                 final int viewIndex = (index >= 0) ? index : (mViews.size() - 1);
                 // BadTokenException or InvalidDisplayException, clean up.
                 if (viewIndex >= 0) {
diff --git a/core/java/android/view/WindowManagerPolicyConstants.java b/core/java/android/view/WindowManagerPolicyConstants.java
index 1f341ca..6d2c0d00 100644
--- a/core/java/android/view/WindowManagerPolicyConstants.java
+++ b/core/java/android/view/WindowManagerPolicyConstants.java
@@ -30,7 +30,8 @@
  * @hide
  */
 public interface WindowManagerPolicyConstants {
-    // Policy flags.  These flags are also defined in frameworks/base/include/ui/Input.h and
+    // Policy flags. These flags are also defined in
+    // frameworks/native/include/input/Input.h and
     // frameworks/native/libs/input/android/os/IInputConstants.aidl
     int FLAG_WAKE = 0x00000001;
     int FLAG_VIRTUAL = 0x00000002;
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index fd57aec..544f42b 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -148,6 +148,15 @@
     /** @hide */
     public static final int AUTOCLICK_DELAY_DEFAULT = 600;
 
+    /** @hide */
+    public static final int AUTOCLICK_CURSOR_AREA_SIZE_DEFAULT = 60;
+
+    /** @hide */
+    public static final int AUTOCLICK_CURSOR_AREA_SIZE_MIN = 20;
+
+    /** @hide */
+    public static final int AUTOCLICK_CURSOR_AREA_SIZE_MAX = 100;
+
     /**
      * Activity action: Launch UI to manage which accessibility service or feature is assigned
      * to the navigation bar Accessibility button.
diff --git a/core/java/android/view/autofill/AutofillFeatureFlags.java b/core/java/android/view/autofill/AutofillFeatureFlags.java
index d527007..206e47f 100644
--- a/core/java/android/view/autofill/AutofillFeatureFlags.java
+++ b/core/java/android/view/autofill/AutofillFeatureFlags.java
@@ -16,6 +16,8 @@
 
 package android.view.autofill;
 
+import static android.service.autofill.Flags.improveFillDialogAconfig;
+
 import android.annotation.SuppressLint;
 import android.annotation.TestApi;
 import android.provider.DeviceConfig;
@@ -662,7 +664,7 @@
     public static boolean isImproveFillDialogEnabled() {
         // TODO(b/266379948): Add condition for checking whether device has PCC first
 
-        return DeviceConfig.getBoolean(
+        return improveFillDialogAconfig() && DeviceConfig.getBoolean(
                 DeviceConfig.NAMESPACE_AUTOFILL,
                 DEVICE_CONFIG_IMPROVE_FILL_DIALOG_ENABLED,
                 DEFAULT_IMPROVE_FILL_DIALOG_ENABLED);
diff --git a/core/java/android/view/contentprotection/flags/content_protection_flags.aconfig b/core/java/android/view/contentprotection/flags/content_protection_flags.aconfig
index b3bd92b..c871d56 100644
--- a/core/java/android/view/contentprotection/flags/content_protection_flags.aconfig
+++ b/core/java/android/view/contentprotection/flags/content_protection_flags.aconfig
@@ -45,3 +45,10 @@
     description: "If true, the APIs to manage content protection device policy will be enabled."
     bug: "319477846"
 }
+
+flag {
+    name: "exported_settings_activity_enabled"
+    namespace: "content_protection"
+    description: "If true, the content protection Settings Activity will be exported for launching externally."
+    bug: "385310141"
+}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index eaa8f60..e904345f 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -992,23 +992,7 @@
         private void setCurrentRootViewLocked(ViewRootImpl rootView) {
             final boolean wasEmpty = mCurRootView == null;
             if (Flags.refactorInsetsController() && !wasEmpty && mCurRootView != rootView) {
-                final int softInputMode = mCurRootView.mWindowAttributes.softInputMode;
-                final int state =
-                        softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE;
-                if (state == WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN) {
-                    // when losing input focus (e.g., by going to another window), we reset the
-                    // requestedVisibleTypes of WindowInsetsController by hiding the IME
-                    final var statsToken = ImeTracker.forLogging().onStart(
-                            ImeTracker.TYPE_HIDE, ImeTracker.ORIGIN_CLIENT,
-                            SoftInputShowHideReason.HIDE_WINDOW_LOST_FOCUS,
-                            false /* fromUser */);
-                    if (DEBUG) {
-                        Log.d(TAG, "setCurrentRootViewLocked, hiding IME because "
-                                + "of STATE_ALWAYS_HIDDEN");
-                    }
-                    mCurRootView.getInsetsController().hide(WindowInsets.Type.ime(),
-                            false /* fromIme */, statsToken);
-                }
+                onImeFocusLost(mCurRootView);
             }
 
             mImeDispatcher.switchRootView(mCurRootView, rootView);
@@ -1019,6 +1003,26 @@
         }
     }
 
+    private void onImeFocusLost(@NonNull ViewRootImpl previousRootView) {
+        final int softInputMode = previousRootView.mWindowAttributes.softInputMode;
+        final int state =
+                softInputMode & WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE;
+        if (state == WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN) {
+            // when losing input focus (e.g., by going to another window), we reset the
+            // requestedVisibleTypes of WindowInsetsController by hiding the IME
+            final var statsToken = ImeTracker.forLogging().onStart(
+                    ImeTracker.TYPE_HIDE, ImeTracker.ORIGIN_CLIENT,
+                    SoftInputShowHideReason.HIDE_WINDOW_LOST_FOCUS,
+                    false /* fromUser */);
+            if (DEBUG) {
+                Log.d(TAG, "onImeFocusLost, hiding IME because "
+                        + "of STATE_ALWAYS_HIDDEN");
+            }
+            previousRootView.getInsetsController().hide(WindowInsets.Type.ime(),
+                    false /* fromIme */, statsToken);
+        }
+    }
+
     /** @hide */
     public DelegateImpl getDelegate() {
         return mDelegate;
diff --git a/core/java/android/widget/flags/flags.aconfig b/core/java/android/widget/flags/flags.aconfig
index 88eb043..83a4c86 100644
--- a/core/java/android/widget/flags/flags.aconfig
+++ b/core/java/android/widget/flags/flags.aconfig
@@ -2,7 +2,7 @@
 container: "system"
 flag {
   name: "enable_fading_view_group"
-  namespace: "system_performance"
+  namespace: "wear_systems"
   description: "FRP screen during OOBE must have fading and scaling animation in Wear Watches"
   bug: "348515581"
   metadata {
diff --git a/core/java/android/window/BackNavigationInfo.java b/core/java/android/window/BackNavigationInfo.java
index 4b7bacb..924bdbf 100644
--- a/core/java/android/window/BackNavigationInfo.java
+++ b/core/java/android/window/BackNavigationInfo.java
@@ -282,20 +282,6 @@
     }
 
     /**
-     * Callback to be called when the back gesture is finished in order to notify the server that
-     * it can ask app to start rendering.
-     * @hide
-     * @param triggerBack Boolean indicating if back gesture has been triggered.
-     */
-    public void onBackGestureFinished(boolean triggerBack) {
-        if (mOnBackNavigationDone != null) {
-            Bundle result = new Bundle();
-            result.putBoolean(KEY_GESTURE_FINISHED, triggerBack);
-            mOnBackNavigationDone.sendResult(result);
-        }
-    }
-
-    /**
      * Get customize animation info.
      * @hide
      */
diff --git a/core/java/android/window/ConfigurationChangeSetting.aidl b/core/java/android/window/ConfigurationChangeSetting.aidl
new file mode 100644
index 0000000..7707512
--- /dev/null
+++ b/core/java/android/window/ConfigurationChangeSetting.aidl
@@ -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 android.window;
+
+/**
+ * Represents a setting request that can be applied as part of a batch to avoid multiple
+ * configuration updates.
+ *
+ * @see IWindowManager#setConfigurationChangeSettingsForUser
+ * @hide
+ */
+parcelable ConfigurationChangeSetting;
+
+/**
+ * Represents a request to change the display density.
+ * @hide
+ */
+parcelable DensitySetting;
+
+/**
+ * Represents a request to change the font scale.
+ * @hide
+ */
+parcelable FontScaleSetting;
diff --git a/core/java/android/window/ConfigurationChangeSetting.java b/core/java/android/window/ConfigurationChangeSetting.java
new file mode 100644
index 0000000..a292fe8
--- /dev/null
+++ b/core/java/android/window/ConfigurationChangeSetting.java
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.window;
+
+import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
+
+import android.annotation.CallSuper;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.ActivityThread;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.view.IWindowManager;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.LocalServices;
+import com.android.window.flags.Flags;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * Represents a setting request that can be applied as part of a batch to avoid multiple
+ * configuration updates.
+ *
+ * @hide
+ */
+public abstract class ConfigurationChangeSetting implements Parcelable {
+    /* The type of the setting for creating from a parcel. */
+    public static final int SETTING_TYPE_UNKNOWN = -1;
+    public static final int SETTING_TYPE_DISPLAY_DENSITY = 0;
+    public static final int SETTING_TYPE_FONT_SCALE = 1;
+
+    @IntDef(prefix = {"SETTING_TYPE_"}, value = {
+            SETTING_TYPE_UNKNOWN,
+            SETTING_TYPE_DISPLAY_DENSITY,
+            SETTING_TYPE_FONT_SCALE
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SettingType {
+    }
+
+    @SettingType
+    private final int mSettingType;
+
+    private ConfigurationChangeSetting(@SettingType int settingType) {
+        if (!Flags.condenseConfigurationChangeForSimpleMode()) {
+            throw new IllegalStateException(
+                    "ConfigurationChangeSetting cannot be instantiated because the "
+                            + "condenseConfigurationChangeForSimpleMode flag is not enabled. "
+                            + "Please ensure this flag is enabled.");
+        }
+        mSettingType = settingType;
+    }
+
+    @CallSuper
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeInt(mSettingType);
+    }
+
+    public static final Creator<ConfigurationChangeSetting> CREATOR = new CreatorImpl();
+
+    /**
+     * Implementation of the {@link Parcelable.Creator} for {@link ConfigurationChangeSetting}.
+     *
+     * <p>Creates {@link ConfigurationChangeSetting} objects from a {@link Parcel}, handling
+     * system/client processes. System process delegates creation to the server-side implementation.
+     *
+     * @hide
+     */
+    @VisibleForTesting(visibility = PRIVATE)
+    public static class CreatorImpl implements Creator<ConfigurationChangeSetting> {
+        private final boolean mIsSystem;
+
+        private CreatorImpl() {
+            this(ActivityThread.isSystem());
+        }
+
+        @VisibleForTesting(visibility = PRIVATE)
+        public CreatorImpl(boolean isSystem) {
+            mIsSystem = isSystem;
+        }
+
+        @Override
+        public ConfigurationChangeSetting createFromParcel(@NonNull Parcel in) {
+            final int settingType = in.readInt();
+            if (mIsSystem) {
+                return LocalServices.getService(ConfigurationChangeSettingInternal.class)
+                        .createImplFromParcel(settingType, in);
+            }
+            switch (settingType) {
+                case SETTING_TYPE_DISPLAY_DENSITY:
+                    return DensitySetting.CREATOR.createFromParcel(in);
+                case SETTING_TYPE_FONT_SCALE:
+                    return FontScaleSetting.CREATOR.createFromParcel(in);
+                default:
+                    throw new IllegalArgumentException("Unknown setting type " + settingType);
+            }
+        }
+
+        @Override
+        public ConfigurationChangeSetting[] newArray(int size) {
+            return new ConfigurationChangeSetting[size];
+        }
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Applies the specific setting request to the system.
+     *
+     * <p>This method should handle the logic for modifying system settings or making other
+     * adjustments to achieve the intended configuration change. It is called within the
+     * context of a batch update, where multiple {@link ConfigurationChangeSetting} instances
+     * might be applied sequentially.
+     *
+     * @param userId The user for which to apply the setting
+     * @hide Only for use within the system server.
+     * @see IWindowManager#setConfigurationChangeSettingsForUser(ConfigurationChangeSetting[], int)
+     */
+    public void apply(@UserIdInt int userId) {
+        // no-op in client process, the apply will be executed in server side.
+    }
+
+    /**
+     * Interface for server side implementation of {@link ConfigurationChangeSetting}.
+     *
+     * @hide Only for use within the system server.
+     */
+    public interface ConfigurationChangeSettingInternal {
+        /**
+         * Create server side {@link ConfigurationChangeSetting} implementation from parcel.
+         *
+         * @param settingType the type of {@link ConfigurationChangeSetting}.
+         * @param in          the {@link Parcel} to read data from.
+         * @return server side {@link ConfigurationChangeSetting} implementation.
+         */
+        @NonNull
+        ConfigurationChangeSetting createImplFromParcel(@SettingType int settingType,
+                @NonNull Parcel in);
+    }
+
+    /**
+     * Represents a request to change the display density.
+     *
+     * @hide
+     */
+    public static class DensitySetting extends ConfigurationChangeSetting {
+        protected final int mDisplayId;
+        protected final int mDensity;
+
+        /**
+         * Constructs a {@link DensitySetting}.
+         *
+         * @param density The new display density.
+         * @hide
+         */
+        public DensitySetting(int displayId, int density) {
+            super(SETTING_TYPE_DISPLAY_DENSITY);
+            mDisplayId = displayId;
+            mDensity = density;
+        }
+
+        protected DensitySetting(@NonNull Parcel in) {
+            this(in.readInt(), in.readInt());
+        }
+
+        @Override
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
+            super.writeToParcel(dest, flags);
+            dest.writeInt(mDisplayId);
+            dest.writeInt(mDensity);
+        }
+
+        public static final Creator<DensitySetting> CREATOR = new Creator<>() {
+            @Override
+            public DensitySetting createFromParcel(@NonNull Parcel in) {
+                return new DensitySetting(in);
+            }
+
+            @Override
+            public DensitySetting[] newArray(int size) {
+                return new DensitySetting[size];
+            }
+        };
+
+        @Override
+        public boolean equals(@Nullable Object obj) {
+            if (!(obj instanceof DensitySetting other)) {
+                return false;
+            }
+            return mDisplayId == other.mDisplayId && mDensity == other.mDensity;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mDisplayId, mDensity);
+        }
+    }
+
+    /**
+     * Represents a request to change the font scale.
+     *
+     * @hide
+     */
+    public static class FontScaleSetting extends ConfigurationChangeSetting {
+        protected final float mFontScaleFactor;
+
+        /**
+         * Constructs a {@code FontScaleSetting}.
+         *
+         * @param fontScaleFactor The new font scale factor.
+         * @hide
+         */
+        public FontScaleSetting(float fontScaleFactor) {
+            super(SETTING_TYPE_FONT_SCALE);
+            mFontScaleFactor = fontScaleFactor;
+        }
+
+        protected FontScaleSetting(@NonNull Parcel in) {
+            this(in.readFloat());
+        }
+
+        @Override
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
+            super.writeToParcel(dest, flags);
+            dest.writeFloat(mFontScaleFactor);
+        }
+
+        public static final Creator<FontScaleSetting> CREATOR = new Creator<>() {
+            @Override
+            public FontScaleSetting createFromParcel(@NonNull Parcel in) {
+                return new FontScaleSetting(in);
+            }
+
+            @Override
+            public FontScaleSetting[] newArray(int size) {
+                return new FontScaleSetting[size];
+            }
+        };
+
+        @Override
+        public boolean equals(@Nullable Object obj) {
+            if (!(obj instanceof FontScaleSetting other)) {
+                return false;
+            }
+            return Float.compare(mFontScaleFactor, other.mFontScaleFactor) == 0;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mFontScaleFactor);
+        }
+    }
+}
diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java
index c800119..02f8e2f 100644
--- a/core/java/android/window/WindowContainerTransaction.java
+++ b/core/java/android/window/WindowContainerTransaction.java
@@ -16,6 +16,7 @@
 
 package android.window;
 
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.window.TaskFragmentOperation.OP_TYPE_CLEAR_ADJACENT_TASK_FRAGMENTS;
 import static android.window.TaskFragmentOperation.OP_TYPE_CREATE_TASK_FRAGMENT;
 import static android.window.TaskFragmentOperation.OP_TYPE_DELETE_TASK_FRAGMENT;
@@ -26,12 +27,14 @@
 import static android.window.TaskFragmentOperation.OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT;
 
 import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
 import android.annotation.TestApi;
 import android.app.PendingIntent;
 import android.app.WindowConfiguration;
+import android.app.WindowConfiguration.WindowingMode;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ShortcutInfo;
@@ -50,6 +53,8 @@
 
 import com.android.window.flags.Flags;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -77,13 +82,14 @@
 
     public WindowContainerTransaction() {}
 
-    private WindowContainerTransaction(Parcel in) {
+    private WindowContainerTransaction(@NonNull Parcel in) {
         in.readMap(mChanges, null /* loader */);
         in.readTypedList(mHierarchyOps, HierarchyOp.CREATOR);
         mErrorCallbackToken = in.readStrongBinder();
         mTaskFragmentOrganizer = ITaskFragmentOrganizer.Stub.asInterface(in.readStrongBinder());
     }
 
+    @NonNull
     private Change getOrCreateChange(IBinder token) {
         Change out = mChanges.get(token);
         if (out == null) {
@@ -111,8 +117,8 @@
      */
     @NonNull
     public WindowContainerTransaction setBounds(
-            @NonNull WindowContainerToken container,@NonNull Rect bounds) {
-        Change chg = getOrCreateChange(container.asBinder());
+            @NonNull WindowContainerToken container, @NonNull Rect bounds) {
+        final Change chg = getOrCreateChange(container.asBinder());
         chg.mConfiguration.windowConfiguration.setBounds(bounds);
         chg.mConfigSetMask |= ActivityInfo.CONFIG_WINDOW_CONFIGURATION;
         chg.mWindowSetMask |= WindowConfiguration.WINDOW_CONFIG_BOUNDS;
@@ -126,8 +132,8 @@
      */
     @NonNull
     public WindowContainerTransaction setAppBounds(
-            @NonNull WindowContainerToken container,@NonNull Rect appBounds) {
-        Change chg = getOrCreateChange(container.asBinder());
+            @NonNull WindowContainerToken container, @NonNull Rect appBounds) {
+        final Change chg = getOrCreateChange(container.asBinder());
         chg.mConfiguration.windowConfiguration.setAppBounds(appBounds);
         chg.mConfigSetMask |= ActivityInfo.CONFIG_WINDOW_CONFIGURATION;
         chg.mWindowSetMask |= WindowConfiguration.WINDOW_CONFIG_APP_BOUNDS;
@@ -143,7 +149,7 @@
     @NonNull
     public WindowContainerTransaction setScreenSizeDp(
             @NonNull WindowContainerToken container, int w, int h) {
-        Change chg = getOrCreateChange(container.asBinder());
+        final Change chg = getOrCreateChange(container.asBinder());
         chg.mConfiguration.screenWidthDp = w;
         chg.mConfiguration.screenHeightDp = h;
         chg.mConfigSetMask |= ActivityInfo.CONFIG_SCREEN_SIZE;
@@ -157,7 +163,7 @@
     @NonNull
     public WindowContainerTransaction setDensityDpi(@NonNull WindowContainerToken container,
             int densityDpi) {
-        Change chg = getOrCreateChange(container.asBinder());
+        final Change chg = getOrCreateChange(container.asBinder());
         chg.mConfiguration.densityDpi = densityDpi;
         chg.mConfigSetMask |= ActivityInfo.CONFIG_DENSITY;
         return this;
@@ -169,8 +175,8 @@
      */
     @NonNull
     public WindowContainerTransaction scheduleFinishEnterPip(
-            @NonNull WindowContainerToken container,@NonNull Rect bounds) {
-        Change chg = getOrCreateChange(container.asBinder());
+            @NonNull WindowContainerToken container, @NonNull Rect bounds) {
+        final Change chg = getOrCreateChange(container.asBinder());
         chg.mPinnedBounds = new Rect(bounds);
         chg.mChangeMask |= Change.CHANGE_PIP_CALLBACK;
 
@@ -190,8 +196,8 @@
      */
     @NonNull
     public WindowContainerTransaction setBoundsChangeTransaction(
-            @NonNull WindowContainerToken container,@NonNull SurfaceControl.Transaction t) {
-        Change chg = getOrCreateChange(container.asBinder());
+            @NonNull WindowContainerToken container, @NonNull SurfaceControl.Transaction t) {
+        final Change chg = getOrCreateChange(container.asBinder());
         chg.mBoundsChangeTransaction = t;
         chg.mChangeMask |= Change.CHANGE_BOUNDS_TRANSACTION;
         return this;
@@ -237,8 +243,8 @@
      */
     @NonNull
     public WindowContainerTransaction setActivityWindowingMode(
-            @NonNull WindowContainerToken container, int windowingMode) {
-        Change chg = getOrCreateChange(container.asBinder());
+            @NonNull WindowContainerToken container, @WindowingMode int windowingMode) {
+        final Change chg = getOrCreateChange(container.asBinder());
         chg.mActivityWindowingMode = windowingMode;
         return this;
     }
@@ -248,8 +254,8 @@
      */
     @NonNull
     public WindowContainerTransaction setWindowingMode(
-            @NonNull WindowContainerToken container, int windowingMode) {
-        Change chg = getOrCreateChange(container.asBinder());
+            @NonNull WindowContainerToken container, @WindowingMode int windowingMode) {
+        final Change chg = getOrCreateChange(container.asBinder());
         chg.mWindowingMode = windowingMode;
         return this;
     }
@@ -262,7 +268,7 @@
     @NonNull
     public WindowContainerTransaction setFocusable(
             @NonNull WindowContainerToken container, boolean focusable) {
-        Change chg = getOrCreateChange(container.asBinder());
+        final Change chg = getOrCreateChange(container.asBinder());
         chg.mFocusable = focusable;
         chg.mChangeMask |= Change.CHANGE_FOCUSABLE;
         return this;
@@ -293,7 +299,7 @@
     @NonNull
     public WindowContainerTransaction setHidden(
             @NonNull WindowContainerToken container, boolean hidden) {
-        Change chg = getOrCreateChange(container.asBinder());
+        final Change chg = getOrCreateChange(container.asBinder());
         chg.mHidden = hidden;
         chg.mChangeMask |= Change.CHANGE_HIDDEN;
         return this;
@@ -305,7 +311,7 @@
     @NonNull
     public WindowContainerTransaction setSmallestScreenWidthDp(
             @NonNull WindowContainerToken container, int widthDp) {
-        Change cfg = getOrCreateChange(container.asBinder());
+        final Change cfg = getOrCreateChange(container.asBinder());
         cfg.mConfiguration.smallestScreenWidthDp = widthDp;
         cfg.mConfigSetMask |= ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
         return this;
@@ -321,7 +327,7 @@
     @NonNull
     public WindowContainerTransaction setIgnoreOrientationRequest(
             @NonNull WindowContainerToken container, boolean ignoreOrientationRequest) {
-        Change chg = getOrCreateChange(container.asBinder());
+        final Change chg = getOrCreateChange(container.asBinder());
         chg.mIgnoreOrientationRequest = ignoreOrientationRequest;
         chg.mChangeMask |= Change.CHANGE_IGNORE_ORIENTATION_REQUEST;
         return this;
@@ -335,7 +341,7 @@
     @NonNull
     public WindowContainerTransaction setForceTranslucent(
             @NonNull WindowContainerToken container, boolean forceTranslucent) {
-        Change chg = getOrCreateChange(container.asBinder());
+        final Change chg = getOrCreateChange(container.asBinder());
         chg.mForceTranslucent = forceTranslucent;
         chg.mChangeMask |= Change.CHANGE_FORCE_TRANSLUCENT;
         return this;
@@ -350,7 +356,7 @@
      */
     @NonNull
     public WindowContainerTransaction setDoNotPip(@NonNull WindowContainerToken container) {
-        Change chg = getOrCreateChange(container.asBinder());
+        final Change chg = getOrCreateChange(container.asBinder());
         chg.mChangeMask |= Change.CHANGE_FORCE_NO_PIP;
         return this;
     }
@@ -362,7 +368,7 @@
     @NonNull
     public WindowContainerTransaction setRelativeBounds(
             @NonNull WindowContainerToken container, @NonNull Rect relBounds) {
-        Change chg = getOrCreateChange(container.asBinder());
+        final Change chg = getOrCreateChange(container.asBinder());
         if (chg.mRelativeBounds == null) {
             chg.mRelativeBounds = new Rect();
         }
@@ -520,8 +526,7 @@
      * @hide // TODO(b/373709676) Rename to setAdjacentRoots and update CTS.
      */
     @NonNull
-    public WindowContainerTransaction setAdjacentRootSet(
-            @NonNull WindowContainerToken... roots) {
+    public WindowContainerTransaction setAdjacentRootSet(@NonNull WindowContainerToken... roots) {
         if (!Flags.allowMultipleAdjacentTaskFragments()) {
             throw new IllegalArgumentException("allowMultipleAdjacentTaskFragments is not enabled."
                     + " Use #setAdjacentRoots instead.");
@@ -550,8 +555,7 @@
      * @hide
      */
     @NonNull
-    public WindowContainerTransaction clearAdjacentRoots(
-            @NonNull WindowContainerToken root) {
+    public WindowContainerTransaction clearAdjacentRoots(@NonNull WindowContainerToken root) {
         mHierarchyOps.add(HierarchyOp.createForClearAdjacentRoots(root.asBinder()));
         return this;
     }
@@ -643,8 +647,8 @@
      * @hide
      */
     @NonNull
-    public WindowContainerTransaction sendPendingIntent(PendingIntent sender, Intent intent,
-            @Nullable Bundle options) {
+    public WindowContainerTransaction sendPendingIntent(@Nullable PendingIntent sender,
+            @Nullable Intent intent, @Nullable Bundle options) {
         mHierarchyOps.add(new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_PENDING_INTENT)
                 .setLaunchOptions(options)
                 .setPendingIntent(sender)
@@ -801,6 +805,7 @@
         mHierarchyOps.add(hierarchyOp);
         return this;
     }
+
     /**
      * Adds a given {@code Rect} as an insets source frame on the {@code receiver}.
      *
@@ -817,8 +822,8 @@
     @NonNull
     public WindowContainerTransaction addInsetsSource(
             @NonNull WindowContainerToken receiver,
-            IBinder owner, int index, @InsetsType int type, Rect frame, Rect[] boundingRects,
-            @InsetsSource.Flags int flags) {
+            @Nullable IBinder owner, int index, @InsetsType int type, @Nullable Rect frame,
+            @Nullable Rect[] boundingRects, @InsetsSource.Flags int flags) {
         final HierarchyOp hierarchyOp =
                 new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_ADD_INSETS_FRAME_PROVIDER)
                         .setContainer(receiver.asBinder())
@@ -845,9 +850,8 @@
      * @hide
      */
     @NonNull
-    public WindowContainerTransaction removeInsetsSource(
-            @NonNull WindowContainerToken receiver,
-            IBinder owner, int index, @InsetsType int type) {
+    public WindowContainerTransaction removeInsetsSource(@NonNull WindowContainerToken receiver,
+            @Nullable IBinder owner, int index, @InsetsType int type) {
         final HierarchyOp hierarchyOp =
                 new HierarchyOp.Builder(HierarchyOp.HIERARCHY_OP_TYPE_REMOVE_INSETS_FRAME_PROVIDER)
                         .setContainer(receiver.asBinder())
@@ -945,8 +949,7 @@
      * @hide
      */
     @NonNull
-    public WindowContainerTransaction addKeyguardState(
-            @NonNull KeyguardState keyguardState) {
+    public WindowContainerTransaction addKeyguardState(@NonNull KeyguardState keyguardState) {
         Objects.requireNonNull(keyguardState);
         final HierarchyOp hierarchyOp =
                 new HierarchyOp.Builder(
@@ -978,8 +981,7 @@
      */
     @NonNull
     public WindowContainerTransaction setAlwaysOnTop(
-            @NonNull WindowContainerToken windowContainer,
-            boolean alwaysOnTop) {
+            @NonNull WindowContainerToken windowContainer, boolean alwaysOnTop) {
         final HierarchyOp hierarchyOp =
                 new HierarchyOp.Builder(
                         HierarchyOp.HIERARCHY_OP_TYPE_SET_ALWAYS_ON_TOP)
@@ -1099,7 +1101,7 @@
      *                 SurfaceFlinger Transactions will not be merged.
      * @hide
      */
-    public void merge(WindowContainerTransaction other, boolean transfer) {
+    public void merge(@NonNull WindowContainerTransaction other, boolean transfer) {
         for (int i = 0, n = other.mChanges.size(); i < n; ++i) {
             final IBinder key = other.mChanges.keyAt(i);
             Change existing = mChanges.get(key);
@@ -1110,8 +1112,8 @@
             existing.merge(other.mChanges.valueAt(i), transfer);
         }
         for (int i = 0, n = other.mHierarchyOps.size(); i < n; ++i) {
-            mHierarchyOps.add(transfer ? other.mHierarchyOps.get(i)
-                    : new HierarchyOp(other.mHierarchyOps.get(i)));
+            final HierarchyOp otherHierarchyOp = other.mHierarchyOps.get(i);
+            mHierarchyOps.add(transfer ? otherHierarchyOp : new HierarchyOp(otherHierarchyOp));
         }
         if (mErrorCallbackToken != null && other.mErrorCallbackToken != null && mErrorCallbackToken
                 != other.mErrorCallbackToken) {
@@ -1138,11 +1140,13 @@
     }
 
     /** @hide */
+    @NonNull
     public Map<IBinder, Change> getChanges() {
         return mChanges;
     }
 
     /** @hide */
+    @NonNull
     public List<HierarchyOp> getHierarchyOps() {
         return mHierarchyOps;
     }
@@ -1187,7 +1191,7 @@
     public static final Creator<WindowContainerTransaction> CREATOR =
             new Creator<WindowContainerTransaction>() {
                 @Override
-                public WindowContainerTransaction createFromParcel(Parcel in) {
+                public WindowContainerTransaction createFromParcel(@NonNull Parcel in) {
                     return new WindowContainerTransaction(in);
                 }
 
@@ -1213,6 +1217,21 @@
         public static final int CHANGE_DRAG_RESIZING = 1 << 8;
         public static final int CHANGE_RELATIVE_BOUNDS = 1 << 9;
 
+        @IntDef(flag = true, prefix = { "CHANGE_" }, value = {
+                CHANGE_FOCUSABLE,
+                CHANGE_BOUNDS_TRANSACTION,
+                CHANGE_PIP_CALLBACK,
+                CHANGE_HIDDEN,
+                CHANGE_BOUNDS_TRANSACTION_RECT,
+                CHANGE_IGNORE_ORIENTATION_REQUEST,
+                CHANGE_FORCE_NO_PIP,
+                CHANGE_FORCE_TRANSLUCENT,
+                CHANGE_DRAG_RESIZING,
+                CHANGE_RELATIVE_BOUNDS
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface ChangeMask {}
+
         private final Configuration mConfiguration = new Configuration();
         private boolean mFocusable = true;
         private boolean mHidden = false;
@@ -1220,7 +1239,7 @@
         private boolean mForceTranslucent = false;
         private boolean mDragResizing = false;
 
-        private int mChangeMask = 0;
+        private @ChangeMask int mChangeMask = 0;
         private @ActivityInfo.Config int mConfigSetMask = 0;
         private @WindowConfiguration.WindowConfig int mWindowSetMask = 0;
 
@@ -1234,9 +1253,9 @@
         private int mActivityWindowingMode = -1;
         private int mWindowingMode = -1;
 
-        public Change() {}
+        private Change() {}
 
-        protected Change(Parcel in) {
+        private Change(@NonNull Parcel in) {
             mConfiguration.readFromParcel(in);
             mFocusable = in.readBoolean();
             mHidden = in.readBoolean();
@@ -1273,7 +1292,7 @@
          *                 undefined state. Use this if you don't intend to use other. When false,
          *                 SurfaceFlinger Transactions will not merge.
          */
-        public void merge(Change other, boolean transfer) {
+        public void merge(@NonNull Change other, boolean transfer) {
             mConfiguration.setTo(other.mConfiguration, other.mConfigSetMask, other.mWindowSetMask);
             mConfigSetMask |= other.mConfigSetMask;
             mWindowSetMask |= other.mWindowSetMask;
@@ -1300,10 +1319,10 @@
                 mDragResizing = other.mDragResizing;
             }
             mChangeMask |= other.mChangeMask;
-            if (other.mActivityWindowingMode >= 0) {
+            if (other.mActivityWindowingMode >= WINDOWING_MODE_UNDEFINED) {
                 mActivityWindowingMode = other.mActivityWindowingMode;
             }
-            if (other.mWindowingMode >= 0) {
+            if (other.mWindowingMode >= WINDOWING_MODE_UNDEFINED) {
                 mWindowingMode = other.mWindowingMode;
             }
             if (other.mBoundsChangeSurfaceBounds != null) {
@@ -1327,6 +1346,7 @@
             return mActivityWindowingMode;
         }
 
+        @NonNull
         public Configuration getConfiguration() {
             return mConfiguration;
         }
@@ -1379,6 +1399,7 @@
             return mConfigAtTransitionEnd;
         }
 
+        @ChangeMask
         public int getChangeMask() {
             return mChangeMask;
         }
@@ -1397,14 +1418,17 @@
          * Returns the bounds to be used for scheduling the enter pip callback
          * or null if no callback is to be scheduled.
          */
+        @Nullable
         public Rect getEnterPipBounds() {
             return mPinnedBounds;
         }
 
+        @Nullable
         public SurfaceControl.Transaction getBoundsChangeTransaction() {
             return mBoundsChangeTransaction;
         }
 
+        @Nullable
         public Rect getBoundsChangeSurfaceBounds() {
             return mBoundsChangeSurfaceBounds;
         }
@@ -1471,7 +1495,7 @@
         }
 
         @Override
-        public void writeToParcel(Parcel dest, int flags) {
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
             mConfiguration.writeToParcel(dest, flags);
             dest.writeBoolean(mFocusable);
             dest.writeBoolean(mHidden);
@@ -1507,7 +1531,7 @@
 
         public static final Creator<Change> CREATOR = new Creator<Change>() {
             @Override
-            public Change createFromParcel(Parcel in) {
+            public Change createFromParcel(@NonNull Parcel in) {
                 return new Change(in);
             }
 
@@ -1521,6 +1545,7 @@
     /**
      * Holds information about a reparent/reorder operation in the hierarchy. This is separate from
      * Changes because they must be executed in the same order that they are added.
+     * @see com.android.server.wm.WindowOrganizerController#applyHierarchyOp
      * @hide
      */
     public static final class HierarchyOp implements Parcelable {
@@ -1549,6 +1574,36 @@
         public static final int HIERARCHY_OP_TYPE_SET_KEYGUARD_STATE = 22;
         public static final int HIERARCHY_OP_TYPE_SET_DISABLE_LAUNCH_ADJACENT = 23;
 
+        @IntDef(prefix = {"HIERARCHY_OP_TYPE_"}, value = {
+                HIERARCHY_OP_TYPE_REPARENT,
+                HIERARCHY_OP_TYPE_REORDER,
+                HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT,
+                HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT,
+                HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS,
+                HIERARCHY_OP_TYPE_LAUNCH_TASK,
+                HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT,
+                HIERARCHY_OP_TYPE_PENDING_INTENT,
+                HIERARCHY_OP_TYPE_START_SHORTCUT,
+                HIERARCHY_OP_TYPE_RESTORE_TRANSIENT_ORDER,
+                HIERARCHY_OP_TYPE_ADD_INSETS_FRAME_PROVIDER,
+                HIERARCHY_OP_TYPE_REMOVE_INSETS_FRAME_PROVIDER,
+                HIERARCHY_OP_TYPE_SET_ALWAYS_ON_TOP,
+                HIERARCHY_OP_TYPE_REMOVE_TASK,
+                HIERARCHY_OP_TYPE_FINISH_ACTIVITY,
+                HIERARCHY_OP_TYPE_CLEAR_ADJACENT_ROOTS,
+                HIERARCHY_OP_TYPE_SET_REPARENT_LEAF_TASK_IF_RELAUNCH,
+                HIERARCHY_OP_TYPE_ADD_TASK_FRAGMENT_OPERATION,
+                HIERARCHY_OP_TYPE_MOVE_PIP_ACTIVITY_TO_PINNED_TASK,
+                HIERARCHY_OP_TYPE_SET_IS_TRIMMABLE,
+                HIERARCHY_OP_TYPE_RESTORE_BACK_NAVIGATION,
+                HIERARCHY_OP_TYPE_SET_EXCLUDE_INSETS_TYPES,
+                HIERARCHY_OP_TYPE_SET_KEYGUARD_STATE,
+                HIERARCHY_OP_TYPE_SET_DISABLE_LAUNCH_ADJACENT
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        public @interface HierarchyOpType {
+        }
+
         // The following key(s) are for use with mLaunchOptions:
         // When launching a task (eg. from recents), this is the taskId to be launched.
         public static final String LAUNCH_KEY_TASK_ID = "android:transaction.hop.taskId";
@@ -1557,6 +1612,7 @@
         public static final String LAUNCH_KEY_SHORTCUT_CALLING_PACKAGE =
                 "android:transaction.hop.shortcut_calling_package";
 
+        @HierarchyOpType
         private final int mType;
 
         // Container we are performing the operation on.
@@ -1621,6 +1677,8 @@
 
         private boolean mLaunchAdjacentDisabled;
 
+        /** Creates a hierarchy operation for reparenting a container within the hierarchy. */
+        @NonNull
         public static HierarchyOp createForReparent(
                 @NonNull IBinder container, @Nullable IBinder reparent, boolean toTop) {
             return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_REPARENT)
@@ -1631,7 +1689,7 @@
         }
 
         /**
-         * Creates the {@link HierarchyOp} for the reorder operation.
+         * Creates a a hierarchy op for the reorder operation.
          *
          * @param container which needs to be reordered
          * @param toTop if true, the container reorders
@@ -1639,6 +1697,7 @@
          *                         reoredered among their respective siblings
          * @return
          */
+        @NonNull
         public static HierarchyOp createForReorder(@NonNull IBinder container, boolean toTop,
                 boolean includingParents) {
             return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_REORDER)
@@ -1649,9 +1708,11 @@
                     .build();
         }
 
-        public static HierarchyOp createForChildrenTasksReparent(IBinder currentParent,
-                IBinder newParent, int[] windowingModes, int[] activityTypes, boolean onTop,
-                boolean reparentTopOnly) {
+        /** Creates a hierarchy op for reparenting child tasks from one container to another. */
+        @NonNull
+        public static HierarchyOp createForChildrenTasksReparent(@Nullable IBinder currentParent,
+                @Nullable IBinder newParent, @Nullable int[] windowingModes,
+                @Nullable int[] activityTypes, boolean onTop, boolean reparentTopOnly) {
             return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT)
                     .setContainer(currentParent)
                     .setReparentContainer(newParent)
@@ -1662,8 +1723,10 @@
                     .build();
         }
 
-        public static HierarchyOp createForSetLaunchRoot(IBinder container,
-                int[] windowingModes, int[] activityTypes) {
+        /** Creates a hierarchy op for setting the launch root for tasks. */
+        @NonNull
+        public static HierarchyOp createForSetLaunchRoot(@Nullable IBinder container,
+                @Nullable int[] windowingModes, @Nullable int[] activityTypes) {
             return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT)
                     .setContainer(container)
                     .setWindowingModes(windowingModes)
@@ -1671,15 +1734,18 @@
                     .build();
         }
 
-        /** Create a hierarchy op for setting adjacent root tasks. */
-        public static HierarchyOp createForAdjacentRoots(IBinder root1, IBinder root2) {
+        /** Creates a hierarchy op for setting adjacent root tasks. */
+        @NonNull
+        public static HierarchyOp createForAdjacentRoots(
+                @Nullable IBinder root1, @Nullable IBinder root2) {
             return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS)
                     .setContainer(root1)
                     .setReparentContainer(root2)
                     .build();
         }
 
-        /** Create a hierarchy op for launching a task. */
+        /** Creates a hierarchy op for launching a task. */
+        @NonNull
         public static HierarchyOp createForTaskLaunch(int taskId, @Nullable Bundle options) {
             final Bundle fullOptions = options == null ? new Bundle() : options;
             fullOptions.putInt(LAUNCH_KEY_TASK_ID, taskId);
@@ -1689,7 +1755,8 @@
                     .build();
         }
 
-        /** Create a hierarchy op for starting a shortcut. */
+        /** Creates a hierarchy op for starting a shortcut. */
+        @NonNull
         public static HierarchyOp createForStartShortcut(@NonNull String callingPackage,
                 @NonNull ShortcutInfo shortcutInfo, @Nullable Bundle options) {
             final Bundle fullOptions = options == null ? new Bundle() : options;
@@ -1700,8 +1767,9 @@
                     .build();
         }
 
-        /** Create a hierarchy op for setting launch adjacent flag root. */
-        public static HierarchyOp createForSetLaunchAdjacentFlagRoot(IBinder container,
+        /** Creates a hierarchy op for setting launch adjacent flag root. */
+        @NonNull
+        public static HierarchyOp createForSetLaunchAdjacentFlagRoot(@Nullable IBinder container,
                 boolean clearRoot) {
             return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT)
                     .setContainer(container)
@@ -1709,8 +1777,9 @@
                     .build();
         }
 
-        /** Create a hierarchy op for disabling launch adjacent. */
-        public static HierarchyOp createForSetDisableLaunchAdjacent(IBinder container,
+        /** Creates a hierarchy op for disabling launch adjacent. */
+        @NonNull
+        public static HierarchyOp createForSetDisableLaunchAdjacent(@Nullable IBinder container,
                 boolean disabled) {
             return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_SET_DISABLE_LAUNCH_ADJACENT)
                     .setContainer(container)
@@ -1718,21 +1787,24 @@
                     .build();
         }
 
-        /** create a hierarchy op for deleting a task **/
+        /** Creates a hierarchy op for deleting a task **/
+        @NonNull
         public static HierarchyOp createForRemoveTask(@NonNull IBinder container) {
             return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_REMOVE_TASK)
                     .setContainer(container)
                     .build();
         }
 
-        /** Create a hierarchy op for clearing adjacent root tasks. */
+        /** Creates a hierarchy op for clearing adjacent root tasks. */
+        @NonNull
         public static HierarchyOp createForClearAdjacentRoots(@NonNull IBinder root) {
             return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_CLEAR_ADJACENT_ROOTS)
                     .setContainer(root)
                     .build();
         }
 
-        /** Create a hierarchy op for setting a task non-trimmable by recents. */
+        /** Creates a hierarchy op for setting a task non-trimmable by recents. */
+        @NonNull
         @FlaggedApi(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
         public static HierarchyOp createForSetTaskTrimmableFromRecents(@NonNull IBinder container,
                 boolean isTrimmableFromRecents) {
@@ -1743,7 +1815,7 @@
         }
 
         /** Only creates through {@link Builder}. */
-        private HierarchyOp(int type) {
+        private HierarchyOp(@HierarchyOpType int type) {
             mType = type;
         }
 
@@ -1773,7 +1845,7 @@
             mLaunchAdjacentDisabled = copy.mLaunchAdjacentDisabled;
         }
 
-        protected HierarchyOp(Parcel in) {
+        private HierarchyOp(@NonNull Parcel in) {
             mType = in.readInt();
             mContainer = in.readStrongBinder();
             mContainers = in.createBinderArray();
@@ -1799,6 +1871,7 @@
             mLaunchAdjacentDisabled = in.readBoolean();
         }
 
+        @HierarchyOpType
         public int getType() {
             return mType;
         }
@@ -1847,10 +1920,12 @@
             return mReparentTopOnly;
         }
 
+        @Nullable
         public int[] getWindowingModes() {
             return mWindowingModes;
         }
 
+        @Nullable
         public int[] getActivityTypes() {
             return mActivityTypes;
         }
@@ -1918,7 +1993,7 @@
         }
 
         /** Gets a string representation of a hierarchy-op type. */
-        public static String hopToString(int type) {
+        public static String hopToString(@HierarchyOpType int type) {
             switch (type) {
                 case HIERARCHY_OP_TYPE_REPARENT: return "reparent";
                 case HIERARCHY_OP_TYPE_REORDER: return "reorder";
@@ -2059,7 +2134,7 @@
         }
 
         @Override
-        public void writeToParcel(Parcel dest, int flags) {
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
             dest.writeInt(mType);
             dest.writeStrongBinder(mContainer);
             dest.writeBinderArray(mContainers);
@@ -2092,7 +2167,7 @@
 
         public static final Creator<HierarchyOp> CREATOR = new Creator<HierarchyOp>() {
             @Override
-            public HierarchyOp createFromParcel(Parcel in) {
+            public HierarchyOp createFromParcel(@NonNull Parcel in) {
                 return new HierarchyOp(in);
             }
 
@@ -2104,6 +2179,7 @@
 
         private static class Builder {
 
+            @HierarchyOpType
             private final int mType;
 
             @Nullable
@@ -2126,7 +2202,7 @@
             private boolean mReparentTopOnly;
 
             @Nullable
-            private int[]  mWindowingModes;
+            private int[] mWindowingModes;
 
             @Nullable
             private int[] mActivityTypes;
@@ -2164,7 +2240,7 @@
 
             private boolean mLaunchAdjacentDisabled;
 
-            Builder(int type) {
+            Builder(@HierarchyOpType int type) {
                 mType = type;
             }
 
@@ -2188,7 +2264,7 @@
                 return this;
             }
 
-            Builder setInsetsFrameOwner(IBinder owner) {
+            Builder setInsetsFrameOwner(@Nullable IBinder owner) {
                 mInsetsFrameOwner = owner;
                 return this;
             }
@@ -2280,6 +2356,7 @@
                 return this;
             }
 
+            @NonNull
             HierarchyOp build() {
                 final HierarchyOp hierarchyOp = new HierarchyOp(mType);
                 hierarchyOp.mContainer = mContainer;
@@ -2367,6 +2444,7 @@
             return mDelaySecondaryLastActivityRemoval;
         }
 
+        @NonNull
         Bundle toBundle() {
             final Bundle b = new Bundle();
             b.putBoolean(DELAY_PRIMARY_LAST_ACTIVITY_REMOVAL, mDelayPrimaryLastActivityRemoval);
diff --git a/core/java/android/window/WindowInfosListenerForTest.java b/core/java/android/window/WindowInfosListenerForTest.java
index ac9bec3..6461f2a 100644
--- a/core/java/android/window/WindowInfosListenerForTest.java
+++ b/core/java/android/window/WindowInfosListenerForTest.java
@@ -103,12 +103,6 @@
         public final boolean isFocusable;
 
         /**
-         * True if the window is preventing splitting
-         */
-        @SuppressLint("UnflaggedApi") // The API is only used for tests.
-        public final boolean isPreventSplitting;
-
-        /**
          * True if the window duplicates touches received to wallpaper.
          */
         @SuppressLint("UnflaggedApi") // The API is only used for tests.
@@ -133,8 +127,6 @@
             this.transform = transform;
             this.isTouchable = (inputConfig & InputConfig.NOT_TOUCHABLE) == 0;
             this.isFocusable = (inputConfig & InputConfig.NOT_FOCUSABLE) == 0;
-            this.isPreventSplitting = (inputConfig
-                            & InputConfig.PREVENT_SPLITTING) != 0;
             this.isDuplicateTouchToWallpaper = (inputConfig
                             & InputConfig.DUPLICATE_TOUCH_TO_WALLPAPER) != 0;
             this.isWatchOutsideTouch = (inputConfig
diff --git a/core/java/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java
index 20e3f6b..2911b0a 100644
--- a/core/java/android/window/WindowOnBackInvokedDispatcher.java
+++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java
@@ -464,7 +464,12 @@
      * Returns false if the legacy back behavior should be used.
      */
     public boolean isOnBackInvokedCallbackEnabled() {
-        return isOnBackInvokedCallbackEnabled(mChecker.getContext());
+        final Context hostContext = mChecker.getContext();
+        if (hostContext == null) {
+            Log.w(TAG, "OnBackInvokedCallback is disabled, host context is removed!");
+            return false;
+        }
+        return isOnBackInvokedCallbackEnabled(hostContext);
     }
 
     /**
@@ -695,7 +700,12 @@
          */
         public boolean checkApplicationCallbackRegistration(int priority,
                 OnBackInvokedCallback callback) {
-            if (!WindowOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled(getContext())
+            final Context hostContext = getContext();
+            if (hostContext == null) {
+                Log.w(TAG, "OnBackInvokedCallback is disabled, host context is removed!");
+                return false;
+            }
+            if (!WindowOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled(hostContext)
                     && !(callback instanceof CompatOnBackInvokedCallback)) {
                 Log.w(TAG,
                         "OnBackInvokedCallback is not enabled for the application."
@@ -720,7 +730,7 @@
             return true;
         }
 
-        private Context getContext() {
+        @Nullable private Context getContext() {
             return mContext.get();
         }
     }
diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig
index ccb1e2b..be0b4fe 100644
--- a/core/java/android/window/flags/lse_desktop_experience.aconfig
+++ b/core/java/android/window/flags/lse_desktop_experience.aconfig
@@ -478,10 +478,10 @@
 }
 
 flag {
-    name: "enable_multiple_desktops"
+    name: "enable_multiple_desktops_frontend"
     namespace: "lse_desktop_experience"
-    description: "Enable multiple desktop sessions for desktop windowing."
-    bug: "379158791"
+    description: "Enable multiple desktop sessions for desktop windowing (frontend)."
+    bug: "362720309"
 }
 
 flag {
@@ -531,8 +531,11 @@
 }
 
 flag {
-    name: "enable_desktop_wallpaper_activity_on_system_user"
+    name: "enable_desktop_wallpaper_activity_for_system_user"
     namespace: "lse_desktop_experience"
     description: "Enables starting DesktopWallpaperActivity on system user."
     bug: "385294350"
+    metadata {
+       purpose: PURPOSE_BUGFIX
+    }
 }
\ No newline at end of file
diff --git a/core/java/android/window/flags/responsible_apis.aconfig b/core/java/android/window/flags/responsible_apis.aconfig
index d5ba32c..4b5adfc 100644
--- a/core/java/android/window/flags/responsible_apis.aconfig
+++ b/core/java/android/window/flags/responsible_apis.aconfig
@@ -75,4 +75,9 @@
     bug: "362575865"
 }
 
-
+flag {
+    name: "bal_strict_mode_grace_period"
+    namespace: "responsible_apis"
+    description: "Strict mode violation triggered by grace period usage"
+    bug: "384807495"
+}
diff --git a/core/java/android/window/flags/window_surfaces.aconfig b/core/java/android/window/flags/window_surfaces.aconfig
index bb47707..8ff2e6a 100644
--- a/core/java/android/window/flags/window_surfaces.aconfig
+++ b/core/java/android/window/flags/window_surfaces.aconfig
@@ -91,6 +91,14 @@
 }
 
 flag {
+  name: "ignore_corner_radius_and_shadows"
+  namespace: "window_surfaces"
+  description: "Ignore the corner radius and shadows of a SurfaceControl"
+  bug: "375624570"
+  is_fixed_read_only: true
+} # ignore_corner_radius_and_shadows
+
+flag {
     name: "jank_api"
     namespace: "window_surfaces"
     description: "Adds the jank data listener to AttachedSurfaceControl"
diff --git a/core/java/android/window/flags/windowing_frontend.aconfig b/core/java/android/window/flags/windowing_frontend.aconfig
index 7eabd17..de3e0d3 100644
--- a/core/java/android/window/flags/windowing_frontend.aconfig
+++ b/core/java/android/window/flags/windowing_frontend.aconfig
@@ -113,13 +113,6 @@
 }
 
 flag {
-    name: "predictive_back_system_anims"
-    namespace: "systemui"
-    description: "Predictive back for system animations"
-    bug: "320510464"
-}
-
-flag {
     name: "remove_activity_starter_dream_callback"
     namespace: "windowing_frontend"
     description: "Avoid a race with DreamManagerService callbacks for isDreaming by checking Activity state directly"
@@ -292,17 +285,6 @@
 }
 
 flag {
-  name: "migrate_predictive_back_transition"
-  namespace: "windowing_frontend"
-  description: "Create transition when visibility change from predictive back"
-  bug: "347168362"
-  is_fixed_read_only: true
-  metadata {
-    purpose: PURPOSE_BUGFIX
-  }
-}
-
-flag {
   name: "always_capture_activity_snapshot"
   namespace: "windowing_frontend"
   description: "Always capture activity snapshot regardless predictive back status"
@@ -314,17 +296,6 @@
 }
 
 flag {
-  name: "remove_starting_window_wait_for_multi_transitions"
-  namespace: "windowing_frontend"
-  description: "Avoid remove starting window too early when playing multiple transitions"
-  bug: "362347290"
-  is_fixed_read_only: true
-  metadata {
-    purpose: PURPOSE_BUGFIX
-  }
-}
-
-flag {
   name: "ensure_keyguard_does_transition_starting"
   namespace: "windowing_frontend"
   description: "Ensure that keyguard is the one starting transitions, instead of delegating to Core"
@@ -445,7 +416,7 @@
 
 flag {
     name: "port_window_size_animation"
-    namespace: "systemui"
+    namespace: "windowing_frontend"
     description: "Port window-resize animation from legacy to shell"
     bug: "384976265"
 }
diff --git a/core/java/android/window/flags/windowing_sdk.aconfig b/core/java/android/window/flags/windowing_sdk.aconfig
index abd93cf..b38feee 100644
--- a/core/java/android/window/flags/windowing_sdk.aconfig
+++ b/core/java/android/window/flags/windowing_sdk.aconfig
@@ -1,8 +1,10 @@
+# proto-file: build/make/tools/aconfig/aconfig_protos/protos/aconfig.proto
+# proto-message: flag_declarations
+# Project link: http://gantry/projects/android_platform_windowing_sdk
+
 package: "com.android.window.flags"
 container: "system"
 
-# Project link: https://gantry.corp.google.com/projects/android_platform_windowing_sdk/changes
-
 flag {
     namespace: "windowing_sdk"
     name: "activity_embedding_overlay_presentation_flag"
diff --git a/core/java/com/android/internal/accessibility/util/AccessibilityUtils.java b/core/java/com/android/internal/accessibility/util/AccessibilityUtils.java
index 0b1ecf7..d03bb5c 100644
--- a/core/java/com/android/internal/accessibility/util/AccessibilityUtils.java
+++ b/core/java/com/android/internal/accessibility/util/AccessibilityUtils.java
@@ -29,6 +29,7 @@
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
 import android.os.Build;
 import android.os.UserHandle;
 import android.provider.Settings;
@@ -351,4 +352,24 @@
         }
         return result;
     }
+
+    /** Returns the {@link ComponentName} of an installed accessibility service by label. */
+    @Nullable
+    public static ComponentName getInstalledAccessibilityServiceComponentNameByLabel(
+            Context context, String label) {
+        AccessibilityManager accessibilityManager =
+                context.getSystemService(AccessibilityManager.class);
+        List<AccessibilityServiceInfo> serviceInfos =
+                accessibilityManager.getInstalledAccessibilityServiceList();
+
+        for (AccessibilityServiceInfo service : serviceInfos) {
+            final ServiceInfo serviceInfo = service.getResolveInfo().serviceInfo;
+            if (label.equals(serviceInfo.loadLabel(context.getPackageManager()).toString())
+                    && (serviceInfo.applicationInfo.isSystemApp()
+                            || serviceInfo.applicationInfo.isUpdatedSystemApp())) {
+                return new ComponentName(serviceInfo.packageName, serviceInfo.name);
+            }
+        }
+        return null;
+    }
 }
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index ca4d1b6..3bdf3d6 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -21,6 +21,7 @@
 import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_SHARE_WITH_PERSONAL;
 import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CANT_SHARE_WITH_WORK;
 import static android.app.admin.DevicePolicyResources.Strings.Core.RESOLVER_CROSS_PROFILE_BLOCKED_TITLE;
+import static android.content.ContentProvider.getUriWithoutUserId;
 import static android.content.ContentProvider.getUserIdFromUri;
 import static android.stats.devicepolicy.DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_PERSONAL;
 import static android.stats.devicepolicy.DevicePolicyEnums.RESOLVER_EMPTY_STATE_NO_SHARING_TO_WORK;
@@ -40,7 +41,9 @@
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.ActivityOptions;
+import android.app.IUriGrantsManager;
 import android.app.SharedElementCallback;
+import android.app.UriGrantsManager;
 import android.app.prediction.AppPredictionContext;
 import android.app.prediction.AppPredictionManager;
 import android.app.prediction.AppPredictor;
@@ -77,6 +80,7 @@
 import android.graphics.Path;
 import android.graphics.drawable.AnimatedVectorDrawable;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
 import android.metrics.LogMaker;
 import android.net.Uri;
 import android.os.AsyncTask;
@@ -86,6 +90,7 @@
 import android.os.Message;
 import android.os.Parcelable;
 import android.os.PatternMatcher;
+import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -692,7 +697,11 @@
                     targets = null;
                     break;
                 }
-                targets[i] = (ChooserTarget) pa[i];
+                ChooserTarget chooserTarget = (ChooserTarget) pa[i];
+                if (!hasValidIcon(chooserTarget)) {
+                    chooserTarget = removeIcon(chooserTarget);
+                }
+                targets[i] = chooserTarget;
             }
             mCallerChooserTargets = targets;
         }
@@ -4214,4 +4223,43 @@
     private boolean shouldNearbyShareBeIncludedAsActionButton() {
         return !shouldNearbyShareBeFirstInRankedRow();
     }
+
+    private boolean hasValidIcon(ChooserTarget target) {
+        Icon icon = target.getIcon();
+        if (icon == null) {
+            return true;
+        }
+        if (icon.getType() == Icon.TYPE_URI || icon.getType() == Icon.TYPE_URI_ADAPTIVE_BITMAP) {
+            Uri uri = icon.getUri();
+            try {
+                getUriGrantsManager().checkGrantUriPermission_ignoreNonSystem(
+                        getLaunchedFromUid(),
+                        getPackageName(),
+                        getUriWithoutUserId(uri),
+                        Intent.FLAG_GRANT_READ_URI_PERMISSION,
+                        getUserIdFromUri(uri)
+                );
+            } catch (SecurityException | RemoteException e) {
+                Log.e(TAG, "Failed to get URI permission for: " + uri, e);
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private IUriGrantsManager getUriGrantsManager() {
+        return UriGrantsManager.getService();
+    }
+
+    private static ChooserTarget removeIcon(ChooserTarget target) {
+        if (target == null) {
+            return null;
+        }
+        return new ChooserTarget(
+                target.getTitle(),
+                null,
+                target.getScore(),
+                target.getComponentName(),
+                target.getIntentExtras());
+    }
 }
diff --git a/core/java/com/android/internal/app/LocalePickerWithRegion.java b/core/java/com/android/internal/app/LocalePickerWithRegion.java
index ffffefa..9667a7b 100644
--- a/core/java/com/android/internal/app/LocalePickerWithRegion.java
+++ b/core/java/com/android/internal/app/LocalePickerWithRegion.java
@@ -34,7 +34,6 @@
 
 import com.android.internal.R;
 
-import java.util.HashSet;
 import java.util.Locale;
 import java.util.Set;
 
@@ -273,8 +272,9 @@
                 || mIsNumberingSystem) {
             if (mListener != null) {
                 mListener.onLocaleSelected(locale);
+            } else {
+                returnToParentFrame();
             }
-            returnToParentFrame();
         } else {
             LocalePickerWithRegion selector;
             if (mayHaveDifferentNumberingSystem) {
diff --git a/core/java/com/android/internal/jank/Cuj.java b/core/java/com/android/internal/jank/Cuj.java
index fc41537..d1adfc9 100644
--- a/core/java/com/android/internal/jank/Cuj.java
+++ b/core/java/com/android/internal/jank/Cuj.java
@@ -246,8 +246,19 @@
      */
     public static final int CUJ_DESKTOP_MODE_UNMAXIMIZE_WINDOW = 119;
 
+    /**
+     * Track moving overview task to desktop interaction from overview menu.
+     *
+     * <p> Tracking starts when the overview task is moved to desktop via the overview menu.
+     * Tracking finishes when successfully made a call to `IDesktopMode.moveToDesktop`,
+     * without waiting for transition completion.
+     * </p>
+     * TODO(b/387471509): Update the CUJ to wait for transition completion.
+     */
+    public static final int CUJ_DESKTOP_MODE_ENTER_FROM_OVERVIEW_MENU = 120;
+
     // 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_UNMAXIMIZE_WINDOW;
+    @VisibleForTesting static final int LAST_CUJ = CUJ_DESKTOP_MODE_ENTER_FROM_OVERVIEW_MENU;
 
     /** @hide */
     @IntDef({
@@ -358,7 +369,8 @@
             CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE,
             CUJ_DESKTOP_MODE_EXIT_MODE_ON_LAST_WINDOW_CLOSE,
             CUJ_DESKTOP_MODE_SNAP_RESIZE,
-            CUJ_DESKTOP_MODE_UNMAXIMIZE_WINDOW
+            CUJ_DESKTOP_MODE_UNMAXIMIZE_WINDOW,
+            CUJ_DESKTOP_MODE_ENTER_FROM_OVERVIEW_MENU
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface CujType {}
@@ -480,6 +492,7 @@
         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;
         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_DESKTOP_MODE_UNMAXIMIZE_WINDOW] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__DESKTOP_MODE_UNMAXIMIZE_WINDOW;
+        CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_DESKTOP_MODE_ENTER_FROM_OVERVIEW_MENU] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__DESKTOP_MODE_ENTER_FROM_OVERVIEW_MENU;
     }
 
     private Cuj() {
@@ -714,6 +727,8 @@
                 return "DESKTOP_MODE_SNAP_RESIZE";
             case CUJ_DESKTOP_MODE_UNMAXIMIZE_WINDOW:
                 return "DESKTOP_MODE_UNMAXIMIZE_WINDOW";
+            case CUJ_DESKTOP_MODE_ENTER_FROM_OVERVIEW_MENU:
+                return "DESKTOP_MODE_ENTER_FROM_OVERVIEW_MENU";
         }
         return "UNKNOWN";
     }
diff --git a/core/java/com/android/internal/notification/SystemNotificationChannels.java b/core/java/com/android/internal/notification/SystemNotificationChannels.java
index 4aebde5..972c2ea 100644
--- a/core/java/com/android/internal/notification/SystemNotificationChannels.java
+++ b/core/java/com/android/internal/notification/SystemNotificationChannels.java
@@ -49,6 +49,7 @@
     public static final String NETWORK_ALERTS = "NETWORK_ALERTS";
     public static final String NETWORK_AVAILABLE = "NETWORK_AVAILABLE";
     public static final String VPN = "VPN";
+    public static final String TIME = "TIME";
     /**
      * @deprecated Legacy device admin channel with low importance which is no longer used,
      *  Use the high importance {@link #DEVICE_ADMIN} channel instead.
@@ -67,6 +68,7 @@
     @Deprecated public static final String SYSTEM_CHANGES_DEPRECATED = "SYSTEM_CHANGES";
     public static final String SYSTEM_CHANGES = "SYSTEM_CHANGES_ALERTS";
     public static final String ACCESSIBILITY_MAGNIFICATION = "ACCESSIBILITY_MAGNIFICATION";
+    public static final String ACCESSIBILITY_HEARING_DEVICE = "ACCESSIBILITY_HEARING_DEVICE";
     public static final String ACCESSIBILITY_SECURITY_POLICY = "ACCESSIBILITY_SECURITY_POLICY";
     public static final String ABUSIVE_BACKGROUND_APPS = "ABUSIVE_BACKGROUND_APPS";
 
@@ -145,6 +147,12 @@
                 NotificationManager.IMPORTANCE_LOW);
         channelsList.add(vpn);
 
+        final NotificationChannel time = new NotificationChannel(
+                TIME,
+                context.getString(R.string.notification_channel_system_time),
+                NotificationManager.IMPORTANCE_DEFAULT);
+        channelsList.add(time);
+
         final NotificationChannel deviceAdmin = new NotificationChannel(
                 DEVICE_ADMIN,
                 getDeviceAdminNotificationChannelName(context),
@@ -203,6 +211,13 @@
         newFeaturePrompt.setBlockable(true);
         channelsList.add(newFeaturePrompt);
 
+        final NotificationChannel accessibilityHearingDeviceChannel = new NotificationChannel(
+                ACCESSIBILITY_HEARING_DEVICE,
+                context.getString(R.string.notification_channel_accessibility_hearing_device),
+                NotificationManager.IMPORTANCE_HIGH);
+        accessibilityHearingDeviceChannel.setBlockable(true);
+        channelsList.add(accessibilityHearingDeviceChannel);
+
         final NotificationChannel accessibilitySecurityPolicyChannel = new NotificationChannel(
                 ACCESSIBILITY_SECURITY_POLICY,
                 context.getString(R.string.notification_channel_accessibility_security_policy),
diff --git a/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java b/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java
index e2005d7..ee897cd 100644
--- a/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java
+++ b/core/java/com/android/internal/os/BatteryStatsHistoryIterator.java
@@ -45,6 +45,8 @@
     private boolean mNextItemReady;
     private boolean mTimeInitialized;
     private boolean mClosed;
+    private long mBaseMonotonicTime;
+    private long mBaseTimeUtc;
 
     public BatteryStatsHistoryIterator(@NonNull BatteryStatsHistory history, long startTimeMs,
             long endTimeMs) {
@@ -84,24 +86,25 @@
             }
 
             if (!mTimeInitialized) {
-                mHistoryItem.time = mBatteryStatsHistory.getHistoryBufferStartTime(p);
+                mBaseMonotonicTime = mBatteryStatsHistory.getHistoryBufferStartTime(p);
+                mHistoryItem.time = mBaseMonotonicTime;
                 mTimeInitialized = true;
             }
 
-            final long lastMonotonicTimeMs = mHistoryItem.time;
-            final long lastWalltimeMs = mHistoryItem.currentTime;
             try {
                 readHistoryDelta(p, mHistoryItem);
             } catch (Throwable t) {
                 Slog.wtf(TAG, "Corrupted battery history", t);
                 break;
             }
-            if (mHistoryItem.cmd != BatteryStats.HistoryItem.CMD_CURRENT_TIME
-                    && mHistoryItem.cmd != BatteryStats.HistoryItem.CMD_RESET
-                    && lastWalltimeMs != 0) {
-                mHistoryItem.currentTime =
-                        lastWalltimeMs + (mHistoryItem.time - lastMonotonicTimeMs);
+
+            if (mHistoryItem.cmd == BatteryStats.HistoryItem.CMD_CURRENT_TIME
+                    || mHistoryItem.cmd == BatteryStats.HistoryItem.CMD_RESET) {
+                mBaseTimeUtc = mHistoryItem.currentTime - (mHistoryItem.time - mBaseMonotonicTime);
             }
+
+            mHistoryItem.currentTime = mBaseTimeUtc + (mHistoryItem.time - mBaseMonotonicTime);
+
             if (mEndTimeMs != 0 && mHistoryItem.time >= mEndTimeMs) {
                 break;
             }
diff --git a/core/java/com/android/internal/policy/IKeyguardService.aidl b/core/java/com/android/internal/policy/IKeyguardService.aidl
index d62c8f3..73c2265 100644
--- a/core/java/com/android/internal/policy/IKeyguardService.aidl
+++ b/core/java/com/android/internal/policy/IKeyguardService.aidl
@@ -53,21 +53,21 @@
      *
      * @param pmSleepReason One of PowerManager.GO_TO_SLEEP_REASON_*, detailing the specific reason
      * we're going to sleep, such as GO_TO_SLEEP_REASON_POWER_BUTTON or GO_TO_SLEEP_REASON_TIMEOUT.
-     * @param cameraGestureTriggered whether the camera gesture was triggered between
-     *                               {@link #onStartedGoingToSleep} and this method; if it's been
-     *                               triggered, we shouldn't lock the device.
+     * @param powerButtonLaunchGestureTriggered whether the power button double tap gesture was
+     *                               triggered between {@link #onStartedGoingToSleep} and this
+     *                               method; if it's been triggered, we shouldn't lock the device.
      */
-    void onFinishedGoingToSleep(int pmSleepReason, boolean cameraGestureTriggered);
+    void onFinishedGoingToSleep(int pmSleepReason, boolean powerButtonLaunchGestureTriggered);
 
     /**
      * Called when the device has started waking up.
 
      * @param pmWakeReason One of PowerManager.WAKE_REASON_*, detailing the reason we're waking up,
      * such as WAKE_REASON_POWER_BUTTON or WAKE_REASON_GESTURE.
-     * @param cameraGestureTriggered Whether we're waking up due to a power button double tap
-     * gesture.
+     * @param powerButtonLaunchGestureTriggered Whether we're waking up due to a power button
+     * double tap gesture.
      */
-    void onStartedWakingUp(int pmWakeReason,  boolean cameraGestureTriggered);
+    void onStartedWakingUp(int pmWakeReason,  boolean powerButtonLaunchGestureTriggered);
 
     /**
      * Called when the device has finished waking up.
diff --git a/core/java/com/android/internal/ravenwood/RavenwoodEnvironment.java b/core/java/com/android/internal/ravenwood/RavenwoodEnvironment.java
index 8df3f2a..e522b50 100644
--- a/core/java/com/android/internal/ravenwood/RavenwoodEnvironment.java
+++ b/core/java/com/android/internal/ravenwood/RavenwoodEnvironment.java
@@ -94,14 +94,21 @@
 
         /** Used for testing */
         @Disabled
-        @ChangeId public static final long TEST_COMPAT_ID_2 = 368131701L;
+        @ChangeId
+        public static final long TEST_COMPAT_ID_2 = 368131701L;
 
         /** Used for testing */
         @EnabledAfter(targetSdkVersion = S)
-        @ChangeId public static final long TEST_COMPAT_ID_3 = 368131659L;
+        @ChangeId
+        public static final long TEST_COMPAT_ID_3 = 368131659L;
 
         /** Used for testing */
         @EnabledAfter(targetSdkVersion = UPSIDE_DOWN_CAKE)
-        @ChangeId public static final long TEST_COMPAT_ID_4 = 368132057L;
+        @ChangeId
+        public static final long TEST_COMPAT_ID_4 = 368132057L;
+
+        /** Used for testing */
+        @ChangeId
+        public static final long TEST_COMPAT_ID_5 = 387558811L;
     }
 }
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index c834dde..555374a 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -124,6 +124,11 @@
     void onCameraLaunchGestureDetected(int source);
 
     /**
+     * Notifies the status bar that a wallet launch gesture has been detected.
+     */
+    void onWalletLaunchGestureDetected();
+
+    /**
      * Notifies the status bar that the Emergency Action launch gesture has been detected.
      *
      * TODO(b/169175022) Update method name and docs when feature name is locked.
diff --git a/core/java/com/android/internal/widget/NotificationExpandButton.java b/core/java/com/android/internal/widget/NotificationExpandButton.java
index 751cfde..80bc4fd 100644
--- a/core/java/com/android/internal/widget/NotificationExpandButton.java
+++ b/core/java/com/android/internal/widget/NotificationExpandButton.java
@@ -27,12 +27,12 @@
 import android.graphics.drawable.LayerDrawable;
 import android.util.AttributeSet;
 import android.view.RemotableViewMethod;
-import android.view.View;
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.Button;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
+import android.widget.LinearLayout;
 import android.widget.RemoteViews;
 import android.widget.TextView;
 
@@ -49,12 +49,15 @@
     private Drawable mPillDrawable;
     private TextView mNumberView;
     private ImageView mIconView;
+    private LinearLayout mPillView;
     private boolean mExpanded;
     private int mNumber;
     private int mDefaultPillColor;
     private int mDefaultTextColor;
     private int mHighlightPillColor;
     private int mHighlightTextColor;
+    // Track whether this ever had mExpanded = true, so that we don't highlight it anymore.
+    private boolean mWasExpanded = false;
 
     public NotificationExpandButton(Context context) {
         this(context, null, 0, 0);
@@ -78,8 +81,8 @@
     protected void onFinishInflate() {
         super.onFinishInflate();
 
-        final View pillView = findViewById(R.id.expand_button_pill);
-        final LayerDrawable layeredPill = (LayerDrawable) pillView.getBackground();
+        mPillView = findViewById(R.id.expand_button_pill);
+        final LayerDrawable layeredPill = (LayerDrawable) mPillView.getBackground();
         mPillDrawable = layeredPill.findDrawableByLayerId(R.id.expand_button_pill_colorized_layer);
         mNumberView = findViewById(R.id.expand_button_number);
         mIconView = findViewById(R.id.expand_button_icon);
@@ -133,6 +136,7 @@
         int contentDescriptionId;
         if (mExpanded) {
             if (notificationsRedesignTemplates()) {
+                mWasExpanded = true;
                 drawableId = R.drawable.ic_notification_2025_collapse;
             } else {
                 drawableId = R.drawable.ic_collapse_notification;
@@ -149,8 +153,12 @@
         setContentDescription(mContext.getText(contentDescriptionId));
         mIconView.setImageDrawable(getContext().getDrawable(drawableId));
 
-        // changing the expanded state can affect the number display
-        updateNumber();
+        if (!notificationsRedesignTemplates()) {
+            // changing the expanded state can affect the number display
+            updateNumber();
+        } else {
+            updateColors();
+        }
     }
 
     private void updateNumber() {
@@ -164,31 +172,76 @@
             mNumberView.setVisibility(GONE);
         }
 
-        // changing number can affect the color
+        // changing number can affect the color and padding
         updateColors();
+        updatePadding();
+    }
+
+    private void updatePadding() {
+        if (!notificationsRedesignTemplates()) {
+            return;
+        }
+
+        // Reduce the padding at the end when showing the number, since the arrow icon has more
+        // inherent spacing than the number does. This makes the content look more centered.
+        // Vertical padding remains unchanged.
+        int reducedPadding = getResources().getDimensionPixelSize(
+                R.dimen.notification_2025_expand_button_reduced_end_padding);
+        int normalPadding = getResources().getDimensionPixelSize(
+                R.dimen.notification_2025_expand_button_horizontal_icon_padding);
+        mPillView.setPaddingRelative(
+                /* start = */ normalPadding,
+                /* top = */ mPillView.getPaddingTop(),
+                /* end = */ shouldShowNumber() ? reducedPadding : normalPadding,
+                /* bottom = */ mPillView.getPaddingBottom()
+        );
+    }
+
+    /**
+     * Use highlight colors for the expander for groups (when the number is showing) that haven't
+     * been opened before, as long as the colors are available.
+     */
+    private boolean shouldBeHighlighted() {
+        return !mWasExpanded && shouldShowNumber()
+                && mHighlightPillColor != 0 && mHighlightTextColor != 0;
     }
 
     private void updateColors() {
-        if (shouldShowNumber()) {
-            if (mHighlightPillColor != 0) {
+        if (notificationsRedesignTemplates()) {
+            if (shouldBeHighlighted()) {
                 mPillDrawable.setTintList(ColorStateList.valueOf(mHighlightPillColor));
-            }
-            mIconView.setColorFilter(mHighlightTextColor);
-            if (mHighlightTextColor != 0) {
+                mIconView.setColorFilter(mHighlightTextColor);
                 mNumberView.setTextColor(mHighlightTextColor);
+            } else {
+                mPillDrawable.setTintList(ColorStateList.valueOf(mDefaultPillColor));
+                mIconView.setColorFilter(mDefaultTextColor);
+                mNumberView.setTextColor(mDefaultTextColor);
             }
         } else {
-            if (mDefaultPillColor != 0) {
-                mPillDrawable.setTintList(ColorStateList.valueOf(mDefaultPillColor));
-            }
-            mIconView.setColorFilter(mDefaultTextColor);
-            if (mDefaultTextColor != 0) {
-                mNumberView.setTextColor(mDefaultTextColor);
+            if (shouldShowNumber()) {
+                if (mHighlightPillColor != 0) {
+                    mPillDrawable.setTintList(ColorStateList.valueOf(mHighlightPillColor));
+                }
+                mIconView.setColorFilter(mHighlightTextColor);
+                if (mHighlightTextColor != 0) {
+                    mNumberView.setTextColor(mHighlightTextColor);
+                }
+            } else {
+                if (mDefaultPillColor != 0) {
+                    mPillDrawable.setTintList(ColorStateList.valueOf(mDefaultPillColor));
+                }
+                mIconView.setColorFilter(mDefaultTextColor);
+                if (mDefaultTextColor != 0) {
+                    mNumberView.setTextColor(mDefaultTextColor);
+                }
             }
         }
     }
 
     private boolean shouldShowNumber() {
+        if (notificationsRedesignTemplates()) {
+            return mNumber > 1;
+        }
         return !mExpanded && mNumber > 1;
     }
 
@@ -230,7 +283,7 @@
 
     /**
      * Sets the number shown inside the expand button.
-     * This only appears when the expand button is collapsed, and when greater than 1.
+     * This only appears when {@link this#shouldShowNumber()} is true.
      */
     @RemotableViewMethod
     public void setNumber(int number) {
diff --git a/core/java/com/android/internal/widget/NotificationProgressBar.java b/core/java/com/android/internal/widget/NotificationProgressBar.java
index 8cd7843..f0b5493 100644
--- a/core/java/com/android/internal/widget/NotificationProgressBar.java
+++ b/core/java/com/android/internal/widget/NotificationProgressBar.java
@@ -23,6 +23,7 @@
 import android.content.res.ColorStateList;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
+import android.graphics.Color;
 import android.graphics.Matrix;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
@@ -31,6 +32,7 @@
 import android.os.Bundle;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.util.Pair;
 import android.view.RemotableViewMethod;
 import android.widget.ProgressBar;
 import android.widget.RemoteViews;
@@ -40,14 +42,12 @@
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Preconditions;
-import com.android.internal.widget.NotificationProgressDrawable.Part;
-import com.android.internal.widget.NotificationProgressDrawable.Point;
-import com.android.internal.widget.NotificationProgressDrawable.Segment;
 
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.SortedSet;
 import java.util.TreeSet;
 
@@ -56,18 +56,25 @@
  * represent Notification ProgressStyle progress, such as for ridesharing and navigation.
  */
 @RemoteViews.RemoteView
-public final class NotificationProgressBar extends ProgressBar {
+public final class NotificationProgressBar extends ProgressBar implements
+        NotificationProgressDrawable.BoundsChangeListener {
     private static final String TAG = "NotificationProgressBar";
 
     private NotificationProgressDrawable mNotificationProgressDrawable;
+    private final Rect mProgressDrawableBounds = new Rect();
 
     private NotificationProgressModel mProgressModel;
 
     @Nullable
-    private List<Part> mProgressDrawableParts = null;
+    private List<Part> mParts = null;
+
+    // List of drawable parts before segment splitting by process.
+    @Nullable
+    private List<NotificationProgressDrawable.Part> mProgressDrawableParts = null;
 
     @Nullable
     private Drawable mTracker = null;
+    private boolean mHasTrackerIcon = false;
 
     /** @see R.styleable#NotificationProgressBar_trackerHeight */
     private final int mTrackerHeight;
@@ -76,7 +83,13 @@
     private final Matrix mMatrix = new Matrix();
     private Matrix mTrackerDrawMatrix = null;
 
-    private float mScale = 0;
+    private float mProgressFraction = 0;
+    /**
+     * The location of progress on the stretched and rescaled progress bar, in fraction. Used for
+     * calculating the tracker position. If stretching and rescaling is not needed, ==
+     * mProgressFraction.
+     */
+    private float mAdjustedProgressFraction = 0;
     /** Indicates whether mTrackerPos needs to be recalculated before the tracker is drawn. */
     private boolean mTrackerPosIsDirty = false;
 
@@ -104,12 +117,13 @@
 
         try {
             mNotificationProgressDrawable = getNotificationProgressDrawable();
+            mNotificationProgressDrawable.setBoundsChangeListener(this);
         } catch (IllegalStateException ex) {
             Log.e(TAG, "Can't get NotificationProgressDrawable", ex);
         }
 
         // Supports setting the tracker in xml, but ProgressStyle notifications set/override it
-        // via {@code setProgressTrackerIcon}.
+        // via {@code #setProgressTrackerIcon}.
         final Drawable tracker = a.getDrawable(R.styleable.NotificationProgressBar_tracker);
         setTracker(tracker);
 
@@ -137,20 +151,25 @@
             final int indeterminateColor = mProgressModel.getIndeterminateColor();
             setIndeterminateTintList(ColorStateList.valueOf(indeterminateColor));
         } else {
+            // TODO: b/372908709 - maybe don't rerun the entire calculation every time the
+            //  progress model is updated? For example, if the segments and parts aren't changed,
+            //  there is no need to call `processAndConvertToViewParts` again.
+
             final int progress = mProgressModel.getProgress();
             final int progressMax = mProgressModel.getProgressMax();
-            mProgressDrawableParts = processAndConvertToDrawableParts(mProgressModel.getSegments(),
+
+            mParts = processAndConvertToViewParts(mProgressModel.getSegments(),
                     mProgressModel.getPoints(),
                     progress,
-                    progressMax,
-                    mProgressModel.isStyledByProgress());
-
-            if (mNotificationProgressDrawable != null) {
-                mNotificationProgressDrawable.setParts(mProgressDrawableParts);
-            }
+                    progressMax);
 
             setMax(progressMax);
             setProgress(progress);
+
+            if (mNotificationProgressDrawable != null
+                    && mNotificationProgressDrawable.getBounds().width() != 0) {
+                updateDrawableParts();
+            }
         }
     }
 
@@ -200,9 +219,7 @@
         } else {
             progressTrackerDrawable = null;
         }
-        return () -> {
-            setTracker(progressTrackerDrawable);
-        };
+        return () -> setTracker(progressTrackerDrawable);
     }
 
     private void setTracker(@Nullable Drawable tracker) {
@@ -226,8 +243,14 @@
         final boolean trackerSizeChanged = trackerSizeChanged(tracker, mTracker);
 
         mTracker = tracker;
-        if (mNotificationProgressDrawable != null) {
-            mNotificationProgressDrawable.setHasTrackerIcon(mTracker != null);
+        final boolean hasTrackerIcon = (mTracker != null);
+        if (mHasTrackerIcon != hasTrackerIcon) {
+            mHasTrackerIcon = hasTrackerIcon;
+            if (mNotificationProgressDrawable != null
+                    && mNotificationProgressDrawable.getBounds().width() != 0
+                    && mProgressModel.isStyledByProgress()) {
+                updateDrawableParts();
+            }
         }
 
         configureTrackerBounds();
@@ -293,6 +316,8 @@
         mTrackerDrawMatrix.postTranslate(Math.round(dx), Math.round(dy));
     }
 
+    // This updates the visual position of the progress indicator, i.e., the tracker. It doesn't
+    // update the NotificationProgressDrawable, which is updated by {@code #setProgressModel}.
     @Override
     public synchronized void setProgress(int progress) {
         super.setProgress(progress);
@@ -300,6 +325,8 @@
         onMaybeVisualProgressChanged();
     }
 
+    // This updates the visual position of the progress indicator, i.e., the tracker. It doesn't
+    // update the NotificationProgressDrawable, which is updated by {@code #setProgressModel}.
     @Override
     public void setProgress(int progress, boolean animate) {
         // Animation isn't supported by NotificationProgressBar.
@@ -308,6 +335,8 @@
         onMaybeVisualProgressChanged();
     }
 
+    // This updates the visual position of the progress indicator, i.e., the tracker. It doesn't
+    // update the NotificationProgressDrawable, which is updated by {@code #setProgressModel}.
     @Override
     public synchronized void setMin(int min) {
         super.setMin(min);
@@ -315,6 +344,8 @@
         onMaybeVisualProgressChanged();
     }
 
+    // This updates the visual position of the progress indicator, i.e., the tracker. It doesn't
+    // update the NotificationProgressDrawable, which is updated by {@code #setProgressModel}.
     @Override
     public synchronized void setMax(int max) {
         super.setMax(max);
@@ -323,10 +354,10 @@
     }
 
     private void onMaybeVisualProgressChanged() {
-        float scale = getScale();
-        if (mScale == scale) return;
+        float progressFraction = getProgressFraction();
+        if (mProgressFraction == progressFraction) return;
 
-        mScale = scale;
+        mProgressFraction = progressFraction;
         mTrackerPosIsDirty = true;
         invalidate();
     }
@@ -372,6 +403,59 @@
         updateTrackerAndBarPos(w, h);
     }
 
+    @Override
+    public void onDrawableBoundsChanged() {
+        final Rect progressDrawableBounds = mNotificationProgressDrawable.getBounds();
+
+        if (mProgressDrawableBounds.equals(progressDrawableBounds)) return;
+
+        if (mProgressDrawableBounds.width() != progressDrawableBounds.width()) {
+            updateDrawableParts();
+        }
+
+        mProgressDrawableBounds.set(progressDrawableBounds);
+    }
+
+    private void updateDrawableParts() {
+        Log.d(TAG, "updateDrawableParts() called. mNotificationProgressDrawable = "
+                + mNotificationProgressDrawable + ", mParts = " + mParts);
+
+        if (mNotificationProgressDrawable == null) return;
+        if (mParts == null) return;
+
+        final float width = mNotificationProgressDrawable.getBounds().width();
+        if (width == 0) {
+            if (mProgressDrawableParts != null) {
+                Log.d(TAG, "Clearing mProgressDrawableParts");
+                mProgressDrawableParts.clear();
+                mNotificationProgressDrawable.setParts(mProgressDrawableParts);
+            }
+            return;
+        }
+
+        mProgressDrawableParts = processAndConvertToDrawableParts(
+                mParts,
+                width,
+                mNotificationProgressDrawable.getSegSegGap(),
+                mNotificationProgressDrawable.getSegPointGap(),
+                mNotificationProgressDrawable.getPointRadius(),
+                mHasTrackerIcon
+        );
+        Pair<List<NotificationProgressDrawable.Part>, Float> p = maybeStretchAndRescaleSegments(
+                mParts,
+                mProgressDrawableParts,
+                mNotificationProgressDrawable.getSegmentMinWidth(),
+                mNotificationProgressDrawable.getPointRadius(),
+                getProgressFraction(),
+                width,
+                mProgressModel.isStyledByProgress(),
+                mHasTrackerIcon ? 0F : mNotificationProgressDrawable.getSegSegGap());
+
+        Log.d(TAG, "Updating NotificationProgressDrawable parts");
+        mNotificationProgressDrawable.setParts(p.first);
+        mAdjustedProgressFraction = p.second / width;
+    }
+
     private void updateTrackerAndBarPos(int w, int h) {
         final int paddedHeight = h - mPaddingTop - mPaddingBottom;
         final Drawable bar = getCurrentDrawable();
@@ -402,11 +486,11 @@
         }
 
         if (tracker != null) {
-            setTrackerPos(w, tracker, mScale, trackerOffsetY);
+            setTrackerPos(w, tracker, mAdjustedProgressFraction, trackerOffsetY);
         }
     }
 
-    private float getScale() {
+    private float getProgressFraction() {
         int min = getMin();
         int max = getMax();
         int range = max - min;
@@ -418,17 +502,17 @@
      *
      * @param w Width of the view, including padding
      * @param tracker Drawable used for the tracker
-     * @param scale Current progress between 0 and 1
+     * @param progressFraction Current progress between 0 and 1
      * @param offsetY Vertical offset for centering. If set to
      *            {@link Integer#MIN_VALUE}, the current offset will be used.
      */
-    private void setTrackerPos(int w, Drawable tracker, float scale, int offsetY) {
+    private void setTrackerPos(int w, Drawable tracker, float progressFraction, int offsetY) {
         int available = w - mPaddingLeft - mPaddingRight;
         final int trackerWidth = tracker.getIntrinsicWidth();
         final int trackerHeight = tracker.getIntrinsicHeight();
         available -= ((mTrackerHeight <= 0) ? trackerWidth : mTrackerWidth);
 
-        final int trackerPos = (int) (scale * available + 0.5f);
+        final int trackerPos = (int) (progressFraction * available + 0.5f);
 
         final int top, bottom;
         if (offsetY == Integer.MIN_VALUE) {
@@ -482,7 +566,7 @@
         if (mTracker == null) return;
 
         if (mTrackerPosIsDirty) {
-            setTrackerPos(getWidth(), mTracker, mScale, Integer.MIN_VALUE);
+            setTrackerPos(getWidth(), mTracker, mAdjustedProgressFraction, Integer.MIN_VALUE);
         }
 
         final int saveCount = canvas.save();
@@ -531,7 +615,7 @@
 
         final Drawable tracker = mTracker;
         if (tracker != null) {
-            setTrackerPos(getWidth(), tracker, mScale, Integer.MIN_VALUE);
+            setTrackerPos(getWidth(), tracker, mAdjustedProgressFraction, Integer.MIN_VALUE);
 
             // Since we draw translated, the drawable's bounds that it signals
             // for invalidation won't be the actual bounds we want invalidated,
@@ -541,16 +625,14 @@
     }
 
     /**
-     * Processes the ProgressStyle data and convert to list of {@code
-     * NotificationProgressDrawable.Part}.
+     * Processes the ProgressStyle data and convert to a list of {@code Part}.
      */
     @VisibleForTesting
-    public static List<Part> processAndConvertToDrawableParts(
+    public static List<Part> processAndConvertToViewParts(
             List<ProgressStyle.Segment> segments,
             List<ProgressStyle.Point> points,
             int progress,
-            int progressMax,
-            boolean isStyledByProgress
+            int progressMax
     ) {
         if (segments.isEmpty()) {
             throw new IllegalArgumentException("List of segments shouldn't be empty");
@@ -571,6 +653,7 @@
         if (progress < 0 || progress > progressMax) {
             throw new IllegalArgumentException("Invalid progress : " + progress);
         }
+
         for (ProgressStyle.Point point : points) {
             final int pos = point.getPosition();
             if (pos < 0 || pos > progressMax) {
@@ -583,23 +666,21 @@
         final Map<Integer, ProgressStyle.Point> positionToPointMap = generatePositionToPointMap(
                 points);
         final SortedSet<Integer> sortedPos = generateSortedPositionSet(startToSegmentMap,
-                positionToPointMap, progress, isStyledByProgress);
+                positionToPointMap);
 
         final Map<Integer, ProgressStyle.Segment> startToSplitSegmentMap =
-                splitSegmentsByPointsAndProgress(
-                        startToSegmentMap, sortedPos, progressMax);
+                splitSegmentsByPoints(startToSegmentMap, sortedPos, progressMax);
 
-        return convertToDrawableParts(startToSplitSegmentMap, positionToPointMap, sortedPos,
-                progress, progressMax,
-                isStyledByProgress);
+        return convertToViewParts(startToSplitSegmentMap, positionToPointMap, sortedPos,
+                progressMax);
     }
 
     // Any segment with a point on it gets split by the point.
-    // If isStyledByProgress is true, also split the segment with the progress value in its range.
-    private static Map<Integer, ProgressStyle.Segment> splitSegmentsByPointsAndProgress(
+    private static Map<Integer, ProgressStyle.Segment> splitSegmentsByPoints(
             Map<Integer, ProgressStyle.Segment> startToSegmentMap,
             SortedSet<Integer> sortedPos,
-            int progressMax) {
+            int progressMax
+    ) {
         int prevSegStart = 0;
         for (Integer pos : sortedPos) {
             if (pos == 0 || pos == progressMax) continue;
@@ -624,32 +705,22 @@
         return startToSegmentMap;
     }
 
-    private static List<Part> convertToDrawableParts(
+    private static List<Part> convertToViewParts(
             Map<Integer, ProgressStyle.Segment> startToSegmentMap,
             Map<Integer, ProgressStyle.Point> positionToPointMap,
             SortedSet<Integer> sortedPos,
-            int progress,
-            int progressMax,
-            boolean isStyledByProgress
+            int progressMax
     ) {
         List<Part> parts = new ArrayList<>();
-        boolean styleRemainingParts = false;
         for (Integer pos : sortedPos) {
             if (positionToPointMap.containsKey(pos)) {
                 final ProgressStyle.Point point = positionToPointMap.get(pos);
-                final int color = maybeGetFadedColor(point.getColor(), styleRemainingParts);
-                parts.add(new Point(null, color, styleRemainingParts));
-            }
-            // We want the Point at the current progress to be filled (not faded), but a Segment
-            // starting at this progress to be faded.
-            if (isStyledByProgress && !styleRemainingParts && pos == progress) {
-                styleRemainingParts = true;
+                parts.add(new Point(point.getColor()));
             }
             if (startToSegmentMap.containsKey(pos)) {
                 final ProgressStyle.Segment seg = startToSegmentMap.get(pos);
-                final int color = maybeGetFadedColor(seg.getColor(), styleRemainingParts);
                 parts.add(new Segment(
-                        (float) seg.getLength() / progressMax, color, styleRemainingParts));
+                        (float) seg.getLength() / progressMax, seg.getColor()));
             }
         }
 
@@ -660,11 +731,24 @@
     private static int maybeGetFadedColor(@ColorInt int color, boolean fade) {
         if (!fade) return color;
 
-        return NotificationProgressDrawable.getFadedColor(color);
+        return getFadedColor(color);
+    }
+
+    /**
+     * Get a color with an opacity that's 40% of the input color.
+     */
+    @ColorInt
+    static int getFadedColor(@ColorInt int color) {
+        return Color.argb(
+                (int) (Color.alpha(color) * 0.4f + 0.5f),
+                Color.red(color),
+                Color.green(color),
+                Color.blue(color));
     }
 
     private static Map<Integer, ProgressStyle.Segment> generateStartToSegmentMap(
-            List<ProgressStyle.Segment> segments) {
+            List<ProgressStyle.Segment> segments
+    ) {
         final Map<Integer, ProgressStyle.Segment> startToSegmentMap = new HashMap<>();
 
         int currentStart = 0;  // Initial start position is 0
@@ -681,7 +765,8 @@
     }
 
     private static Map<Integer, ProgressStyle.Point> generatePositionToPointMap(
-            List<ProgressStyle.Point> points) {
+            List<ProgressStyle.Point> points
+    ) {
         final Map<Integer, ProgressStyle.Point> positionToPointMap = new HashMap<>();
 
         for (ProgressStyle.Point point : points) {
@@ -693,14 +778,404 @@
 
     private static SortedSet<Integer> generateSortedPositionSet(
             Map<Integer, ProgressStyle.Segment> startToSegmentMap,
-            Map<Integer, ProgressStyle.Point> positionToPointMap, int progress,
-            boolean isStyledByProgress) {
+            Map<Integer, ProgressStyle.Point> positionToPointMap
+    ) {
         final SortedSet<Integer> sortedPos = new TreeSet<>(startToSegmentMap.keySet());
         sortedPos.addAll(positionToPointMap.keySet());
-        if (isStyledByProgress) {
-            sortedPos.add(progress);
-        }
 
         return sortedPos;
     }
+
+    /**
+     * Processes the list of {@code Part} and convert to a list of
+     * {@code NotificationProgressDrawable.Part}.
+     */
+    @VisibleForTesting
+    public static List<NotificationProgressDrawable.Part> processAndConvertToDrawableParts(
+            List<Part> parts,
+            float totalWidth,
+            float segSegGap,
+            float segPointGap,
+            float pointRadius,
+            boolean hasTrackerIcon
+    ) {
+        List<NotificationProgressDrawable.Part> drawableParts = new ArrayList<>();
+
+        // generally, we will start drawing at (x, y) and end at (x+w, y)
+        float x = (float) 0;
+
+        final int nParts = parts.size();
+        for (int iPart = 0; iPart < nParts; iPart++) {
+            final Part part = parts.get(iPart);
+            final Part prevPart = iPart == 0 ? null : parts.get(iPart - 1);
+            final Part nextPart = iPart + 1 == nParts ? null : parts.get(iPart + 1);
+            if (part instanceof Segment segment) {
+                final float segWidth = segment.mFraction * totalWidth;
+                // Advance the start position to account for a point immediately prior.
+                final float startOffset = getSegStartOffset(prevPart, pointRadius, segPointGap, x);
+                final float start = x + startOffset;
+                // Retract the end position to account for the padding and a point immediately
+                // after.
+                final float endOffset = getSegEndOffset(segment, nextPart, pointRadius, segPointGap,
+                        segSegGap, x + segWidth, totalWidth, hasTrackerIcon);
+                final float end = x + segWidth - endOffset;
+
+                drawableParts.add(
+                        new NotificationProgressDrawable.Segment(start, end, segment.mColor,
+                                segment.mFaded));
+
+                segment.mStart = x;
+                segment.mEnd = x + segWidth;
+
+                // Advance the current position to account for the segment's fraction of the total
+                // width (ignoring offset and padding)
+                x += segWidth;
+            } else if (part instanceof Point point) {
+                final float pointWidth = 2 * pointRadius;
+                float start = x - pointRadius;
+                if (start < 0) start = 0;
+                float end = start + pointWidth;
+                if (end > totalWidth) {
+                    end = totalWidth;
+                    if (totalWidth > pointWidth) start = totalWidth - pointWidth;
+                }
+
+                drawableParts.add(
+                        new NotificationProgressDrawable.Point(start, end, point.mColor));
+            }
+        }
+
+        return drawableParts;
+    }
+
+    private static float getSegStartOffset(Part prevPart, float pointRadius, float segPointGap,
+            float startX) {
+        if (!(prevPart instanceof Point)) return 0F;
+        final float pointOffset = (startX < pointRadius) ? (pointRadius - startX) : 0;
+        return pointOffset + pointRadius + segPointGap;
+    }
+
+    private static float getSegEndOffset(Segment seg, Part nextPart, float pointRadius,
+            float segPointGap,
+            float segSegGap, float endX, float totalWidth, boolean hasTrackerIcon) {
+        if (nextPart == null) return 0F;
+        if (nextPart instanceof Segment nextSeg) {
+            if (!seg.mFaded && nextSeg.mFaded) {
+                // @see Segment#mFaded
+                return hasTrackerIcon ? 0F : segSegGap;
+            }
+            return segSegGap;
+        }
+
+        final float pointWidth = 2 * pointRadius;
+        final float pointOffset = (endX + pointRadius > totalWidth && totalWidth > pointWidth)
+                ? (endX + pointRadius - totalWidth) : 0;
+        return segPointGap + pointRadius + pointOffset;
+    }
+
+    /**
+     * Processes the list of {@code NotificationProgressBar.Part} data and convert to a pair of:
+     *   - list of {@code NotificationProgressDrawable.Part}.
+     *   - location of progress on the stretched and rescaled progress bar.
+     */
+    @VisibleForTesting
+    public static Pair<List<NotificationProgressDrawable.Part>, Float>
+                maybeStretchAndRescaleSegments(
+            List<Part> parts,
+            List<NotificationProgressDrawable.Part> drawableParts,
+            float segmentMinWidth,
+            float pointRadius,
+            float progressFraction,
+            float totalWidth,
+            boolean isStyledByProgress,
+            float progressGap
+    ) {
+        final List<NotificationProgressDrawable.Segment> drawableSegments = drawableParts
+                .stream()
+                .filter(NotificationProgressDrawable.Segment.class::isInstance)
+                .map(NotificationProgressDrawable.Segment.class::cast)
+                .toList();
+        float totalExcessWidth = 0;
+        float totalPositiveExcessWidth = 0;
+        for (NotificationProgressDrawable.Segment drawableSegment : drawableSegments) {
+            final float excessWidth = drawableSegment.getWidth() - segmentMinWidth;
+            totalExcessWidth += excessWidth;
+            if (excessWidth > 0) totalPositiveExcessWidth += excessWidth;
+        }
+
+        // All drawable segments are above minimum width. No need to stretch and rescale.
+        if (totalExcessWidth == totalPositiveExcessWidth) {
+            return maybeSplitDrawableSegmentsByProgress(
+                    parts,
+                    drawableParts,
+                    progressFraction,
+                    totalWidth,
+                    isStyledByProgress,
+                    progressGap);
+        }
+
+        if (totalExcessWidth < 0) {
+            // TODO: b/372908709 - throw an error so that the caller can catch and go to fallback
+            //  option. (instead of return.)
+            Log.w(TAG, "Not enough width to satisfy the minimum width for segments.");
+            return maybeSplitDrawableSegmentsByProgress(
+                    parts,
+                    drawableParts,
+                    progressFraction,
+                    totalWidth,
+                    isStyledByProgress,
+                    progressGap);
+        }
+
+        final int nParts = drawableParts.size();
+        float startOffset = 0;
+        for (int iPart = 0; iPart < nParts; iPart++) {
+            final NotificationProgressDrawable.Part drawablePart = drawableParts.get(iPart);
+            if (drawablePart instanceof NotificationProgressDrawable.Segment drawableSegment) {
+                final float origDrawableSegmentWidth = drawableSegment.getWidth();
+
+                float drawableSegmentWidth = segmentMinWidth;
+                // Allocate the totalExcessWidth to the segments above minimum, proportionally to
+                // their initial excessWidth.
+                if (origDrawableSegmentWidth > segmentMinWidth) {
+                    drawableSegmentWidth +=
+                            totalExcessWidth * (origDrawableSegmentWidth - segmentMinWidth)
+                                    / totalPositiveExcessWidth;
+                }
+
+                final float widthDiff = drawableSegmentWidth - drawableSegment.getWidth();
+
+                // Adjust drawable segments to new widths
+                drawableSegment.setStart(drawableSegment.getStart() + startOffset);
+                drawableSegment.setEnd(
+                        drawableSegment.getStart() + origDrawableSegmentWidth + widthDiff);
+
+                // Also adjust view segments to new width. (For view segments, only start is
+                // needed?)
+                // Check that segments and drawableSegments are of the same size?
+                final Segment segment = (Segment) parts.get(iPart);
+                final float origSegmentWidth = segment.getWidth();
+                segment.mStart = segment.mStart + startOffset;
+                segment.mEnd = segment.mStart + origSegmentWidth + widthDiff;
+
+                // Increase startOffset for the subsequent segments.
+                startOffset += widthDiff;
+            } else if (drawablePart instanceof NotificationProgressDrawable.Point drawablePoint) {
+                drawablePoint.setStart(drawablePoint.getStart() + startOffset);
+                drawablePoint.setEnd(drawablePoint.getStart() + 2 * pointRadius);
+            }
+        }
+
+        return maybeSplitDrawableSegmentsByProgress(
+                parts,
+                drawableParts,
+                progressFraction,
+                totalWidth,
+                isStyledByProgress,
+                progressGap);
+    }
+
+    // Find the location of progress on the stretched and rescaled progress bar.
+    // If isStyledByProgress is true, also split the segment with the progress value in its range.
+    private static Pair<List<NotificationProgressDrawable.Part>, Float>
+                maybeSplitDrawableSegmentsByProgress(
+            // Needed to get the original segment start and end positions in pixels.
+            List<Part> parts,
+            List<NotificationProgressDrawable.Part> drawableParts,
+            float progressFraction,
+            float totalWidth,
+            boolean isStyledByProgress,
+            float progressGap
+    ) {
+        if (progressFraction == 1) return new Pair<>(drawableParts, totalWidth);
+
+        int iPartFirstSegmentToStyle = -1;
+        int iPartSegmentToSplit = -1;
+        float rescaledProgressX = 0;
+        float startFraction = 0;
+        final int nParts = parts.size();
+        for (int iPart = 0; iPart < nParts; iPart++) {
+            final Part part = parts.get(iPart);
+            if (!(part instanceof Segment)) continue;
+            final Segment segment = (Segment) part;
+            if (startFraction == progressFraction) {
+                iPartFirstSegmentToStyle = iPart;
+                rescaledProgressX = segment.mStart;
+                break;
+            } else if (startFraction < progressFraction
+                    && progressFraction < startFraction + segment.mFraction) {
+                iPartSegmentToSplit = iPart;
+                rescaledProgressX =
+                        segment.mStart + (progressFraction - startFraction) / segment.mFraction
+                                * segment.getWidth();
+                break;
+            }
+            startFraction += segment.mFraction;
+        }
+
+        if (!isStyledByProgress) return new Pair<>(drawableParts, rescaledProgressX);
+
+        List<NotificationProgressDrawable.Part> splitDrawableParts = new ArrayList<>();
+        boolean styleRemainingParts = false;
+        for (int iPart = 0; iPart < nParts; iPart++) {
+            final NotificationProgressDrawable.Part drawablePart = drawableParts.get(iPart);
+            if (drawablePart instanceof NotificationProgressDrawable.Point drawablePoint) {
+                final int color = maybeGetFadedColor(drawablePoint.getColor(), styleRemainingParts);
+                splitDrawableParts.add(
+                        new NotificationProgressDrawable.Point(drawablePoint.getStart(),
+                                drawablePoint.getEnd(), color));
+            }
+            if (iPart == iPartFirstSegmentToStyle) styleRemainingParts = true;
+            if (drawablePart instanceof NotificationProgressDrawable.Segment drawableSegment) {
+                if (iPart == iPartSegmentToSplit) {
+                    if (rescaledProgressX <= drawableSegment.getStart()) {
+                        styleRemainingParts = true;
+                        final int color = maybeGetFadedColor(drawableSegment.getColor(), true);
+                        splitDrawableParts.add(
+                                new NotificationProgressDrawable.Segment(drawableSegment.getStart(),
+                                        drawableSegment.getEnd(),
+                                        color, true));
+                    } else if (drawableSegment.getStart() < rescaledProgressX
+                            && rescaledProgressX < drawableSegment.getEnd()) {
+                        splitDrawableParts.add(
+                                new NotificationProgressDrawable.Segment(drawableSegment.getStart(),
+                                        rescaledProgressX - progressGap,
+                                        drawableSegment.getColor()));
+                        final int color = maybeGetFadedColor(drawableSegment.getColor(), true);
+                        splitDrawableParts.add(
+                                new NotificationProgressDrawable.Segment(rescaledProgressX,
+                                        drawableSegment.getEnd(), color, true));
+                        styleRemainingParts = true;
+                    } else {
+                        splitDrawableParts.add(
+                                new NotificationProgressDrawable.Segment(drawableSegment.getStart(),
+                                        drawableSegment.getEnd(),
+                                        drawableSegment.getColor()));
+                        styleRemainingParts = true;
+                    }
+                } else {
+                    final int color = maybeGetFadedColor(drawableSegment.getColor(),
+                            styleRemainingParts);
+                    splitDrawableParts.add(
+                            new NotificationProgressDrawable.Segment(drawableSegment.getStart(),
+                                    drawableSegment.getEnd(),
+                                    color, styleRemainingParts));
+                }
+            }
+        }
+
+        return new Pair<>(splitDrawableParts, rescaledProgressX);
+    }
+
+    /**
+     * A part of the progress bar, which is either a {@link Segment} with non-zero length, or a
+     * {@link Point} with zero length.
+     */
+    // TODO: b/372908709 - maybe this should be made private? Only test the final
+    //  NotificationDrawable.Parts.
+    // TODO: b/372908709 - rename to BarPart, BarSegment, BarPoint. This avoids naming ambiguity
+    //  with the types in NotificationProgressDrawable.
+    public interface Part {
+    }
+
+    /**
+     * A segment is a part of the progress bar with non-zero length. For example, it can
+     * represent a portion in a navigation journey with certain traffic condition.
+     *
+     */
+    public static final class Segment implements Part {
+        private final float mFraction;
+        @ColorInt private final int mColor;
+        /** Whether the segment is faded or not.
+         * <p>
+         *     <pre>
+         *     When mFaded is set to true, a combination of the following is done to the segment:
+         *       1. The drawing color is mColor with opacity updated to 40%.
+         *       2. The gap between faded and non-faded segments is:
+         *          - the segment-segment gap, when there is no tracker icon
+         *          - 0, when there is tracker icon
+         *     </pre>
+         * </p>
+         */
+        private final boolean mFaded;
+
+        /** Start position (in pixels) */
+        private float mStart;
+        /** End position (in pixels */
+        private float mEnd;
+
+        public Segment(float fraction, @ColorInt int color) {
+            this(fraction, color, false);
+        }
+
+        public Segment(float fraction, @ColorInt int color, boolean faded) {
+            mFraction = fraction;
+            mColor = color;
+            mFaded = faded;
+        }
+
+        /** Returns the calculated drawing width of the part */
+        public float getWidth() {
+            return mEnd - mStart;
+        }
+
+        @Override
+        public String toString() {
+            return "Segment(fraction=" + this.mFraction + ", color=" + this.mColor + ", faded="
+                    + this.mFaded + "), mStart = " + this.mStart + ", mEnd = " + this.mEnd;
+        }
+
+        // Needed for unit tests
+        @Override
+        public boolean equals(@androidx.annotation.Nullable Object other) {
+            if (this == other) return true;
+
+            if (other == null || getClass() != other.getClass()) return false;
+
+            Segment that = (Segment) other;
+            if (Float.compare(this.mFraction, that.mFraction) != 0) return false;
+            if (this.mColor != that.mColor) return false;
+            return this.mFaded == that.mFaded;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mFraction, mColor, mFaded);
+        }
+    }
+
+    /**
+     * A point is a part of the progress bar with zero length. Points are designated points within a
+     * progress bar to visualize distinct stages or milestones. For example, a stop in a multi-stop
+     * ride-share journey.
+     */
+    public static final class Point implements Part {
+        @ColorInt private final int mColor;
+
+        public Point(@ColorInt int color) {
+            mColor = color;
+        }
+
+        @Override
+        public String toString() {
+            return "Point(color=" + this.mColor + ")";
+        }
+
+        // Needed for unit tests.
+        @Override
+        public boolean equals(@androidx.annotation.Nullable Object other) {
+            if (this == other) return true;
+
+            if (other == null || getClass() != other.getClass()) return false;
+
+            Point that = (Point) other;
+
+            return this.mColor == that.mColor;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mColor);
+        }
+    }
 }
diff --git a/core/java/com/android/internal/widget/NotificationProgressDrawable.java b/core/java/com/android/internal/widget/NotificationProgressDrawable.java
index 8629a1c..ef0a5d5 100644
--- a/core/java/com/android/internal/widget/NotificationProgressDrawable.java
+++ b/core/java/com/android/internal/widget/NotificationProgressDrawable.java
@@ -21,7 +21,6 @@
 import android.content.res.Resources.Theme;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
-import android.graphics.Color;
 import android.graphics.ColorFilter;
 import android.graphics.Paint;
 import android.graphics.PixelFormat;
@@ -49,7 +48,8 @@
 
 /**
  * This is used by NotificationProgressBar for displaying a custom background. It composes of
- * segments, which have non-zero length, and points, which have zero length.
+ * segments, which have non-zero length varying drawing width, and points, which have zero length
+ * and fixed size for drawing.
  *
  * @see Segment
  * @see Point
@@ -57,14 +57,15 @@
 public final class NotificationProgressDrawable extends Drawable {
     private static final String TAG = "NotifProgressDrawable";
 
+    @Nullable
+    private BoundsChangeListener mBoundsChangeListener = null;
+
     private State mState;
     private boolean mMutated;
 
     private final ArrayList<Part> mParts = new ArrayList<>();
-    private boolean mHasTrackerIcon;
 
     private final RectF mSegRectF = new RectF();
-    private final Rect mPointRect = new Rect();
     private final RectF mPointRectF = new RectF();
 
     private final Paint mFillPaint = new Paint();
@@ -80,27 +81,31 @@
     }
 
     /**
-     * <p>Set the segment default color for the drawable.</p>
-     * <p>Note: changing this property will affect all instances of a drawable loaded from a
-     * resource. It is recommended to invoke {@link #mutate()} before changing this property.</p>
-     *
-     * @param color The color of the stroke
-     * @see #mutate()
+     * Returns the gap between two segments.
      */
-    public void setSegmentDefaultColor(@ColorInt int color) {
-        mState.setSegmentColor(color);
+    public float getSegSegGap() {
+        return mState.mSegSegGap;
     }
 
     /**
-     * <p>Set the point rect default color for the drawable.</p>
-     * <p>Note: changing this property will affect all instances of a drawable loaded from a
-     * resource. It is recommended to invoke {@link #mutate()} before changing this property.</p>
-     *
-     * @param color The color of the point rect
-     * @see #mutate()
+     * Returns the gap between a segment and a point.
      */
-    public void setPointRectDefaultColor(@ColorInt int color) {
-        mState.setPointRectColor(color);
+    public float getSegPointGap() {
+        return mState.mSegPointGap;
+    }
+
+    /**
+     * Returns the gap between a segment and a point.
+     */
+    public float getSegmentMinWidth() {
+        return mState.mSegmentMinWidth;
+    }
+
+    /**
+     * Returns the radius for the points.
+     */
+    public float getPointRadius() {
+        return mState.mPointRadius;
     }
 
     /**
@@ -120,47 +125,18 @@
         setParts(Arrays.asList(parts));
     }
 
-    /**
-     * Set whether a tracker is drawn on top of this NotificationProgressDrawable.
-     */
-    public void setHasTrackerIcon(boolean hasTrackerIcon) {
-        if (mHasTrackerIcon != hasTrackerIcon) {
-            mHasTrackerIcon = hasTrackerIcon;
-            invalidateSelf();
-        }
-    }
-
     @Override
     public void draw(@NonNull Canvas canvas) {
-        final float pointRadius =
-                mState.mPointRadius; // how big the point icon will be, halved
-
-        // generally, we will start drawing at (x, y) and end at (x+w, y)
-        float x = (float) getBounds().left;
+        final float pointRadius = mState.mPointRadius;
+        final float left = (float) getBounds().left;
         final float centerY = (float) getBounds().centerY();
-        final float totalWidth = (float) getBounds().width();
-        float segPointGap = mState.mSegPointGap;
 
         final int numParts = mParts.size();
         for (int iPart = 0; iPart < numParts; iPart++) {
             final Part part = mParts.get(iPart);
-            final Part prevPart = iPart == 0 ? null : mParts.get(iPart - 1);
-            final Part nextPart = iPart + 1 == numParts ? null : mParts.get(iPart + 1);
+            final float start = left + part.mStart;
+            final float end = left + part.mEnd;
             if (part instanceof Segment segment) {
-                final float segWidth = segment.mFraction * totalWidth;
-                // Advance the start position to account for a point immediately prior.
-                final float startOffset = getSegStartOffset(prevPart, pointRadius, segPointGap, x);
-                final float start = x + startOffset;
-                // Retract the end position to account for the padding and a point immediately
-                // after.
-                final float endOffset = getSegEndOffset(segment, nextPart, pointRadius, segPointGap,
-                        mState.mSegSegGap, x + segWidth, totalWidth, mHasTrackerIcon);
-                final float end = x + segWidth - endOffset;
-
-                // Advance the current position to account for the segment's fraction of the total
-                // width (ignoring offset and padding)
-                x += segWidth;
-
                 // No space left to draw the segment
                 if (start > end) continue;
 
@@ -168,69 +144,25 @@
                         : mState.mSegmentHeight / 2F;
                 final float cornerRadius = mState.mSegmentCornerRadius;
 
-                mFillPaint.setColor(segment.mColor != Color.TRANSPARENT ? segment.mColor
-                        : (segment.mFaded ? mState.mFadedSegmentColor : mState.mSegmentColor));
+                mFillPaint.setColor(segment.mColor);
 
                 mSegRectF.set(start, centerY - radiusY, end, centerY + radiusY);
                 canvas.drawRoundRect(mSegRectF, cornerRadius, cornerRadius, mFillPaint);
             } else if (part instanceof Point point) {
-                final float pointWidth = 2 * pointRadius;
-                float start = x - pointRadius;
-                if (start < 0) start = 0;
-                float end = start + pointWidth;
-                if (end > totalWidth) {
-                    end = totalWidth;
-                    if (totalWidth > pointWidth) start = totalWidth - pointWidth;
-                }
-                mPointRect.set((int) start, (int) (centerY - pointRadius), (int) end,
-                        (int) (centerY + pointRadius));
+                // TODO: b/367804171 - actually use a vector asset for the default point
+                //  rather than drawing it as a box?
+                mPointRectF.set(start, centerY - pointRadius, end, centerY + pointRadius);
+                final float inset = mState.mPointRectInset;
+                final float cornerRadius = mState.mPointRectCornerRadius;
+                mPointRectF.inset(inset, inset);
 
-                if (point.mIcon != null) {
-                    point.mIcon.setBounds(mPointRect);
-                    point.mIcon.draw(canvas);
-                } else {
-                    // TODO: b/367804171 - actually use a vector asset for the default point
-                    //  rather than drawing it as a box?
-                    mPointRectF.set(start, centerY - pointRadius, end, centerY + pointRadius);
-                    final float inset = mState.mPointRectInset;
-                    final float cornerRadius = mState.mPointRectCornerRadius;
-                    mPointRectF.inset(inset, inset);
+                mFillPaint.setColor(point.mColor);
 
-                    mFillPaint.setColor(point.mColor != Color.TRANSPARENT ? point.mColor
-                            : (point.mFaded ? mState.mFadedPointRectColor
-                                    : mState.mPointRectColor));
-
-                    canvas.drawRoundRect(mPointRectF, cornerRadius, cornerRadius, mFillPaint);
-                }
+                canvas.drawRoundRect(mPointRectF, cornerRadius, cornerRadius, mFillPaint);
             }
         }
     }
 
-    private static float getSegStartOffset(Part prevPart, float pointRadius, float segPointGap,
-            float startX) {
-        if (!(prevPart instanceof Point)) return 0F;
-        final float pointOffset = (startX < pointRadius) ? (pointRadius - startX) : 0;
-        return pointOffset + pointRadius + segPointGap;
-    }
-
-    private static float getSegEndOffset(Segment seg, Part nextPart, float pointRadius,
-            float segPointGap,
-            float segSegGap, float endX, float totalWidth, boolean hasTrackerIcon) {
-        if (nextPart == null) return 0F;
-        if (nextPart instanceof Segment nextSeg) {
-            if (!seg.mFaded && nextSeg.mFaded) {
-                // @see Segment#mFaded
-                return hasTrackerIcon ? 0F : segSegGap;
-            }
-            return segSegGap;
-        }
-
-        final float pointWidth = 2 * pointRadius;
-        final float pointOffset = (endX + pointRadius > totalWidth && totalWidth > pointWidth)
-                ? (endX + pointRadius - totalWidth) : 0;
-        return segPointGap + pointRadius + pointOffset;
-    }
-
     @Override
     public @Config int getChangingConfigurations() {
         return super.getChangingConfigurations() | mState.getChangingConfigurations();
@@ -260,6 +192,19 @@
         return PixelFormat.UNKNOWN;
     }
 
+    public void setBoundsChangeListener(BoundsChangeListener listener) {
+        mBoundsChangeListener = listener;
+    }
+
+    @Override
+    protected void onBoundsChange(Rect bounds) {
+        super.onBoundsChange(bounds);
+
+        if (mBoundsChangeListener != null) {
+            mBoundsChangeListener.onDrawableBoundsChanged();
+        }
+    }
+
     @Override
     public void inflate(@NonNull Resources r, @NonNull XmlPullParser parser,
             @NonNull AttributeSet attrs, @Nullable Resources.Theme theme)
@@ -384,6 +329,8 @@
         // Extract the theme attributes, if any.
         state.mThemeAttrsSegments = a.extractThemeAttrs();
 
+        state.mSegmentMinWidth = a.getDimension(
+                R.styleable.NotificationProgressDrawableSegments_minWidth, state.mSegmentMinWidth);
         state.mSegmentHeight = a.getDimension(
                 R.styleable.NotificationProgressDrawableSegments_height, state.mSegmentHeight);
         state.mFadedSegmentHeight = a.getDimension(
@@ -392,9 +339,6 @@
         state.mSegmentCornerRadius = a.getDimension(
                 R.styleable.NotificationProgressDrawableSegments_cornerRadius,
                 state.mSegmentCornerRadius);
-        final int color = a.getColor(R.styleable.NotificationProgressDrawableSegments_color,
-                state.mSegmentColor);
-        setSegmentDefaultColor(color);
     }
 
     private void updatePointsFromTypedArray(TypedArray a) {
@@ -413,9 +357,6 @@
         state.mPointRectCornerRadius = a.getDimension(
                 R.styleable.NotificationProgressDrawablePoints_cornerRadius,
                 state.mPointRectCornerRadius);
-        final int color = a.getColor(R.styleable.NotificationProgressDrawablePoints_color,
-                state.mPointRectColor);
-        setPointRectDefaultColor(color);
     }
 
     static int resolveDensity(@Nullable Resources r, int parentDensity) {
@@ -464,65 +405,58 @@
     }
 
     /**
-     * A part of the progress bar, which is either a S{@link Segment} with non-zero length, or a
-     * {@link Point} with zero length.
+     * Listener to receive updates about drawable bounds changing
      */
-    public interface Part {
+    public interface BoundsChangeListener {
+        /** Called when bounds have changed */
+        void onDrawableBoundsChanged();
     }
 
     /**
-     * A segment is a part of the progress bar with non-zero length. For example, it can
-     * represent a portion in a navigation journey with certain traffic condition.
-     *
+     * A part of the progress bar, which is either a {@link Segment} with non-zero length and
+     * varying drawing width, or a {@link Point} with zero length and fixed size for drawing.
      */
-    public static final class Segment implements Part {
-        private final float mFraction;
-        @ColorInt private final int mColor;
-        /** Whether the segment is faded or not.
-         * <p>
-         *     <pre>
-         *     When mFaded is set to true, a combination of the following is done to the segment:
-         *       1. The drawing color is mColor with opacity updated to 40%.
-         *       2. The gap between faded and non-faded segments is:
-         *          - the segment-segment gap, when there is no tracker icon
-         *          - 0, when there is tracker icon
-         *     </pre>
-         * </p>
-         */
-        private final boolean mFaded;
+    public abstract static class Part {
+        // TODO: b/372908709 - maybe rename start/end to left/right, to be consistent with the
+        //  bounds rect.
+        /** Start position for drawing (in pixels) */
+        protected float mStart;
+        /** End position for drawing (in pixels) */
+        protected float mEnd;
+        /** Drawing color. */
+        @ColorInt protected final int mColor;
 
-        public Segment(float fraction) {
-            this(fraction, Color.TRANSPARENT);
-        }
-
-        public Segment(float fraction, @ColorInt int color) {
-            this(fraction, color, false);
-        }
-
-        public Segment(float fraction, @ColorInt int color, boolean faded) {
-            mFraction = fraction;
+        protected Part(float start, float end, @ColorInt int color) {
+            mStart = start;
+            mEnd = end;
             mColor = color;
-            mFaded = faded;
         }
 
-        public float getFraction() {
-            return this.mFraction;
+        public float getStart() {
+            return this.mStart;
+        }
+
+        public void setStart(float start) {
+            mStart = start;
+        }
+
+        public float getEnd() {
+            return this.mEnd;
+        }
+
+        public void setEnd(float end) {
+            mEnd = end;
+        }
+
+        /** Returns the calculated drawing width of the part */
+        public float getWidth() {
+            return mEnd - mStart;
         }
 
         public int getColor() {
             return this.mColor;
         }
 
-        public boolean getFaded() {
-            return this.mFaded;
-        }
-
-        @Override
-        public String toString() {
-            return "Segment(fraction=" + this.mFraction + ", color=" + this.mColor + ", faded="
-                    + this.mFaded + ')';
-        }
-
         // Needed for unit tests
         @Override
         public boolean equals(@Nullable Object other) {
@@ -530,80 +464,79 @@
 
             if (other == null || getClass() != other.getClass()) return false;
 
-            Segment that = (Segment) other;
-            if (Float.compare(this.mFraction, that.mFraction) != 0) return false;
-            if (this.mColor != that.mColor) return false;
-            return this.mFaded == that.mFaded;
+            Part that = (Part) other;
+            if (Float.compare(this.mStart, that.mStart) != 0) return false;
+            if (Float.compare(this.mEnd, that.mEnd) != 0) return false;
+            return this.mColor == that.mColor;
         }
 
         @Override
         public int hashCode() {
-            return Objects.hash(mFraction, mColor, mFaded);
+            return Objects.hash(mStart, mEnd, mColor);
         }
     }
 
     /**
-     * A point is a part of the progress bar with zero length. Points are designated points within a
-     * progressbar to visualize distinct stages or milestones. For example, a stop in a multi-stop
-     * ride-share journey.
+     * A segment is a part of the progress bar with non-zero length. For example, it can
+     * represent a portion in a navigation journey with certain traffic condition.
+     * <p>
+     * The start and end positions for drawing a segment are assumed to have been adjusted for
+     * the Points and gaps neighboring the segment.
+     * </p>
      */
-    public static final class Point implements Part {
-        @Nullable
-        private final Drawable mIcon;
-        @ColorInt private final int mColor;
+    public static final class Segment extends Part {
+        /**
+         * Whether the segment is faded or not.
+         * <p>
+         * Faded segments and non-faded segments are drawn with different heights.
+         * </p>
+         */
         private final boolean mFaded;
 
-        public Point(@Nullable Drawable icon) {
-            this(icon, Color.TRANSPARENT, false);
+        public Segment(float start, float end, int color) {
+            this(start, end, color, false);
         }
 
-        public Point(@Nullable Drawable icon, @ColorInt int color) {
-            this(icon, color, false);
-
-        }
-
-        public Point(@Nullable Drawable icon, @ColorInt int color, boolean faded) {
-            mIcon = icon;
-            mColor = color;
+        public Segment(float start, float end, int color, boolean faded) {
+            super(start, end, color);
             mFaded = faded;
         }
 
-        @Nullable
-        public Drawable getIcon() {
-            return this.mIcon;
-        }
-
-        public int getColor() {
-            return this.mColor;
-        }
-
-        public boolean getFaded() {
-            return this.mFaded;
-        }
-
         @Override
         public String toString() {
-            return "Point(icon=" + this.mIcon + ", color=" + this.mColor + ", faded=" + this.mFaded
-                    + ")";
+            return "Segment(start=" + this.mStart + ", end=" + this.mEnd + ", color=" + this.mColor
+                    + ", faded=" + this.mFaded + ')';
         }
 
         // Needed for unit tests.
         @Override
         public boolean equals(@Nullable Object other) {
-            if (this == other) return true;
+            if (!super.equals(other)) return false;
 
-            if (other == null || getClass() != other.getClass()) return false;
-
-            Point that = (Point) other;
-
-            if (!Objects.equals(this.mIcon, that.mIcon)) return false;
-            if (this.mColor != that.mColor) return false;
+            Segment that = (Segment) other;
             return this.mFaded == that.mFaded;
         }
 
         @Override
         public int hashCode() {
-            return Objects.hash(mIcon, mColor, mFaded);
+            return Objects.hash(super.hashCode(), mFaded);
+        }
+    }
+
+    /**
+     * A point is a part of the progress bar with zero length. Points are designated points within a
+     * progress bar to visualize distinct stages or milestones. For example, a stop in a multi-stop
+     * ride-share journey.
+     */
+    public static final class Point extends Part {
+        public Point(float start, float end, int color) {
+            super(start, end, color);
+        }
+
+        @Override
+        public String toString() {
+            return "Point(start=" + this.mStart + ", end=" + this.mEnd + ", color=" + this.mColor
+                    + ")";
         }
     }
 
@@ -628,16 +561,14 @@
         int mChangingConfigurations;
         float mSegSegGap = 0.0f;
         float mSegPointGap = 0.0f;
+        float mSegmentMinWidth = 0.0f;
         float mSegmentHeight;
         float mFadedSegmentHeight;
         float mSegmentCornerRadius;
-        int mSegmentColor;
-        int mFadedSegmentColor;
+        // how big the point icon will be, halved
         float mPointRadius;
         float mPointRectInset;
         float mPointRectCornerRadius;
-        int mPointRectColor;
-        int mFadedPointRectColor;
 
         int[] mThemeAttrs;
         int[] mThemeAttrsSegments;
@@ -652,16 +583,13 @@
             mChangingConfigurations = orig.mChangingConfigurations;
             mSegSegGap = orig.mSegSegGap;
             mSegPointGap = orig.mSegPointGap;
+            mSegmentMinWidth = orig.mSegmentMinWidth;
             mSegmentHeight = orig.mSegmentHeight;
             mFadedSegmentHeight = orig.mFadedSegmentHeight;
             mSegmentCornerRadius = orig.mSegmentCornerRadius;
-            mSegmentColor = orig.mSegmentColor;
-            mFadedSegmentColor = orig.mFadedSegmentColor;
             mPointRadius = orig.mPointRadius;
             mPointRectInset = orig.mPointRectInset;
             mPointRectCornerRadius = orig.mPointRectCornerRadius;
-            mPointRectColor = orig.mPointRectColor;
-            mFadedPointRectColor = orig.mFadedPointRectColor;
 
             mThemeAttrs = orig.mThemeAttrs;
             mThemeAttrsSegments = orig.mThemeAttrsSegments;
@@ -674,6 +602,18 @@
         }
 
         private void applyDensityScaling(int sourceDensity, int targetDensity) {
+            if (mSegSegGap > 0) {
+                mSegSegGap = scaleFromDensity(
+                        mSegSegGap, sourceDensity, targetDensity);
+            }
+            if (mSegPointGap > 0) {
+                mSegPointGap = scaleFromDensity(
+                        mSegPointGap, sourceDensity, targetDensity);
+            }
+            if (mSegmentMinWidth > 0) {
+                mSegmentMinWidth = scaleFromDensity(
+                        mSegmentMinWidth, sourceDensity, targetDensity);
+            }
             if (mSegmentHeight > 0) {
                 mSegmentHeight = scaleFromDensity(
                         mSegmentHeight, sourceDensity, targetDensity);
@@ -740,28 +680,6 @@
                 applyDensityScaling(sourceDensity, targetDensity);
             }
         }
-
-        public void setSegmentColor(int color) {
-            mSegmentColor = color;
-            mFadedSegmentColor = getFadedColor(color);
-        }
-
-        public void setPointRectColor(int color) {
-            mPointRectColor = color;
-            mFadedPointRectColor = getFadedColor(color);
-        }
-    }
-
-    /**
-     * Get a color with an opacity that's 25% of the input color.
-     */
-    @ColorInt
-    static int getFadedColor(@ColorInt int color) {
-        return Color.argb(
-                (int) (Color.alpha(color) * 0.4f + 0.5f),
-                Color.red(color),
-                Color.green(color),
-                Color.blue(color));
     }
 
     @Override
diff --git a/core/jni/android_content_res_ApkAssets.cpp b/core/jni/android_content_res_ApkAssets.cpp
index 1e7bfe3..e6364a9 100644
--- a/core/jni/android_content_res_ApkAssets.cpp
+++ b/core/jni/android_content_res_ApkAssets.cpp
@@ -111,9 +111,8 @@
 class LoaderAssetsProvider : public AssetsProvider {
  public:
   static std::unique_ptr<AssetsProvider> Create(JNIEnv* env, jobject assets_provider) {
-    return (!assets_provider) ? EmptyAssetsProvider::Create()
-                              : std::unique_ptr<AssetsProvider>(new LoaderAssetsProvider(
-                                    env, assets_provider));
+      return std::unique_ptr<AssetsProvider>{
+              assets_provider ? new LoaderAssetsProvider(env, assets_provider) : nullptr};
   }
 
   bool ForEachFile(const std::string& /* root_path */,
@@ -129,8 +128,8 @@
     return debug_name_;
   }
 
-  bool IsUpToDate() const override {
-    return true;
+  UpToDate IsUpToDate() const override {
+      return UpToDate::Always;
   }
 
   ~LoaderAssetsProvider() override {
@@ -212,7 +211,7 @@
     auto string_result = static_cast<jstring>(env->CallObjectMethod(
         assets_provider_, gAssetsProviderOffsets.toString));
     ScopedUtfChars str(env, string_result);
-    debug_name_ = std::string(str.c_str(), str.size());
+    debug_name_ = std::string(str.c_str());
   }
 
   // The global reference to the AssetsProvider
@@ -243,10 +242,10 @@
       apk_assets = ApkAssets::LoadOverlay(path.c_str(), property_flags);
       break;
     case FORMAT_ARSC:
-      apk_assets = ApkAssets::LoadTable(AssetsProvider::CreateAssetFromFile(path.c_str()),
-                                        std::move(loader_assets),
-                                        property_flags);
-      break;
+        apk_assets = ApkAssets::LoadTable(AssetsProvider::CreateAssetFromFile(path.c_str()),
+                                          MultiAssetsProvider::Create(std::move(loader_assets)),
+                                          property_flags);
+        break;
     case FORMAT_DIRECTORY: {
       auto assets = MultiAssetsProvider::Create(std::move(loader_assets),
                                                 DirectoryAssetsProvider::Create(path.c_str()));
@@ -316,10 +315,11 @@
         break;
     }
     case FORMAT_ARSC:
-      apk_assets = ApkAssets::LoadTable(
-          AssetsProvider::CreateAssetFromFd(std::move(dup_fd), nullptr /* path */),
-          std::move(loader_assets), property_flags);
-      break;
+        apk_assets = ApkAssets::LoadTable(AssetsProvider::CreateAssetFromFd(std::move(dup_fd),
+                                                                            nullptr /* path */),
+                                          MultiAssetsProvider::Create(std::move(loader_assets)),
+                                          property_flags);
+        break;
     default:
       const std::string error_msg = base::StringPrintf("Unsupported format type %d", format);
       jniThrowException(env, "java/lang/IllegalArgumentException", error_msg.c_str());
@@ -386,12 +386,15 @@
         break;
     }
     case FORMAT_ARSC:
-      apk_assets = ApkAssets::LoadTable(
-          AssetsProvider::CreateAssetFromFd(std::move(dup_fd), nullptr /* path */,
-                                            static_cast<off64_t>(offset),
-                                            static_cast<off64_t>(length)),
-          std::move(loader_assets), property_flags);
-      break;
+        apk_assets =
+                ApkAssets::LoadTable(AssetsProvider::CreateAssetFromFd(std::move(dup_fd),
+                                                                       nullptr /* path */,
+                                                                       static_cast<off64_t>(offset),
+                                                                       static_cast<off64_t>(
+                                                                               length)),
+                                     MultiAssetsProvider::Create(std::move(loader_assets)),
+                                     property_flags);
+        break;
     default:
       const std::string error_msg = base::StringPrintf("Unsupported format type %d", format);
       jniThrowException(env, "java/lang/IllegalArgumentException", error_msg.c_str());
@@ -408,13 +411,16 @@
 }
 
 static jlong NativeLoadEmpty(JNIEnv* env, jclass /*clazz*/, jint flags, jobject assets_provider) {
-  auto apk_assets = ApkAssets::Load(LoaderAssetsProvider::Create(env, assets_provider), flags);
-  if (apk_assets == nullptr) {
-    const std::string error_msg =
-        base::StringPrintf("Failed to load empty assets with provider %p", (void*)assets_provider);
-    jniThrowException(env, "java/io/IOException", error_msg.c_str());
-    return 0;
-  }
+    auto apk_assets = ApkAssets::Load(MultiAssetsProvider::Create(
+                                              LoaderAssetsProvider::Create(env, assets_provider)),
+                                      flags);
+    if (apk_assets == nullptr) {
+        const std::string error_msg =
+                base::StringPrintf("Failed to load empty assets with provider %p",
+                                   (void*)assets_provider);
+        jniThrowException(env, "java/io/IOException", error_msg.c_str());
+        return 0;
+    }
   return CreateGuardedApkAssets(std::move(apk_assets));
 }
 
@@ -443,10 +449,10 @@
     return reinterpret_cast<jlong>(apk_assets->GetLoadedArsc()->GetStringPool());
 }
 
-static jboolean NativeIsUpToDate(CRITICAL_JNI_PARAMS_COMMA jlong ptr) {
+static jint 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;
+    return (jint)apk_assets->IsUpToDate();
 }
 
 static jlong NativeOpenXml(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring file_name) {
@@ -558,7 +564,7 @@
         {"nativeGetDebugName", "(J)Ljava/lang/String;", (void*)NativeGetDebugName},
         {"nativeGetStringBlock", "(J)J", (void*)NativeGetStringBlock},
         // @CriticalNative
-        {"nativeIsUpToDate", "(J)Z", (void*)NativeIsUpToDate},
+        {"nativeIsUpToDate", "(J)I", (void*)NativeIsUpToDate},
         {"nativeOpenXml", "(JLjava/lang/String;)J", (void*)NativeOpenXml},
         {"nativeGetOverlayableInfo", "(JLjava/lang/String;)Landroid/content/om/OverlayableInfo;",
          (void*)NativeGetOverlayableInfo},
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index 57bfc70..b2649a4 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -198,7 +198,7 @@
   auto assetmanager = LockAndStartAssetManager(ptr);
   const ScopedUtfChars package_name_utf8(env, package_name);
   CHECK(package_name_utf8.c_str() != nullptr);
-  const std::string std_package_name(package_name_utf8.c_str());
+  const std::string_view std_package_name(package_name_utf8.c_str());
   const std::unordered_map<std::string, std::string>* map = nullptr;
 
   assetmanager->ForEachPackage([&](const std::string& this_package_name, uint8_t package_id) {
diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp
index 240be3f..c105a60 100644
--- a/core/jni/android_view_MotionEvent.cpp
+++ b/core/jni/android_view_MotionEvent.cpp
@@ -704,7 +704,8 @@
                                                     jint flags) {
     MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
     // Prevent private flags from being used from Java.
-    event->setFlags(flags & ~AMOTION_EVENT_PRIVATE_FLAG_MASK);
+    const int32_t privateFlags = event->getFlags() & AMOTION_EVENT_PRIVATE_FLAG_MASK;
+    event->setFlags((flags & ~AMOTION_EVENT_PRIVATE_FLAG_MASK) | privateFlags);
 }
 
 static jint android_view_MotionEvent_nativeGetEdgeFlags(CRITICAL_JNI_PARAMS_COMMA jlong nativePtr) {
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 0c243d1..6f69e40 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -1113,6 +1113,22 @@
     transaction->setCornerRadius(ctrl, cornerRadius);
 }
 
+static void nativeSetClientDrawnCornerRadius(JNIEnv* env, jclass clazz, jlong transactionObj,
+                                             jlong nativeObject, jfloat clientDrawnCornerRadius) {
+    auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+
+    SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl*>(nativeObject);
+    transaction->setClientDrawnCornerRadius(ctrl, clientDrawnCornerRadius);
+}
+
+static void nativeSetClientDrawnShadows(JNIEnv* env, jclass clazz, jlong transactionObj,
+                                        jlong nativeObject, jfloat clientDrawnShadowRadius) {
+    auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+
+    SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl*>(nativeObject);
+    transaction->setClientDrawnShadowRadius(ctrl, clientDrawnShadowRadius);
+}
+
 static void nativeSetBackgroundBlurRadius(JNIEnv* env, jclass clazz, jlong transactionObj,
          jlong nativeObject, jint blurRadius) {
     auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
@@ -2547,6 +2563,10 @@
             (void*)nativeSetCrop },
     {"nativeSetCornerRadius", "(JJF)V",
             (void*)nativeSetCornerRadius },
+    {"nativeSetClientDrawnCornerRadius", "(JJF)V",
+            (void*) nativeSetClientDrawnCornerRadius },
+    {"nativeSetClientDrawnShadows", "(JJF)V",
+            (void*) nativeSetClientDrawnShadows },
     {"nativeSetBackgroundBlurRadius", "(JJI)V",
             (void*)nativeSetBackgroundBlurRadius },
     {"nativeSetLayerStack", "(JJI)V",
diff --git a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
index f40cfd9f..3108f1f 100644
--- a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
+++ b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp
@@ -314,8 +314,9 @@
                                                when, uncompLen, crc);
             }
 
-            ALOGE("Library '%s' is not PAGE(%zu)-aligned - will not be able to open it directly "
-                  "from apk.\n",
+            ALOGE("extractNativeLibs=false library '%s' is not PAGE(%zu)-"
+                  "aligned within apk (APK alignment, not ELF alignment) -"
+                  "will not be able to open it directly from apk.\n",
                   fileName, kPageSize);
             return INSTALL_FAILED_INVALID_APK;
         }
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index cf81ba1..4f7ba93 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -107,6 +107,8 @@
         optional SettingProto accessibility_key_gesture_targets = 59 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto hct_rect_prompt_status = 60 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto em_value = 61 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        // Settings for accessibility autoclick
+        optional SettingProto autoclick_cursor_area_size = 62 [ (android.privacy).dest = DEST_AUTOMATIC ];
 
     }
     optional Accessibility accessibility = 2;
@@ -645,14 +647,8 @@
     optional SettingProto theme_customization_overlay_packages = 75 [ (android.privacy).dest = DEST_AUTOMATIC ];
     optional SettingProto trust_agents_initialized = 57 [ (android.privacy).dest = DEST_AUTOMATIC ];
 
-    message TrackpadGesture {
-        optional SettingProto trackpad_gesture_back_enabled = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
-        optional SettingProto trackpad_gesture_home_enabled = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
-        optional SettingProto trackpad_gesture_overview_enabled = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
-        optional SettingProto trackpad_gesture_notification_enabled = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
-        optional SettingProto trackpad_gesture_quick_switch_enabled = 5 [ (android.privacy).dest = DEST_AUTOMATIC ];
-    }
-    optional TrackpadGesture trackpad_gesture = 94;
+    reserved 94;  // formerly trackpad_gesture
+    reserved "trackpad_gesture";
 
     message Tts {
         option (android.msg_privacy).dest = DEST_EXPLICIT;
diff --git a/core/proto/android/providers/settings/system.proto b/core/proto/android/providers/settings/system.proto
index dd9bfa5..64c9f54 100644
--- a/core/proto/android/providers/settings/system.proto
+++ b/core/proto/android/providers/settings/system.proto
@@ -228,6 +228,8 @@
         optional SettingProto reverse_vertical_scrolling = 1 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto swap_primary_button = 2 [ (android.privacy).dest = DEST_AUTOMATIC ];
         optional SettingProto scrolling_acceleration = 3 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto pointer_acceleration_enabled = 4 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto scrolling_speed = 5 [ (android.privacy).dest = DEST_AUTOMATIC ];
     }
 
     optional Mouse mouse = 38;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index dc95471..ed021b6 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -5405,10 +5405,9 @@
     <permission android:name="android.permission.CHANGE_ACCESSIBILITY_VOLUME"
                 android:protectionLevel="signature" />
 
-    <!-- @FlaggedApi("com.android.server.accessibility.motion_event_observing")
-    @hide
-    @TestApi
-    Allows an accessibility service to observe motion events without consuming them. -->
+    <!-- @TestApi Allows an accessibility service to observe motion events
+         without consuming them.
+         @hide -->
     <permission android:name="android.permission.ACCESSIBILITY_MOTION_EVENT_OBSERVING"
                 android:protectionLevel="signature" />
 
diff --git a/core/res/res/drawable/ic_accessibility_hearing_aid.xml b/core/res/res/drawable/ic_accessibility_hearing_aid.xml
index e5ffeb0..79c61a6 100644
--- a/core/res/res/drawable/ic_accessibility_hearing_aid.xml
+++ b/core/res/res/drawable/ic_accessibility_hearing_aid.xml
@@ -17,8 +17,10 @@
 <adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
     <background android:drawable="@color/accessibility_feature_background" />
     <foreground>
+        <!-- TODO: b/382377298 - To update correct drawable with same inset
+        @dimen/accessibility_icon_foreground_padding_ratio or remove the inset together. -->
         <inset
             android:drawable="@drawable/ic_accessibility_hearing_aid_foreground"
-            android:inset="@dimen/accessibility_icon_foreground_padding_ratio" />
+            android:inset="30%" />
     </foreground>
 </adaptive-icon>
diff --git a/core/res/res/drawable/ic_accessibility_hearing_aid_blue_dot.xml b/core/res/res/drawable/ic_accessibility_hearing_aid_blue_dot.xml
new file mode 100644
index 0000000..4f0036c
--- /dev/null
+++ b/core/res/res/drawable/ic_accessibility_hearing_aid_blue_dot.xml
@@ -0,0 +1,27 @@
+<!--
+    Copyright (C) 2024 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="14dp"
+    android:height="14dp"
+    android:viewportWidth="14"
+    android:viewportHeight="14">
+    <path
+        android:pathData="M7,7m-6,0a6,6 0,1 1,12 0a6,6 0,1 1,-12 0"
+        android:strokeWidth="2"
+        android:fillColor="#3998D3"
+        android:strokeColor="#3E373C"/>
+</vector>
diff --git a/core/res/res/drawable/ic_accessibility_hearing_aid_disconnected.xml b/core/res/res/drawable/ic_accessibility_hearing_aid_disconnected.xml
new file mode 100644
index 0000000..2b302e1
--- /dev/null
+++ b/core/res/res/drawable/ic_accessibility_hearing_aid_disconnected.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.
+-->
+
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+    <background android:drawable="@color/accessibility_feature_background" />
+    <foreground>
+        <!-- TODO: b/382377298 - To update correct drawable with same inset
+        @dimen/accessibility_icon_foreground_padding_ratio or remove the inset together. -->
+        <inset
+            android:drawable="@drawable/ic_accessibility_hearing_aid_disconnected_foreground"
+            android:inset="30%" />
+    </foreground>
+</adaptive-icon>
diff --git a/core/res/res/drawable/ic_accessibility_hearing_aid_disconnected_foreground.xml b/core/res/res/drawable/ic_accessibility_hearing_aid_disconnected_foreground.xml
new file mode 100644
index 0000000..1097ed9
--- /dev/null
+++ b/core/res/res/drawable/ic_accessibility_hearing_aid_disconnected_foreground.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="22dp"
+    android:height="23dp"
+    android:viewportWidth="22"
+    android:viewportHeight="23">
+    <path
+        android:pathData="M13,1C14.275,1 15.42,1.284 16.435,1.851L14.934,3.352C14.346,3.117 13.701,3 13,3C11.583,3 10.396,3.479 9.438,4.438C8.479,5.396 8,6.583 8,8C8,8.671 8.109,9.324 8.326,9.96L6.794,11.492C6.265,10.355 6,9.191 6,8C6,6.033 6.675,4.375 8.025,3.025C9.375,1.675 11.033,1 13,1ZM13,4.5C13.251,4.5 13.491,4.522 13.72,4.566L9.566,8.72C9.522,8.49 9.5,8.25 9.5,8C9.5,7.033 9.842,6.208 10.525,5.525C11.208,4.842 12.033,4.5 13,4.5ZM9.332,14.582L1.707,22.207L0.293,20.793L20.293,0.793L21.707,2.207L19.22,4.694C19.74,5.677 20,6.779 20,8H18C18,7.356 17.901,6.76 17.703,6.211L16.465,7.449C16.514,7.793 16.509,8.152 16.45,8.525L20.575,12.1L19.075,13.3C19.408,13.833 19.646,14.421 19.788,15.063C19.929,15.704 20,16.35 20,17C20,18.1 19.608,19.042 18.825,19.825C18.042,20.608 17.1,21 16,21C15.05,21 14.217,20.717 13.5,20.15C12.783,19.583 12.267,18.833 11.95,17.9C11.667,17.067 11.363,16.45 11.038,16.05C10.712,15.65 10.175,15.183 9.425,14.65C9.394,14.628 9.363,14.605 9.332,14.582ZM10.756,13.158C11.518,13.704 12.129,14.23 12.587,14.738C13.063,15.262 13.442,15.975 13.725,16.875C13.908,17.458 14.188,17.958 14.563,18.375C14.938,18.792 15.417,19 16,19C16.533,19 17,18.804 17.4,18.413C17.8,18.021 18,17.55 18,17C18,16.567 17.958,16.138 17.875,15.712C17.792,15.288 17.658,14.9 17.475,14.55L15.775,15.9L13.275,11.5C12.985,11.524 12.706,11.516 12.438,11.477L10.756,13.158ZM3.989,14.297L5.434,12.852C5.145,12.405 4.896,11.934 4.688,11.438C4.229,10.346 4,9.2 4,8C4,6.783 4.229,5.625 4.688,4.525C5.146,3.425 5.8,2.45 6.65,1.6L5.2,0.2C4.167,1.25 3.375,2.446 2.825,3.788C2.275,5.129 2,6.533 2,8C2,9.433 2.275,10.821 2.825,12.163C3.136,12.921 3.524,13.632 3.989,14.297ZM16.375,12.9L17.425,12.05L15.55,10.4L15.363,10.587C15.304,10.646 15.242,10.7 15.175,10.75L16.375,12.9Z"
+        android:fillColor="#ffffff"
+        android:fillType="evenOdd"/>
+</vector>
diff --git a/core/res/res/drawable/ic_accessibility_hearing_aid_green_dot.xml b/core/res/res/drawable/ic_accessibility_hearing_aid_green_dot.xml
new file mode 100644
index 0000000..e8f0063
--- /dev/null
+++ b/core/res/res/drawable/ic_accessibility_hearing_aid_green_dot.xml
@@ -0,0 +1,27 @@
+<!--
+    Copyright (C) 2024 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="14dp"
+    android:height="14dp"
+    android:viewportWidth="14"
+    android:viewportHeight="14">
+    <path
+        android:pathData="M7,7m-6,0a6,6 0,1 1,12 0a6,6 0,1 1,-12 0"
+        android:strokeWidth="2"
+        android:fillColor="#6DD58C"
+        android:strokeColor="#3E373C"/>
+</vector>
diff --git a/core/res/res/drawable/notification_progress.xml b/core/res/res/drawable/notification_progress.xml
index 5d272fb..ff5450e 100644
--- a/core/res/res/drawable/notification_progress.xml
+++ b/core/res/res/drawable/notification_progress.xml
@@ -24,6 +24,7 @@
             android:segPointGap="@dimen/notification_progress_segPoint_gap">
             <segments
                 android:color="?attr/colorProgressBackgroundNormal"
+                android:minWidth="@dimen/notification_progress_segments_min_width"
                 android:height="@dimen/notification_progress_segments_height"
                 android:fadedHeight="@dimen/notification_progress_segments_faded_height"
                 android:cornerRadius="@dimen/notification_progress_segments_corner_radius"/>
diff --git a/core/res/res/layout/notification_2025_conversation_header.xml b/core/res/res/layout/notification_2025_conversation_header.xml
index db79e79..1bde173 100644
--- a/core/res/res/layout/notification_2025_conversation_header.xml
+++ b/core/res/res/layout/notification_2025_conversation_header.xml
@@ -136,10 +136,10 @@
 
     <ImageView
         android:id="@+id/phishing_alert"
-        android:layout_width="@dimen/notification_phishing_alert_size"
-        android:layout_height="@dimen/notification_phishing_alert_size"
-        android:layout_marginStart="@dimen/notification_conversation_header_separating_margin"
-        android:baseline="10dp"
+        android:layout_width="@dimen/notification_2025_badge_size"
+        android:layout_height="@dimen/notification_2025_badge_size"
+        android:layout_marginStart="@dimen/notification_2025_badge_margin"
+        android:baseline="@dimen/notification_2025_badge_baseline"
         android:scaleType="fitCenter"
         android:src="@drawable/ic_dialog_alert_material"
         android:visibility="gone"
@@ -148,10 +148,10 @@
 
     <ImageView
         android:id="@+id/profile_badge"
-        android:layout_width="@dimen/notification_badge_size"
-        android:layout_height="@dimen/notification_badge_size"
-        android:layout_marginStart="@dimen/notification_conversation_header_separating_margin"
-        android:baseline="10dp"
+        android:layout_width="@dimen/notification_2025_badge_size"
+        android:layout_height="@dimen/notification_2025_badge_size"
+        android:layout_marginStart="@dimen/notification_2025_badge_margin"
+        android:baseline="@dimen/notification_2025_badge_baseline"
         android:scaleType="fitCenter"
         android:visibility="gone"
         android:contentDescription="@string/notification_work_profile_content_description"
@@ -159,10 +159,10 @@
 
     <ImageView
         android:id="@+id/alerted_icon"
-        android:layout_width="@dimen/notification_alerted_size"
-        android:layout_height="@dimen/notification_alerted_size"
-        android:layout_marginStart="@dimen/notification_conversation_header_separating_margin"
-        android:baseline="10dp"
+        android:layout_width="@dimen/notification_2025_badge_size"
+        android:layout_height="@dimen/notification_2025_badge_size"
+        android:layout_marginStart="@dimen/notification_2025_badge_margin"
+        android:baseline="@dimen/notification_2025_badge_baseline"
         android:contentDescription="@string/notification_alerted_content_description"
         android:scaleType="fitCenter"
         android:src="@drawable/ic_notifications_alerted"
diff --git a/core/res/res/layout/notification_2025_expand_button.xml b/core/res/res/layout/notification_2025_expand_button.xml
index c8263c2..1c36754 100644
--- a/core/res/res/layout/notification_2025_expand_button.xml
+++ b/core/res/res/layout/notification_2025_expand_button.xml
@@ -22,6 +22,7 @@
     android:layout_height="wrap_content"
     android:layout_gravity="top|end"
     android:contentDescription="@string/expand_button_content_description_collapsed"
+    android:padding="@dimen/notification_2025_margin"
     >
 
     <LinearLayout
diff --git a/core/res/res/layout/notification_2025_template_collapsed_base.xml b/core/res/res/layout/notification_2025_template_collapsed_base.xml
index c827dcb..d29b7af 100644
--- a/core/res/res/layout/notification_2025_template_collapsed_base.xml
+++ b/core/res/res/layout/notification_2025_template_collapsed_base.xml
@@ -87,7 +87,7 @@
                 >
 
                 <!--
-                NOTE: The notification_top_line_views layout contains the app_name_text.
+                NOTE: The notification_2025_top_line_views layout contains the app_name_text.
                 In order to include the title view at the beginning, the Notification.Builder
                 has logic to hide that view whenever this title view is to be visible.
                 -->
@@ -104,7 +104,7 @@
                     android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title"
                     />
 
-                <include layout="@layout/notification_top_line_views" />
+                <include layout="@layout/notification_2025_top_line_views" />
 
             </NotificationTopLineView>
 
@@ -168,7 +168,6 @@
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:layout_gravity="top|end"
-                android:layout_margin="@dimen/notification_2025_margin"
                 />
 
         </FrameLayout>
diff --git a/core/res/res/layout/notification_2025_template_collapsed_call.xml b/core/res/res/layout/notification_2025_template_collapsed_call.xml
index ce38c164..6f3c15a 100644
--- a/core/res/res/layout/notification_2025_template_collapsed_call.xml
+++ b/core/res/res/layout/notification_2025_template_collapsed_call.xml
@@ -70,7 +70,6 @@
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:layout_gravity="top|end"
-                android:layout_margin="@dimen/notification_2025_margin"
                 />
 
         </FrameLayout>
diff --git a/core/res/res/layout/notification_2025_template_collapsed_media.xml b/core/res/res/layout/notification_2025_template_collapsed_media.xml
index 0021b83..5beab50 100644
--- a/core/res/res/layout/notification_2025_template_collapsed_media.xml
+++ b/core/res/res/layout/notification_2025_template_collapsed_media.xml
@@ -89,7 +89,7 @@
                 >
 
                 <!--
-                NOTE: The notification_top_line_views layout contains the app_name_text.
+                NOTE: The notification_2025_top_line_views layout contains the app_name_text.
                 In order to include the title view at the beginning, the Notification.Builder
                 has logic to hide that view whenever this title view is to be visible.
                 -->
@@ -106,7 +106,7 @@
                     android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title"
                     />
 
-                <include layout="@layout/notification_top_line_views" />
+                <include layout="@layout/notification_2025_top_line_views" />
 
             </NotificationTopLineView>
 
@@ -189,7 +189,6 @@
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:layout_gravity="top|end"
-                android:layout_margin="@dimen/notification_2025_margin"
                 />
 
         </FrameLayout>
diff --git a/core/res/res/layout/notification_2025_template_collapsed_messaging.xml b/core/res/res/layout/notification_2025_template_collapsed_messaging.xml
index f3e4ce1..d7c3263 100644
--- a/core/res/res/layout/notification_2025_template_collapsed_messaging.xml
+++ b/core/res/res/layout/notification_2025_template_collapsed_messaging.xml
@@ -115,7 +115,7 @@
                         >
 
                         <!--
-                        NOTE: The notification_top_line_views layout contains the app_name_text.
+                        NOTE: The notification_2025_top_line_views layout contains the app_name_text.
                         In order to include the title view at the beginning, the Notification.Builder
                         has logic to hide that view whenever this title view is to be visible.
                         -->
@@ -132,7 +132,7 @@
                             android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title"
                             />
 
-                        <include layout="@layout/notification_top_line_views" />
+                        <include layout="@layout/notification_2025_top_line_views" />
 
                     </NotificationTopLineView>
 
@@ -193,7 +193,6 @@
                         android:layout_width="wrap_content"
                         android:layout_height="wrap_content"
                         android:layout_gravity="top|end"
-                        android:layout_margin="@dimen/notification_2025_margin"
                         />
 
                 </FrameLayout>
diff --git a/core/res/res/layout/notification_2025_template_conversation.xml b/core/res/res/layout/notification_2025_template_conversation.xml
index 6be5a1c..24b6ad0 100644
--- a/core/res/res/layout/notification_2025_template_conversation.xml
+++ b/core/res/res/layout/notification_2025_template_conversation.xml
@@ -152,7 +152,6 @@
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
                     android:layout_gravity="top|end"
-                    android:layout_margin="@dimen/notification_2025_margin"
                     />
             </LinearLayout>
         </LinearLayout>
diff --git a/core/res/res/layout/notification_2025_template_header.xml b/core/res/res/layout/notification_2025_template_header.xml
index 3f34eb3..72b3798 100644
--- a/core/res/res/layout/notification_2025_template_header.xml
+++ b/core/res/res/layout/notification_2025_template_header.xml
@@ -68,7 +68,7 @@
         android:theme="@style/Theme.DeviceDefault.Notification"
         >
 
-        <include layout="@layout/notification_top_line_views" />
+        <include layout="@layout/notification_2025_top_line_views" />
 
     </NotificationTopLineView>
 
@@ -85,7 +85,6 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_gravity="top|end"
-        android:layout_margin="@dimen/notification_2025_margin"
         android:layout_alignParentEnd="true" />
 
     <include layout="@layout/notification_close_button"
diff --git a/core/res/res/layout/notification_2025_template_heads_up_base.xml b/core/res/res/layout/notification_2025_template_heads_up_base.xml
index e4ff835..084ec7d 100644
--- a/core/res/res/layout/notification_2025_template_heads_up_base.xml
+++ b/core/res/res/layout/notification_2025_template_heads_up_base.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?><!--
-  ~ Copyright (C) 2014 The Android Open Source Project
+  ~ Copyright (C) 2024 The Android Open Source Project
   ~
   ~ Licensed under the Apache License, Version 2.0 (the "License");
   ~ you may not use this file except in compliance with the License.
diff --git a/core/res/res/layout/notification_2025_top_line_views.xml b/core/res/res/layout/notification_2025_top_line_views.xml
new file mode 100644
index 0000000..7487346
--- /dev/null
+++ b/core/res/res/layout/notification_2025_top_line_views.xml
@@ -0,0 +1,159 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2025 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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 layout file should be included inside a NotificationTopLineView, sometimes after a
+ <TextView android:id="@+id/title"/>
+-->
+<merge
+    xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <TextView
+        android:id="@+id/app_name_text"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginEnd="@dimen/notification_header_separating_margin"
+        android:singleLine="true"
+        android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
+        android:visibility="?attr/notificationHeaderAppNameVisibility"
+        />
+
+    <TextView
+        android:id="@+id/header_text_secondary_divider"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
+        android:layout_marginStart="@dimen/notification_header_separating_margin"
+        android:layout_marginEnd="@dimen/notification_header_separating_margin"
+        android:text="@string/notification_header_divider_symbol"
+        android:visibility="gone"
+        />
+
+    <TextView
+        android:id="@+id/header_text_secondary"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
+        android:layout_marginStart="@dimen/notification_header_separating_margin"
+        android:layout_marginEnd="@dimen/notification_header_separating_margin"
+        android:visibility="gone"
+        android:singleLine="true"
+        />
+
+    <TextView
+        android:id="@+id/header_text_divider"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
+        android:layout_marginStart="@dimen/notification_header_separating_margin"
+        android:layout_marginEnd="@dimen/notification_header_separating_margin"
+        android:text="@string/notification_header_divider_symbol"
+        android:visibility="gone"
+        />
+
+    <TextView
+        android:id="@+id/header_text"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
+        android:layout_marginStart="@dimen/notification_header_separating_margin"
+        android:layout_marginEnd="@dimen/notification_header_separating_margin"
+        android:visibility="gone"
+        android:singleLine="true"
+        />
+
+    <TextView
+        android:id="@+id/time_divider"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Info"
+        android:layout_marginStart="@dimen/notification_header_separating_margin"
+        android:layout_marginEnd="@dimen/notification_header_separating_margin"
+        android:text="@string/notification_header_divider_symbol"
+        android:singleLine="true"
+        android:visibility="gone"
+        />
+
+    <DateTimeView
+        android:id="@+id/time"
+        android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Time"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="@dimen/notification_header_separating_margin"
+        android:layout_marginEnd="@dimen/notification_header_separating_margin"
+        android:showRelative="true"
+        android:singleLine="true"
+        android:visibility="gone"
+        />
+
+    <ViewStub
+        android:id="@+id/chronometer"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="@dimen/notification_header_separating_margin"
+        android:layout_marginEnd="@dimen/notification_header_separating_margin"
+        android:layout="@layout/notification_template_part_chronometer"
+        android:visibility="gone"
+        />
+
+    <ImageButton
+        android:id="@+id/feedback"
+        android:layout_width="@dimen/notification_feedback_size"
+        android:layout_height="@dimen/notification_feedback_size"
+        android:layout_marginStart="@dimen/notification_header_separating_margin"
+        android:baseline="13dp"
+        android:scaleType="fitCenter"
+        android:src="@drawable/ic_feedback_indicator"
+        android:background="?android:selectableItemBackgroundBorderless"
+        android:visibility="gone"
+        android:contentDescription="@string/notification_feedback_indicator"
+        />
+
+    <ImageView
+        android:id="@+id/phishing_alert"
+        android:layout_width="@dimen/notification_2025_badge_size"
+        android:layout_height="@dimen/notification_2025_badge_size"
+        android:layout_marginStart="@dimen/notification_2025_badge_margin"
+        android:baseline="@dimen/notification_2025_badge_baseline"
+        android:scaleType="fitCenter"
+        android:src="@drawable/ic_dialog_alert_material"
+        android:visibility="gone"
+        android:contentDescription="@string/notification_phishing_alert_content_description"
+        />
+
+    <ImageView
+        android:id="@+id/profile_badge"
+        android:layout_width="@dimen/notification_2025_badge_size"
+        android:layout_height="@dimen/notification_2025_badge_size"
+        android:layout_marginStart="@dimen/notification_2025_badge_margin"
+        android:baseline="@dimen/notification_2025_badge_baseline"
+        android:scaleType="fitCenter"
+        android:visibility="gone"
+        android:contentDescription="@string/notification_work_profile_content_description"
+        />
+
+    <ImageView
+        android:id="@+id/alerted_icon"
+        android:layout_width="@dimen/notification_2025_badge_size"
+        android:layout_height="@dimen/notification_2025_badge_size"
+        android:layout_marginStart="@dimen/notification_2025_badge_margin"
+        android:baseline="@dimen/notification_2025_badge_baseline"
+        android:contentDescription="@string/notification_alerted_content_description"
+        android:scaleType="fitCenter"
+        android:src="@drawable/ic_notifications_alerted"
+        android:visibility="gone"
+        />
+</merge>
+
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 4d96ade..1578bc2 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -87,6 +87,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"Kan nie selnetwerk bereik nie"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Probeer die voorkeurnetwerk verander. Tik om te verander."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Noodoproepe is onbeskikbaar"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"Noodoproepe vereis ’n selnetwerk."</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"Opletberigte"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"Oproepaanstuur"</string>
@@ -1778,6 +1780,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Eenhandmodus"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Ekstra donker"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Gehoortoestelle"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Het volumesleutels ingehou. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> aangeskakel."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Het volumesleutels ingehou. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> is afgeskakel"</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Los die volumesleutels. Druk en hou albei volumesleutels weer 3 sekondes lank in om <xliff:g id="SERVICE_NAME">%1$s</xliff:g> aan te skakel."</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index ecfce08..1140130 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -87,6 +87,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"የሞባይል አውታረ መረብን መድረስ አልተቻለም"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"ተመራጭ አውታረ መረብን ለመለወጥ ይሞክሩ። ለመለወጥ መታ ያድርጉ።"</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"የአደጋ ጊዜ ጥሪ አይገኝም"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"የአደጋ ጥሪዎች የተንቀሳቃሽ ስልክ አውታረ መረብ ያስፈልጋቸዋል"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"ማንቂያዎች"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"ጥሪ ማስተላለፍ"</string>
@@ -1778,6 +1780,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"የአንድ እጅ ሁነታ"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"ተጨማሪ ደብዛዛ"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"የመስሚያ መሣሪያዎች"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"የድምፅ ቁልፎችን ይዟል። <xliff:g id="SERVICE_NAME">%1$s</xliff:g> በርቷል።"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"የድምፅ ቁልፎችን ይዟል። <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ጠፍተዋል።"</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"የድምጽ መጠን ቁልፎቹን ይልቀቁ። <xliff:g id="SERVICE_NAME">%1$s</xliff:g>ን ለማብራት ሁለቱንም የድምጽ መጠን ቁልፎች በድጋሚ ለ3 ሰከንዶች ተጭነው ይያዙ።"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index dc047ac..02e5be1 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -76,8 +76,8 @@
     <string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"الإعداد التلقائي لمعرف المتصل هو غير محظور  . الاتصال التالي: محظور"</string>
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"الإعداد التلقائي لمعرف المتصل هو غير محظور  . الاتصال التالي: غير محظور"</string>
     <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"‏هذا التطبيق غير متوافق مع حجم الصفحة الذي يبلغ 16 كيلوبايت. وتعذَّر إكمال عملية التأكُّد من محاذاة ملفات APK. وسيتم تشغيل هذا التطبيق باستخدام الوضع المتوافق مع حجم الصفحة. ولكي يتوافق التطبيق مع الأجهزة بشكلٍ أفضل، يُرجى إعادة تحويله برمجيًا ليصبح متوافقًا مع حجم الصفحة الذي يبلغ 16 كيلوبايت. لمزيد من المعلومات، يُرجى الاطّلاع على الرابط &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
-    <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"‏هذا التطبيق غير متوافق مع حجم الصفحة الذي يبلغ 16 كيلوبايت. وتعذَّر إكمال عملية التأكُّد من محاذاة ملفات ELF. وسيتم تشغيل هذا التطبيق باستخدام الوضع المتوافق مع حجم الصفحة. ولكي يتوافق التطبيق مع الأجهزة بشكلٍ أفضل، يُرجى إعادة تحويله برمجيًا ليصبح متوافقًا مع حجم الصفحة الذي يبلغ 16 كيلوبايت. لمزيد من المعلومات، يُرجى الاطّلاع على الرابط &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
-    <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"‏هذا التطبيق غير متوافق مع حجم الصفحة الذي يبلغ 16 كيلوبايت. وتعذَّر إكمال عمليات التأكُّد من محاذاة ملفات APK وELF. وسيتم تشغيل هذا التطبيق باستخدام الوضع المتوافق مع حجم الصفحة. ولكي يتوافق التطبيق مع الأجهزة بشكلٍ أفضل، يُرجى إعادة تحويله برمجيًا ليصبح متوافقًا مع حجم الصفحة الذي يبلغ 16 كيلوبايت. لمزيد من المعلومات، يُرجى الاطّلاع على الرابط &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+    <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"‏هذا التطبيق غير متوافق مع حجم الصفحة الذي يبلغ 16 كيلوبايت. تعذَّر إكمال عملية التأكُّد من محاذاة ملفات ELF. سيتم تشغيل هذا التطبيق باستخدام الوضع المتوافق مع حجم الصفحة. لكي يتوافق التطبيق مع الأجهزة بشكلٍ أفضل، يُرجى إعادة تحويله برمجيًا ليصبح متوافقًا مع حجم الصفحة الذي يبلغ 16 كيلوبايت. لمزيد من المعلومات، يُرجى الانتقال إلى الرابط &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+    <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"‏هذا التطبيق غير متوافق مع حجم الصفحة الذي يبلغ 16 كيلوبايت. تعذَّر إكمال عمليات التأكُّد من محاذاة ملفات APK وELF. سيتم تشغيل هذا التطبيق باستخدام الوضع المتوافق مع حجم الصفحة. لكي يتوافق التطبيق مع الأجهزة بشكلٍ أفضل، يُرجى إعادة تحويله برمجيًا ليصبح متوافقًا مع حجم 16 كيلوبايت. لمزيد من المعلومات، يُرجى الاتنقال إلى الرابط &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"الخدمة غير متوفرة."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"لا يمكنك تغيير إعداد معرّف المتصل."</string>
     <string name="auto_data_switch_title" msgid="3286350716870518297">"تم تبديل البيانات إلى <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -91,6 +91,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"يتعذّر الوصول إلى شبكة الجوّال."</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"حاول تغيير الشبكة المفضلة. انقر لتغييرها."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"لا تتوفر إمكانية الاتصال في حالات الطوارئ."</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"يتطلّب إجراء مكالمات الطوارئ الاتصال بشبكة جوّال"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"التنبيهات"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"إعادة توجيه المكالمة"</string>
@@ -1782,6 +1784,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"وضع \"التصفح بيد واحدة\""</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"زيادة تعتيم الشاشة"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"سماعات الأذن الطبية"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"تم الضغط مع الاستمرار على مفتاحَي التحكّم في مستوى الصوت. تم تفعيل <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"تم الضغط مع الاستمرار على مفتاحَي التحكّم في مستوى الصوت. تم إيقاف <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"ارفع إصبعَيك عن مفتاحَي مستوى الصوت. لتفعيل خدمة \"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\"، اضغط مع الاستمرار على كلا مفتاحَي مستوى الصوت مجددًا لمدة 3 ثوانٍ."</string>
diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml
index 402394d0..9f3b263 100644
--- a/core/res/res/values-as/strings.xml
+++ b/core/res/res/values-as/strings.xml
@@ -87,6 +87,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"ম’বাইল নেটৱৰ্কৰ লগত সংযোগ কৰিব পৰা নাই"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"পচন্দৰ নেটৱৰ্ক সলনি কৰি চেষ্টা কৰি চাওক। সলনি কৰিবলৈ টিপক।"</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"জৰুৰীকালীন কল কৰাৰ সুবিধা উপলব্ধ নহয়"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"জৰুৰীকালীন কল কৰিবলৈ ম’বাইল নেটৱৰ্কৰ প্ৰয়োজন"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"সতৰ্কবাণীসমূহ"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"কল ফৰৱাৰ্ডিং"</string>
@@ -1778,6 +1780,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"এখন হাতেৰে ব্যৱহাৰ কৰাৰ ম’ড"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"অতিৰিক্তভাৱে পোহৰ কমোৱাৰ সুবিধা"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"শুনাৰ ডিভাইচ"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"ভলিউম কীসমূহ ধৰি ৰাখক। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> অন কৰা হ\'ল।"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"ভলিউম কী ধৰি ৰাখিছিল। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> অফ কৰা হ\'ল।"</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"ভলিউম কী এৰি দিয়ক। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> অন কৰিবলৈ, দুয়োটা ভলিউম কী পুনৰ ৩ ছেকেণ্ডৰ বাবে টিপি হেঁচি ৰাখক।"</string>
diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml
index c696e63..54062de 100644
--- a/core/res/res/values-az/strings.xml
+++ b/core/res/res/values-az/strings.xml
@@ -87,6 +87,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"Mobil şəbəkəyə daxil olmaq mümkün deyil"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Tərcih edilən şəbəkəni dəyişin. Dəyişmək üçün klikləyin."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Təcili zəng əlçatan deyil"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"Təcili zənglər üçün mobil şəbəkə tələb olunur"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"Siqnallar"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"Zəng yönləndirmə"</string>
@@ -1778,6 +1780,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Birəlli rejim"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Əlavə tündləşmə"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Eşitmə cihazları"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Səs səviyyəsi düymələrinə basıb saxlayın. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> aktiv edildi."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Səs səviyyəsi düymələrinə basılaraq saxlanıb. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> deaktiv edilib."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Səs düymələrini buraxın. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> xidmətini aktiv etmək üçün hər iki səs düyməsinə yenidən 3 saniyə basıb saxlayın."</string>
diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml
index dc39346..69b4612 100644
--- a/core/res/res/values-b+sr+Latn/strings.xml
+++ b/core/res/res/values-b+sr+Latn/strings.xml
@@ -88,6 +88,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"Povezivanje sa mobilnom mrežom nije uspelo"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Probajte da promenite željenu mrežu. Dodirnite da biste promenili."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Hitni pozivi nisu dostupni"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"Hitni pozivi zahtevaju mobilnu mrežu"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"Upozorenja"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"Preusmeravanje poziva"</string>
@@ -1779,6 +1781,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Režim jednom rukom"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Dodatno zatamni"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Slušni aparati"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Držali ste tastere za jačinu zvuka. Usluga <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je uključena."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Držali ste tastere za jačinu zvuka. Usluga <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je isključena."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Pustite tastere za jačinu zvuka. Da biste uključili <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, ponovo pritisnite i zadržite oba tastera za jačinu zvuka 3 sekunde."</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index d1a2104..9f7f823 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -89,6 +89,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"Сетка мабільнай сувязі недаступная"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Націсніце, каб выбраць іншую сетку."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Экстранныя выклікі недаступныя"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"Для экстранных выклікаў патрабуецца мабільная сетка"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"Абвесткі"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"Пераадрасацыя выкліку"</string>
@@ -1780,6 +1782,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Рэжым кіравання адной рукой"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Дадатковае памяншэнне яркасці"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Слыхавыя апараты"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Клавішы гучнасці ўтрымліваліся націснутымі. Уключана служба \"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\"."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Клавішы гучнасці ўтрымліваліся націснутымі. Служба \"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\" выключана."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Адпусціце клавішы гучнасці. Каб уключыць сэрвіс \"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\", націсніце абедзве клавішы гучнасці яшчэ раз і ўтрымлівайце іх на працягу 3 секунд."</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 3332195..1353d22 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -87,6 +87,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"Не може да се установи връзка с мобилната мрежа"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Изберете друга предпочитана мрежа. Докоснете за промяна."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Няма достъп до спешните обаждания"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"За спешните обаждания се изисква мобилна мрежа"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"Сигнали"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"Пренасочване на обаждания"</string>
@@ -1778,6 +1780,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Работа с една ръка"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Допълнително затъмняване"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Слухови апарати"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Задържахте бутоните за силата на звука. Услугата <xliff:g id="SERVICE_NAME">%1$s</xliff:g> е включена."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Задържахте бутоните за силата на звука. Услугата <xliff:g id="SERVICE_NAME">%1$s</xliff:g> е изключена."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Освободете бутоните за силата на звука. За да включите <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, отново натиснете двата бутона за силата на звука и задръжте за 3 секунди."</string>
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 2b19c57..d74a3a3 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -87,6 +87,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"মোবাইল নেটওয়ার্কে কানেক্ট করা যাচ্ছে না"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"পছন্দের নেটওয়ার্ক পরিবর্তন করে দেখুন। অন্য নেটওয়ার্ক বেছে নিতে ট্যাপ করুন।"</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"জরুরি কল করা যাবে না"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"জরুরি কলের জন্য মোবাইল নেটওয়ার্ক থাকতে হবে"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"সতর্কতা"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"কল ফরওয়ার্ড করা"</string>
@@ -1778,6 +1780,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"এক হাতে ব্যবহার করার মোড"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"অতিরিক্ত কম উজ্জ্বলতা"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"হিয়ারিং ডিভাইস"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"ভলিউম কী ধরে ছিলেন। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> চালু করা হয়েছে।"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"ভলিউম কী ধরে ছিলেন। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> বন্ধ করা হয়েছে।"</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"ভলিউম \'কী\' রিলিজ করুন। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> চালু করুন, দু\'টি ভলিউম \'কী\' আবার প্রেস করে ৩ সেকেন্ড ধরে রাখুন।"</string>
diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml
index c49e337..c916870 100644
--- a/core/res/res/values-bs/strings.xml
+++ b/core/res/res/values-bs/strings.xml
@@ -88,6 +88,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"Nije moguće dosegnuti mobilnu mrežu"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Pokušajte promijeniti preferiranu mrežu. Dodirnite za promjenu."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Hitni pozivi su nedostupni"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"Za hitne pozive potrebna je mreža"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"Upozorenja"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"Prosljeđivanje poziva"</string>
@@ -1779,6 +1781,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Način rada jednom rukom"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Dodatno zatamnjenje"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Slušni aparati"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Držali ste tipke za jačinu zvuka. Usluga <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je uključena."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Držali ste tipke za jačinu zvuka. Usluga <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je isključena."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Pustite tipke za jačinu zvuka. Da uključite uslugu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, ponovo pritisnite i zadržite obje tipke za jačinu zvuka 3 sekunde."</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 196f9ac..5c53857 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -88,6 +88,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"No es pot accedir a la xarxa mòbil"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Prova de canviar de xarxa preferent. Toca per canviar-la."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Les trucades d\'emergència no estan disponibles"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"Per poder fer trucades d\'emergència, cal tenir connexió de xarxa mòbil"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"Alertes"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"Desviació de trucades"</string>
@@ -1779,6 +1781,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Mode d\'una mà"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Atenuació extra"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Audiòfons"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"S\'han mantingut premudes les tecles de volum. S\'ha activat <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"S\'han mantingut premudes les tecles de volum. S\'ha desactivat <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Deixa anar les tecles de volum. Per activar <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, torna a mantenir premudes totes dues tecles de volum durant 3 segons."</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 8038a29..8d595d8 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -73,9 +73,9 @@
     <string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Ve výchozím nastavení je funkce ID volajícího omezena. Příští hovor: Neomezeno"</string>
     <string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Ve výchozím nastavení není funkce ID volajícího omezena. Příští hovor: Omezeno"</string>
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Ve výchozím nastavení není funkce ID volajícího omezena. Příští hovor: Neomezeno"</string>
-    <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Tato aplikace není kompatibilní s 16KB režimem. Kontrola zarovnání souboru APK selhala. Tato aplikace poběží v režimu kompatibilním s velikostmi stránek. Pro optimální kompatibilitu ji překompilujte s podporou 16KB režimu. Další informace najdete na stránce &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
-    <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Tato aplikace není kompatibilní s 16KB režimem. Kontrola zarovnání souboru ELF selhala. Tato aplikace poběží v režimu kompatibilním s velikostmi stránek. Pro optimální kompatibilitu ji překompilujte s podporou 16KB režimu. Další informace najdete na stránce &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
-    <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Tato aplikace není kompatibilní s 16KB režimem. Kontroly zarovnání souborů APK a ELF selhaly. Tato aplikace poběží v režimu kompatibilním s velikostmi stránek. Pro optimální kompatibilitu ji překompilujte s podporou 16KB režimu. Další informace najdete na stránce &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+    <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Tato aplikace není kompatibilní s 16KB stránkováním. Kontrola zarovnání v souboru APK selhala. Tato aplikace poběží v režimu kompatibility s velikostí stránky. Pro optimální kompatibilitu ji překompilujte s podporou 16KB stránkování. Další informace najdete na stránce &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+    <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Tato aplikace není kompatibilní s 16KB stránkováním. Kontrola zarovnání v souboru ELF selhala. Tato aplikace poběží v režimu kompatibility s velikostí stránky. Pro optimální kompatibilitu ji překompilujte s podporou 16KB stránkování. Další informace najdete na stránce &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+    <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Tato aplikace není kompatibilní s 16KB stránkováním. Kontroly zarovnání v souborech APK a ELF selhaly. Tato aplikace poběží v režimu kompatibility s velikostí stránky. Pro optimální kompatibilitu ji překompilujte s podporou 16KB stránkování. Další informace najdete na stránce &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"Služba není zřízena."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"Nastavení ID volajícího nesmíte měnit."</string>
     <string name="auto_data_switch_title" msgid="3286350716870518297">"Datové připojení bylo přepnuto na operátora <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -89,6 +89,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"Mobilní síť není dostupná"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Zkuste změnit preferovanou síť. Změníte ji klepnutím."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Tísňová volání jsou nedostupná"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"Tísňová volání vyžadují mobilní síť"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"Upozornění"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"Přesměrování hovorů"</string>
@@ -1780,6 +1782,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Režim jedné ruky"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Velmi tmavé zobrazení"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Naslouchátka"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Byla podržena tlačítka hlasitosti. Služba <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je zapnutá."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Byla podržena tlačítka hlasitosti. Služba <xliff:g id="SERVICE_NAME">%1$s</xliff:g> byla vypnuta."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Uvolněte tlačítka hlasitosti. Pokud chcete zapnout službu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, znovu tři sekundy podržte obě tlačítka hlasitosti."</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 5403cce..66e3402 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -87,6 +87,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"Der er ingen forbindelse til mobilnetværket"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Prøv at skifte dit foretrukne netværk. Tryk for skifte."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Det er ikke muligt at foretage nødopkald"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"Nødopkald kræver adgang til et mobilnetværk"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"Underretninger"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"Viderestilling af opkald"</string>
@@ -1778,6 +1780,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Enhåndstilstand"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Ekstra dæmpet belysning"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Høreapparater"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Lydstyrkeknapperne blev holdt nede. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> er aktiveret."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Lydstyrkeknapperne blev holdt nede. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> er deaktiveret."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Slip lydstyrkeknapperne. Du kan aktivere <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ved at holde begge lydstyrkeknapper nede igen i 3 sekunder."</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 66bc41f..6311880 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -87,6 +87,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"Mobilfunknetz nicht erreichbar"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Versuche, das bevorzugte Netzwerk zu ändern. Tippe, um ein anderes auszuwählen."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Notrufe nicht möglich"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"Für Notrufe ist ein Mobilfunknetz erforderlich"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"Warnmeldungen"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"Anrufweiterleitung"</string>
@@ -1778,6 +1780,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Einhandmodus"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Extradunkel"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Hörgeräte"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Lautstärketasten wurden gedrückt gehalten. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ist aktiviert."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Lautstärketasten wurden gedrückt gehalten. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ist deaktiviert."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Lass die Lautstärketasten los. Halte zum Aktivieren von <xliff:g id="SERVICE_NAME">%1$s</xliff:g> beide Lautstärketasten noch einmal 3 Sekunden lang gedrückt."</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 1aae63f..23fc44c 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -87,6 +87,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"Δεν είναι δυνατή η σύνδεση στο δίκτυο κινητής τηλεφωνίας"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Δοκιμάστε να αλλάξετε το προτιμώμενο δίκτυο. Πατήστε για αλλαγή."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Οι κλήσεις έκτακτης ανάγκης δεν είναι διαθέσιμες"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"Για τις κλήσεις έκτακτης ανάγκης απαιτείται δίκτυο κινητής τηλεφωνίας"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"Ειδοποιήσεις"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"Προώθηση κλήσης"</string>
@@ -1778,6 +1780,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Λειτουργία ενός χεριού"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Επιπλέον μείωση φωτεινότητας"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Συσκευές ακοής"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Τα πλήκτρα έντασης είναι πατημένα. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ενεργοποιήθηκε."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Τα πλήκτρα έντασης είναι πατημένα. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>: απενεργοποιημένο"</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Αφήστε τα κουμπιά έντασης ήχου. Για να ενεργοποιήσετε την υπηρεσία <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, πατήστε ξανά παρατεταμένα και τα δύο κουμπιά έντασης ήχου για τρία δευτερόλεπτα."</string>
diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml
index 3c5e02a..823db75 100644
--- a/core/res/res/values-en-rAU/strings.xml
+++ b/core/res/res/values-en-rAU/strings.xml
@@ -87,6 +87,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"Can’t reach mobile network"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Try changing preferred network. Tap to change."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Emergency calling unavailable"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"Emergency calls require a mobile network"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"Alerts"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"Call forwarding"</string>
@@ -1778,6 +1780,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"One-handed mode"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Extra dim"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Hearing devices"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned on."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned off."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Release the volume keys. To turn on <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, press and hold both volume keys again for three seconds."</string>
diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml
index a212808..83fd118 100644
--- a/core/res/res/values-en-rCA/strings.xml
+++ b/core/res/res/values-en-rCA/strings.xml
@@ -87,6 +87,7 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"Can’t reach mobile network"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Try changing preferred network. Tap to change."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Emergency calling unavailable"</string>
+    <string name="emergency_calling_do_not_show_again" msgid="5034171343309733068">"Do Not Show Again"</string>
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"Emergency calls require a mobile network"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"Alerts"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"Call forwarding"</string>
@@ -1778,6 +1779,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"One-Handed mode"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Extra dim"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Hearing devices"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned on."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned off."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Release the volume keys. To turn on <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, press and hold both volume keys again for 3 seconds."</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index cf02080..46a0d2b 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -87,6 +87,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"Can’t reach mobile network"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Try changing preferred network. Tap to change."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Emergency calling unavailable"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"Emergency calls require a mobile network"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"Alerts"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"Call forwarding"</string>
@@ -1778,6 +1780,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"One-handed mode"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Extra dim"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Hearing devices"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned on."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned off."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Release the volume keys. To turn on <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, press and hold both volume keys again for three seconds."</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index 30b6011..22cf48f 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -87,6 +87,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"Can’t reach mobile network"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Try changing preferred network. Tap to change."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Emergency calling unavailable"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"Emergency calls require a mobile network"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"Alerts"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"Call forwarding"</string>
@@ -1778,6 +1780,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"One-handed mode"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Extra dim"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Hearing devices"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned on."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Held volume keys. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> turned off."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Release the volume keys. To turn on <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, press and hold both volume keys again for three seconds."</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 867e8c5..b956286 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -73,8 +73,8 @@
     <string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"El identificador de llamadas está predeterminado en no restringido. Llamada siguiente: restringida"</string>
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"El Identificador de llamadas está predeterminado en no restringido. Llamada siguiente: no restringido"</string>
     <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Esta app no es compatible con 16 KB. No se pudo verificar la alineación del APK. Esta app se ejecutará con el modo de compatibilidad de tamaño de página. Para obtener mejores resultados, vuelve a compilar la aplicación con la compatibilidad de 16 KB. Para obtener más información, consulta el siguiente vínculo: &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
-    <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Esta app no es compatible con 16 KB. No se pudo verificar la alineación de ELF. Esta app se ejecutará con el modo de compatibilidad de tamaño de página. Para obtener mejores resultados, vuelve a compilar la aplicación con la compatibilidad de 16 KB. Para obtener más información, consulta el siguiente vínculo: &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
-    <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Esta app no es compatible con 16 KB. No se pudieron verificar las alineaciones de APK y ELF. Esta app se ejecutará con el modo de compatibilidad de tamaño de página. Para obtener mejores resultados, vuelve a compilar la aplicación con la compatibilidad de 16 KB. Para obtener más información, consulta el siguiente vínculo: &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+    <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Esta app no es compatible con 16 KB. No se pudo verificar la alineación de ELF. La app se ejecutará con el modo de compatibilidad de tamaño de página. Para obtener mejores resultados, vuelve a compilar la aplicación con la compatibilidad de 16 KB. Si necesitas más información, consulta el siguiente vínculo: &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+    <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Esta app no es compatible con 16 KB. No se pudieron verificar las alineaciones de APK y ELF. La app se ejecutará con el modo de compatibilidad de tamaño de página. Para obtener mejores resultados, vuelve a compilar la aplicación con la compatibilidad de 16 KB. Si necesitas más información, consulta el siguiente vínculo: &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"Servicio no suministrado."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"No puedes cambiar la configuración del identificador de llamadas."</string>
     <string name="auto_data_switch_title" msgid="3286350716870518297">"Se cambiaron los datos a <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -88,6 +88,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"No se puede acceder a la red móvil"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Presiona para cambiar la red preferida."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Servicio de llamadas de emergencia no disponible"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"Las llamadas de emergencia requieren una red móvil"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"Alertas"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"Desvío de llamada"</string>
@@ -1779,6 +1781,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Modo de una mano"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Atenuación extra"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Dispositivos auditivos"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Como mantuviste presionadas las teclas de volumen, se activó <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Se presionaron las teclas de volumen. Se desactivó <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Suelta las teclas de volumen. Para activar <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, vuelve a mantener presionadas las teclas de volumen durante 3 segundos."</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 1f10c5d..c50fbc7 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -88,6 +88,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"No se puede establecer conexión con la red móvil"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Toca para cambiar la red preferida."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Servicio de llamadas de emergencia no disponible"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"Para hacer llamadas de emergencia, necesitas conectarte a una red móvil"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"Alertas"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"Desvío de llamadas"</string>
@@ -1779,6 +1781,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Modo Una mano"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Atenuación extra"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Audífonos"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Al mantener pulsadas las teclas de volumen, se ha activado <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Se han mantenido pulsadas las teclas de volumen. Se ha desactivado <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Suelta las teclas de volumen. Para activar <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, mantén pulsadas las dos teclas de volumen de nuevo durante 3 segundos."</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index 58f5f20..3e3e5db 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -87,6 +87,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"Mobiilsidevõrguga ei saa ühendust"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Proovige eelistatud võrku vahetada. Puudutage muutmiseks."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Hädaabikõned pole saadaval"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"Hädaabikõnede jaoks on vajalik mobiilsidevõrk"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"Teatised"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"Kõnede suunamine"</string>
@@ -1778,6 +1780,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Ühekäerežiim"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Eriti tume"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Kuuldeseadmed"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Helitugevuse klahve hoiti all. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> lülitati sisse."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Helitugevuse klahve hoiti all. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> lülitati välja."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Vabastage helitugevuse klahvid. Teenuse <xliff:g id="SERVICE_NAME">%1$s</xliff:g> sisselülitamiseks vajutage uuesti mõlemat helitugevuse klahvi ja hoidke neid 3 sekundit all."</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index e470f0e..2f477f3 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -73,7 +73,7 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Deitzailearen identitatea zerbitzuaren balio lehenetsiak ez du murriztapenik ezartzen. Hurrengo deia: murriztapenik gabe."</string>
     <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Aplikazio hau ez da bateragarria 16 KBko bertsioarekin. Ezin izan da egiaztatu APKren lerrokatzea. Orriaren tamainarekin bateragarria den modua erabilita exekutatuko da aplikazioa. Emaitza onenak lortzeko, konpilatu aplikazioa berriro 16 KBko bertsioarekin bateragarria izan dadin. Informazio gehiago lortzeko, joan hona: &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;."</string>
     <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Aplikazio hau ez da bateragarria 16 KBko bertsioarekin. Ezin izan da egiaztatu ELFaren lerrokatzea. Orriaren tamainarekin bateragarria den modua erabilita exekutatuko da aplikazioa. Emaitza onenak lortzeko, konpilatu aplikazioa berriro 16 KBko bertsioarekin bateragarria izan dadin. Informazio gehiago lortzeko, joan hona: &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;."</string>
-    <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Aplikazio hau ez da bateragarria 16 KBko bertsioarekin. Ezin izan da egiaztatu APK eta ELF arteko lerrokatzea. Orriaren tamainarekin bateragarria den modua erabilita exekutatuko da aplikazioa. Emaitza onenak lortzeko, konpilatu aplikazioa berriro 16 KBko bertsioarekin bateragarria izan dadin. Informazio gehiago lortzeko, joan hona: &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;."</string>
+    <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Aplikazio hau ez da bateragarria 16 KB-ko bertsioarekin. Ezin izan da egiaztatu APK eta ELF arteko lerrokatzea. Orriaren tamainarekin bateragarria den modua erabilita exekutatuko da aplikazioa. Emaitza onenak lortzeko, konpilatu aplikazioa berriro 16 KB-ko bertsioarekin bateragarria izan dadin. Informazio gehiago lortzeko, joan hona: &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;."</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"Zerbitzua ez da hornitu."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"Ezin duzu aldatu deitzailearen identitatearen ezarpena."</string>
     <string name="auto_data_switch_title" msgid="3286350716870518297">"<xliff:g id="CARRIERDISPLAY">%s</xliff:g> operadorearen datu-konexiora aldatu zara"</string>
@@ -87,6 +87,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"Ezin da konektatu sare mugikorrera"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Aldatu sare hobetsia. Sakatu aldatzeko."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Ezin da egin larrialdi-deirik"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"Sare mugikorrera konektatuta egon behar duzu larrialdi-deiak egin ahal izateko"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"Alertak"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"Dei-desbideratzea"</string>
@@ -1778,6 +1780,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Esku bakarreko modua"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Are ilunago"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Entzumen-gailuak"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Bolumen-botoiak sakatuta eduki direnez, <xliff:g id="SERVICE_NAME">%1$s</xliff:g> aktibatu egin da."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Bolumen-botoiak sakatuta eduki direnez, <xliff:g id="SERVICE_NAME">%1$s</xliff:g> desaktibatu egin da."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Askatu bolumen-botoiak. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> aktibatzeko, eduki sakatuta berriro bi bolumen-botoiak hiru segundoz."</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 954f6ba..d8fdff6 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -87,6 +87,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"شبکه تلفن همراه دردسترس نیست"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"تغییر شبکه ترجیحی را امتحان کنید. برای تغییر، تک‌ضرب بزنید."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"تماس اضطراری امکان‌پذیر نیست"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"برای برقراری تماس اضطراری به شبکه تلفن همراه نیاز دارید"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"هشدارها"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"بازارسال تماس"</string>
@@ -1778,6 +1780,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"حالت یک‌دستی"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"بسیار کم‌نور"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"دستگاه‌های کمک‌شنوایی"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"کلیدهای میزان صدا پایین نگه داشته شد. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> روشن شد."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"کلیدهای میزان صدا پایین نگه داشته شد. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> خاموش شد."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"کلیدهای میزان صدا را رها کنید. برای روشن کردن <xliff:g id="SERVICE_NAME">%1$s</xliff:g>، هر دو کلید میزان صدا را مجدداً به‌مدت ۳ ثانیه فشار دهید و نگه دارید."</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 96609c9..9f88732 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -87,6 +87,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"Mobiiliverkkoon ei saada yhteyttä"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Kokeile vaihtaa ensisijaista verkkoa. Vaihda se napauttamalla."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Hätäpuhelut eivät ole käytettävissä"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"Hätäpuhelu edellyttää mobiiliverkkoa"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"Ilmoitukset"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"Soitonsiirto"</string>
@@ -1778,6 +1780,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Yhden käden moodi"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Erittäin himmeä"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Kuulolaitteet"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Äänenvoimakkuuspainikkeita painettiin pitkään. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> laitettiin päälle."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Äänenvoimakkuuspainikkeita painettiin pitkään. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> laitettiin pois päältä."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Vapauta äänenvoimakkuuspainikkeet. Laita <xliff:g id="SERVICE_NAME">%1$s</xliff:g> päälle painamalla äänenvoimakkuuspainikkeita uudelleen kolmen sekunnin ajan."</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index 50a7696..c535bc0 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -88,6 +88,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"Impossible de joindre le réseau cellulaire"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Essayez de changer de réseau préféré. Touchez l\'écran pour changer."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Le service d\'appel d\'urgence n\'est pas accessible"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"Les appels d\'urgence nécessitent un réseau cellulaire"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"Alertes"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"Transfert d\'appel"</string>
@@ -1779,6 +1781,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Mode Une main"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Très sombre"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Appareils auditifs"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Touches de volume maintenues enfoncées. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> activé."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Touches de volume maintenues enfoncées. Service <xliff:g id="SERVICE_NAME">%1$s</xliff:g> désactivé."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Relâchez les touches de volume. Pour activer <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, maintenez les deux touches de volume enfoncées pendant 3 secondes."</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index a72444d..aa45f91 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -73,8 +73,8 @@
     <string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Par défaut, les numéros des appelants ne sont pas restreints. Appel suivant : restreint"</string>
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Par défaut, les numéros des appelants ne sont pas restreints. Appel suivant : non restreint"</string>
     <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Cette appli n\'est pas compatible avec les pages de 16 ko. Échec de la vérification de l\'alignement de l\'APK. Cette appli sera exécutée dans un mode compatible avec la taille de la page. Pour une compatibilité optimale, veuillez recompiler l\'application de manière à ce que la taille de 16 ko soit prise en charge. Pour en savoir plus, consultez &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
-    <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Cette appli n\'est pas compatible avec les pages de 16 ko. Échec de la vérification de l\'alignement de l\'ELF. Cette appli sera exécutée dans un mode compatible avec la taille de la page. Pour une compatibilité optimale, veuillez recompiler l\'application de manière à ce que la taille de 16 ko soit prise en charge. Pour en savoir plus, consultez &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
-    <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Cette appli n\'est pas compatible avec les pages de 16 ko. Échec des vérifications de l\'alignement de l\'APK et de l\'ELF. Cette appli sera exécutée dans un mode compatible avec la taille de la page. Pour une compatibilité optimale, veuillez recompiler l\'application de manière à ce que la taille de 16 ko soit prise en charge. Pour en savoir plus, consultez &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+    <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Cette appli n\'est pas compatible avec les pages de 16 ko. Échec de la vérification de l\'alignement de l\'ELF. Cette appli sera exécutée dans un mode compatible avec la taille de la page. Pour une compatibilité optimale, veuillez recompiler l\'application afin de prendre en charge les pages de 16 ko. Pour en savoir plus, consultez &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+    <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Cette appli n\'est pas compatible avec les pages de 16 ko. Échec des vérifications de l\'alignement de l\'APK et de l\'ELF. Cette appli sera exécutée dans un mode compatible avec la taille de la page. Pour une compatibilité optimale, veuillez recompiler l\'application afin de prendre en charge les pages de 16 ko. Pour en savoir plus, consultez &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"Ce service n\'est pas pris en charge."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"Impossible de modifier le paramètre relatif au numéro de l\'appelant."</string>
     <string name="auto_data_switch_title" msgid="3286350716870518297">"Données transférées vers <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -88,6 +88,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"Impossible d\'accéder au réseau mobile"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Essayez de changer le réseau préféré. Appuyez pour le modifier."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Appels d\'urgence non disponibles"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"Les appels d\'urgence requièrent un réseau mobile"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"Alertes"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"Transfert d\'appel"</string>
@@ -1779,6 +1781,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Mode une main"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Luminosité ultra-réduite"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Appareils auditifs"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Touches de volume appuyées de manière prolongée. Service <xliff:g id="SERVICE_NAME">%1$s</xliff:g> activé."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Touches de volume appuyées de manière prolongée. Service <xliff:g id="SERVICE_NAME">%1$s</xliff:g> désactivé."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Relâchez les boutons de volume. Pour activer <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, appuyez de nouveau sur les deux boutons de volume pendant trois secondes."</string>
diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml
index 27454b9..a1016b6 100644
--- a/core/res/res/values-gl/strings.xml
+++ b/core/res/res/values-gl/strings.xml
@@ -87,6 +87,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"Non se puido conectar coa rede de telefonía móbil"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Proba a cambiar a rede preferida. Toca para cambiar."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"As chamadas de emerxencia non están dispoñibles"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"As chamadas de emerxencia precisan unha rede de telefonía móbil"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"Alertas"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"Desvío de chamadas"</string>
@@ -1778,6 +1780,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Modo dunha soa man"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Atenuación extra"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Dispositivos auditivos"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Teclas de volume premidas. Activouse o servizo <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Teclas de volume premidas. Desactivouse <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Solta as teclas de volume. Para activar <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, mantenas premidas de novo durante 3 segundos."</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index 4e4c9ff..046939b 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -87,6 +87,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"મોબાઇલ નેટવર્ક સુધી પહોંચી શકાતું નથી"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"પસંદગીનું નેટવર્ક બદલવાનો પ્રયાસ કરો. બદલવા માટે ટૅપ કરો."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"કટોકટીની કૉલિંગ સેવા અનુપલબ્ધ"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"ઇમર્જન્સી કૉલ માટે મોબાઇલ નેટવર્કની આવશ્યકતા છે"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"અલર્ટ"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"કૉલ ફૉર્વર્ડિંગ"</string>
@@ -1778,6 +1780,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"એક-હાથે વાપરો મોડ"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"એક્સ્ટ્રા ડિમ"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"સાંભળવામાં સહાય કરતા ડિવાઇસ"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"વૉલ્યૂમ કી દબાવી રાખો. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ચાલુ કરી."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"વૉલ્યૂમ કી દબાવી રાખો. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> બંધ કરી."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"વૉલ્યૂમ કી છોડી દો. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>ને ચાલુ કરવા માટે, 3 સેકન્ડ માટે બન્ને વૉલ્યૂમ કીને ફરીથી દબાવી રાખો."</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 624d7a5..7717d02 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -72,8 +72,8 @@
     <string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"कॉलर आईडी डिफ़ॉल्ट रूप से सीमित नहीं है. अगली कॉल: सीमित"</string>
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"कॉलर आईडी डिफ़ॉल्ट रूप से सीमित नहीं है. अगली कॉल: सीमित नहीं"</string>
     <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"यह ऐप्लिकेशन 16 केबी वाले पेजों के साथ काम नहीं करता. APK के अलाइनमेंट की जांच नहीं की जा सकी. यह ऐप्लिकेशन, पेज साइज़ के साथ काम करने वाले मोड का इस्तेमाल करके चलेगा. पेज साइज़ के साथ बेहतर तरीके से काम के लिए, कृपया 16 केबी वाले पेजों के साथ ऐप्लिकेशन को फिर से कंपाइल करें. ज़्यादा जानकारी के लिए, &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; पर जाएं"</string>
-    <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"यह ऐप्लिकेशन 16 केबी वाले पेजों के साथ काम नहीं करता. ईएलएफ़ अलाइनमेंट की जांच नहीं की जा सकी. यह ऐप्लिकेशन, पेज साइज़ के साथ काम करने वाले मोड का इस्तेमाल करके चलेगा. पेज साइज़ के साथ बेहतर तरीके से काम के लिए, कृपया 16 केबी वाले पेजों के साथ ऐप्लिकेशन को फिर से कंपाइल करें. ज़्यादा जानकारी के लिए, &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; पर जाएं"</string>
-    <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"यह ऐप्लिकेशन 16 केबी वाले पेजों के साथ काम नहीं करता. APK और ईएलएफ़ के अलाइनमेंट की जांच नहीं की जा सकी. यह ऐप्लिकेशन, पेज साइज़ के साथ काम करने वाले मोड का इस्तेमाल करके चलेगा. पेज साइज़ के साथ बेहतर तरीके से काम के लिए, कृपया 16 केबी वाले पेजों के साथ ऐप्लिकेशन को फिर से कंपाइल करें. ज़्यादा जानकारी के लिए, &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; पर जाएं"</string>
+    <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"यह ऐप्लिकेशन 16 केबी वाले पेज साइज़ के साथ काम नहीं करता. इसके लिए ज़रूरी ईएलएफ़ अलाइनमेंट की जांच भी पूरी नहीं हो सकी. यह ऐप्लिकेशन, इस पेज साइज़ के साथ काम करने वाले मोड का इस्तेमाल करके चलेगा. इस पेज साइज़ के साथ बेहतर तरीके से काम के लिए, कृपया ऐप्लिकेशन को 16 केबी वाले पेज साइज़ के साथ फिर से कंपाइल करें. ज़्यादा जानकारी के लिए, &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; पर जाएं"</string>
+    <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"यह ऐप्लिकेशन 16 केबी वाले पेज साइज़ के साथ काम नहीं करता. APK और ईएलएफ़ के अलाइनमेंट से जुड़ी ज़रूरी जांच भी पूरी नहीं हो सकी. यह ऐप्लिकेशन, इस पेज साइज़ के साथ काम करने वाले मोड का इस्तेमाल करके चलेगा. इस पेज साइज़ के साथ बेहतर तरीके से काम के लिए, कृपया ऐप्लिकेशन को 16 केबी वाले पेज साइज़ के साथ फिर से कंपाइल करें. ज़्यादा जानकारी के लिए, &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; पर जाएं"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"सेवा प्रावधान की हुई नहीं है."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"आप कॉलर आईडी सेटिंग नहीं बदल सकते."</string>
     <string name="auto_data_switch_title" msgid="3286350716870518297">"डेटा को <xliff:g id="CARRIERDISPLAY">%s</xliff:g> पर स्विच किया गया"</string>
@@ -87,6 +87,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"मोबाइल नेटवर्क से कनेक्ट नहीं किया जा सका"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"पसंदीदा नेटवर्क बदलकर देखें. बदलने के लिए टैप करें."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"आपातकालीन कॉल करने की सुविधा उपलब्ध नहीं है"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"आपातकालीन कॉल के लिए मोबाइल नेटवर्क ज़रूरी है"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"सूचनाएं"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"कॉल को दूसरे नंबर पर भेजना"</string>
@@ -1778,6 +1780,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"वन-हैंडेड मोड"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"स्क्रीन की रोशनी को सामान्य लेवल से और कम करने की सुविधा"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"कान की मशीन"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"आवाज़ कम-ज़्यादा करने वाले दोनों बटन दबाकर रखें. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> को चालू कर दिया गया."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"आवाज़ कम-ज़्यादा करने वाले दोनों बटन दबाकर रखें. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> को बंद कर दिया गया."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"आवाज़ बटन को छोड़ें. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> की सुविधा चालू करने के लिए, आवाज़ वाले दोनों बटन तीन सेकंड तक दबाकर रखें."</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 3429c14..118b539 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -72,9 +72,9 @@
     <string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Zadana postavka ID-a pozivatelja ima ograničenje. Sljedeći poziv: Nije ograničen"</string>
     <string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Zadana postavka ID-a pozivatelja nema ograničenje. Sljedeći poziv: Ograničen"</string>
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Zadana postavka ID-a pozivatelja nema ograničenje. Sljedeći poziv: Nije ograničen"</string>
-    <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Ova aplikacija nije kompatibilna s veličinom stranice od 16 KB. Provjera poravnanja APK-a nije uspjela. Ova će se aplikacija pokrenuti pomoću načina kompatibilnog s veličinom stranice. Za najbolju kompatibilnost ponovo kompilirajte aplikaciju s podrškom od 16 KB. Više informacija potražite na stranici &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
-    <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Ova aplikacija nije kompatibilna s veličinom stranice od 16 KB. Provjera poravnanja ELF-a nije uspjela. Ova će se aplikacija pokrenuti pomoću načina kompatibilnog s veličinom stranice. Za najbolju kompatibilnost ponovo kompilirajte aplikaciju s podrškom od 16 KB. Više informacija potražite na stranici &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
-    <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Ova aplikacija nije kompatibilna s veličinom stranice od 16 KB. Provjere poravnanja APK-a i ELF-a nisu uspjele. Ova će se aplikacija pokrenuti pomoću načina kompatibilnog s veličinom stranice. Za najbolju kompatibilnost ponovo kompilirajte aplikaciju s podrškom od 16 KB. Više informacija potražite na stranici &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+    <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Ova aplikacija nije kompatibilna sa 16 KB stranicama. Provjera usklađenosti APK-a nije uspjela. Aplikacija će se pokrenuti u načinu kompatibilnom s formatom stranice. Da biste postigli kompatibilnost, ponovo kompilirajte aplikaciju s podrškom za 16 KB stranice. Više informacija potražite na stranici &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+    <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Ova aplikacija nije kompatibilna sa 16 KB stranicama. Provjera usklađenosti ELF-a nije uspjela. Aplikacija će se pokrenuti u načinu kompatibilnom s formatom stranice. Da biste postigli kompatibilnost, ponovo kompilirajte aplikaciju s podrškom za 16 KB stranice. Više informacija potražite na stranici &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+    <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Ova aplikacija nije kompatibilna sa 16 KB stranicama. Provjere usklađenosti APK-a i ELF-a nisu uspjele. Aplikacija će se pokrenuti u načinu kompatibilnom s formatom stranice. Da biste postigli kompatibilnost, ponovo kompilirajte aplikaciju s podrškom za 16 KB stranice. Više informacija potražite na stranici &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"Usluga nije rezervirana."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"Ne možete promijeniti postavku ID-a pozivatelja."</string>
     <string name="auto_data_switch_title" msgid="3286350716870518297">"Podaci su prebačeni na <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -88,6 +88,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"Mobilna mreža nije dostupna"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Pokušajte promijeniti preferiranu mrežu. Dodirnite da biste je promijenili."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Hitni pozivi nisu dostupni"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"Za hitne pozive potrebna je mobilna mreža"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"Upozorenja"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"Preusmjeravanje poziva"</string>
@@ -1779,6 +1781,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Način rada jednom rukom"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Još tamnije"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Slušna pomagala"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Držali ste tipke za glasnoću. Uključila se usluga <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Držali ste tipke za glasnoću. Isključila se usluga <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Pustite tipke za glasnoću. Da biste uključili uslugu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, ponovo pritisnite i zadržite obje tipke za glasnoću tri sekunde."</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 2d46a3c..09f162e 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -87,6 +87,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"A mobilhálózat nem érhető el"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Próbálja meg módosítani a preferált hálózatot. Koppintson a módosításhoz."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Segélyhívás nem lehetséges"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"A segélyhíváshoz mobilhálózatra van szükség"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"Értesítések"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"Hívásátirányítás"</string>
@@ -1778,6 +1780,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Egykezes mód"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Extrasötét"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Hallásjavító eszközök"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Nyomva tartotta a hangerőgombokat. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> bekapcsolva."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Nyomva tartotta a hangerőgombokat. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> kikapcsolva."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Engedje fel a hangerőszabályzó gombokat. A(z) <xliff:g id="SERVICE_NAME">%1$s</xliff:g> bekapcsolásához tartsa újra lenyomva a hangerőszabályzó gombokat három másodpercig."</string>
diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml
index 82d141f..7a821a6 100644
--- a/core/res/res/values-hy/strings.xml
+++ b/core/res/res/values-hy/strings.xml
@@ -71,9 +71,9 @@
     <string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Զանգողի ID-ն լռելյայն սահմանափակված է: Հաջորդ զանգը` չսահմանափակված"</string>
     <string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Զանգողի ID-ն լռելյայն չսահմանափակված է: Հաջորդ զանգը` Սահմանափակված"</string>
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Զանգողի ID-ն լռելյայն չսահմանափակված է: Հաջորդ զանգը` չսահմանափակված"</string>
-    <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Այս հավելվածը հարմարեցված չէ 16 ԿԲ չափի համար։ APK-ի հետ համատեղելիությունը ձախողվել է։ Այս հավելվածը կաշխատի էջի չափի հետ համատեղելի ռեժիմում։ Համատեղելիությունն ապահովելու համար նորից կոմպիլացրեք հավելվածը 16 ԿԲ չափն աջակցելու համար։ Լրացուցիչ տեղեկություններ ստանալու համար այցելեք &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
-    <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Այս հավելվածը հարմարեցված չէ 16 ԿԲ չափի համար։ ELF-ի հետ համատեղելիությունը ձախողվել է։ Այս հավելվածը կաշխատի էջի չափի հետ համատեղելի ռեժիմում։ Համատեղելիությունն ապահովելու համար նորից կոմպիլացրեք հավելվածը 16 ԿԲ չափն աջակցելու համար։ Լրացուցիչ տեղեկություններ ստանալու համար այցելեք &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
-    <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Այս հավելվածը հարմարեցված չէ 16 ԿԲ չափի համար։ APK-ի և ELF-ի հետ համատեղելիության ստուգումը ձախողվել է։ Այս հավելվածը կաշխատի էջի չափի հետ համատեղելի ռեժիմում։ Համատեղելիությունն ապահովելու համար նորից կոմպիլացրեք հավելվածը 16 ԿԲ չափն աջակցելու համար։ Լրացուցիչ տեղեկություններ ստանալու համար այցելեք &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+    <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Այս հավելվածը հարմարեցված չէ 16 ԿԲ ծավալի համար։ APK-ի հետ համատեղելիության ստուգումը ձախողվել է։ Այս հավելվածը կաշխատի էջի ծավալի հետ համատեղելի ռեժիմում։ Համատեղելիությունն ապահովելու համար նորից կոմպիլացրեք հավելվածը 16 ԿԲ ծավալն աջակցելու համար։ Լրացուցիչ տեղեկություններ ստանալու համար այցելեք &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+    <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Այս հավելվածը հարմարեցված չէ 16 ԿԲ ծավալի համար։ ELF-ի հետ համատեղելիության ստուգումը ձախողվել է։ Այս հավելվածը կաշխատի էջի ծավալի հետ համատեղելի ռեժիմում։ Համատեղելիությունն ապահովելու համար նորից կոմպիլացրեք հավելվածը 16 ԿԲ ծավալն աջակցելու համար։ Լրացուցիչ տեղեկություններ ստանալու համար այցելեք &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+    <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Այս հավելվածը հարմարեցված չէ 16 ԿԲ ծավալի համար։ APK-ի և ELF-ի հետ համատեղելիության ստուգումը ձախողվել է։ Այս հավելվածը կաշխատի էջի ծավալի հետ համատեղելի ռեժիմում։ Համատեղելիությունն ապահովելու համար նորից կոմպիլացրեք հավելվածը 16 ԿԲ ծավալն աջակցելու համար։ Լրացուցիչ տեղեկություններ ստանալու համար այցելեք &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"Ծառայությունը չի տրամադրվում:"</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"Դուք չեք կարող փոխել զանգողի ID-ի կարգավորումները:"</string>
     <string name="auto_data_switch_title" msgid="3286350716870518297">"Օգտագործվում է <xliff:g id="CARRIERDISPLAY">%s</xliff:g>-ի բջջային ինտերնետը"</string>
@@ -87,6 +87,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"Չհաջողվեց միանալ բջջային ցանցին"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Փորձեք այլ ցանցի միանալ: Հպեք՝ նախընտրած ցանցը փոխելու համար:"</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Շտապ կանչերը հասանելի չեն"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"Շտապ կանչերի համար բջջային ցանց է անհրաժեշտ"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"Ծանուցումներ"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"Զանգի վերահասցեավորում"</string>
@@ -1778,6 +1780,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Մեկ ձեռքի ռեժիմ"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Հավելյալ խամրեցում"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Լսողական սարքեր"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Ձայնի կարգավորման կոճակները սեղմվեցին։ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ծառայությունը միացավ։"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Ձայնի կարգավորման կոճակները սեղմվեցին։ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ծառայությունն անջատվեց։"</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Բաց թողեք ձայնի ուժգնության կոճակները։ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ծառայությունը միացնելու համար սեղմեք և 3 վայրկյան պահեք ձայնի ուժգնության երկու կոճակը։"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 63bc9cf..3c0deb7 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -87,6 +87,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"Tidak dapat menjangkau jaringan seluler"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Coba ubah jaringan pilihan. Ketuk untuk mengubah."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Panggilan darurat tidak tersedia"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"Panggilan darurat memerlukan jaringan seluler"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"Notifikasi"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"Penerusan panggilan"</string>
@@ -1778,6 +1780,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Mode satu tangan"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Ekstra redup"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Alat bantu dengar"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Tombol volume ditahan. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> diaktifkan."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Tombol volume ditahan. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> dinonaktifkan."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Lepaskan tombol volume. Untuk mengaktifkan <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, tekan kedua tombol volume lagi selama 3 detik."</string>
diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml
index 1b43099..6a35191 100644
--- a/core/res/res/values-is/strings.xml
+++ b/core/res/res/values-is/strings.xml
@@ -87,6 +87,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"Ekki næst samband við farsímakerfi"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Prófaðu að velja annað símkerfi. Ýttu til að breyta."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Neyðarsímtöl eru ekki í boði"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"Neyðarsímtöl krefjast farsímakerfis"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"Tilkynningar"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"Símtalsflutningur"</string>
@@ -1778,6 +1780,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Einhent stilling"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Mjög dökkt"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Heyrnartæki"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Hljóðstyrkstökkum haldið inni. Kveikt á <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Hljóðstyrkstökkum haldið inni. Slökkt á <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Slepptu hljóðstyrkstökkunum. Til að kveikja á <xliff:g id="SERVICE_NAME">%1$s</xliff:g> skaltu halda báðum hljóðstyrkstökkunum aftur inni í 3 sekúndur."</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 9fd7913..db402c5 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -72,9 +72,9 @@
     <string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"ID chiamante generalmente limitato. Prossima chiamata: non limitato"</string>
     <string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"ID chiamante generalmente non limitato. Prossima chiamata: limitato"</string>
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"ID chiamante generalmente non limitato. Prossima chiamata: non limitato"</string>
-    <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Questa app non è compatibile con 16 kB. Controllo allineamento APK non riuscito. Questa app verrà eseguita utilizzando la modalità compatibile con le dimensioni della pagina. Per la massima compatibilità, ricompila l\'applicazione con il supporto a 16 kB. Per maggiori dettagli, consulta &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
-    <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Questa app non è compatibile con 16 kB. Controllo allineamento ELF non riuscito. Questa app verrà eseguita utilizzando la modalità compatibile con le dimensioni della pagina. Per la massima compatibilità, ricompila l\'applicazione con il supporto a 16 kB. Per maggiori dettagli, consulta &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
-    <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Questa app non è compatibile con 16 kB. Controlli di allineamento APK ed ELF non riusciti. Questa app verrà eseguita utilizzando la modalità compatibile con le dimensioni della pagina. Per la massima compatibilità, ricompila l\'applicazione con il supporto a 16 kB. Per maggiori dettagli, consulta &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+    <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Questa app non è compatibile con le pagine di 16 kB. Controllo allineamento APK non riuscito. Questa app verrà eseguita utilizzando la modalità compatibile con le dimensioni della pagina. Per la massima compatibilità, ricompila l\'applicazione con il supporto a 16 kB. Per maggiori dettagli, consulta &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+    <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Questa app non è compatibile con le pagine di 16 kB. Controllo allineamento ELF non riuscito. Questa app verrà eseguita utilizzando la modalità compatibile con le dimensioni della pagina. Per la massima compatibilità, ricompila l\'applicazione con il supporto a 16 kB. Per maggiori dettagli, consulta &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+    <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Questa app non è compatibile con le pagine di 16 kB. Controlli di allineamento APK ed ELF non riusciti. Questa app verrà eseguita utilizzando la modalità compatibile con le dimensioni della pagina. Per la massima compatibilità, ricompila l\'applicazione con il supporto a 16 kB. Per maggiori dettagli, consulta &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"Servizio non fornito."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"Non è possibile modificare l\'impostazione ID chiamante."</string>
     <string name="auto_data_switch_title" msgid="3286350716870518297">"I dati sono stati trasferiti a <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -88,6 +88,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"Impossibile raggiungere la rete mobile"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Prova a cambiare la rete preferita. Tocca per cambiare."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Chiamate di emergenza non disponibili"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"Per le chiamate di emergenza è necessaria una rete mobile"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"Avvisi"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"Deviazione chiamate"</string>
@@ -1779,6 +1781,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Modalità a una mano"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Attenuazione extra"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Protesi uditive"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Tieni premuti i tasti del volume. Servizio <xliff:g id="SERVICE_NAME">%1$s</xliff:g> attivato."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Tieni premuti i tasti del volume. Servizio <xliff:g id="SERVICE_NAME">%1$s</xliff:g> disattivato."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Rilascia i tasti del volume. Per attivare <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, tieni di nuovo premuti entrambi i tasti del volume per 3 secondi."</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 3a5c63c..503eb974 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -72,9 +72,9 @@
     <string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"שירות השיחה המזוהה עובר כברירת מחדל למצב מוגבל. השיחה הבאה: לא מוגבלת"</string>
     <string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"שירות \'שיחה מזוהה\' עובר כברירת מחדל למצב לא מוגבל. השיחה הבאה: מוגבלת"</string>
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"זיהוי מתקשר עובר כברירת מחדל למצב לא מוגבל. השיחה הבאה: לא מוגבלת"</string>
-    <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"‏האפליקציה הזו לא תואמת לדפים בגודל 16KB. בדיקות ההתאמה ל-APK נכשלה. האפליקציה הזו תופעל במצב תואם לגודל הדף. כדי לקבל את התאימות הטובה ביותר, צריך להדר מחדש (recompile) את האפליקציה לתמיכה בדפים בגודל 16KB. מידע נוסף זמין בכתובת &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
-    <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"‏האפליקציה הזו לא תואמת לדפים בגודל 16KB. בדיקות ההתאמה ל-ELF נכשלה. האפליקציה הזו תופעל במצב תואם לגודל הדף. כדי לקבל את התאימות הטובה ביותר, צריך להדר מחדש (recompile) את האפליקציה לתמיכה בדפים בגודל 16KB. מידע נוסף זמין בכתובת &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
-    <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"‏האפליקציה הזו לא תואמת לדפים בגודל 16KB. בדיקות ההתאמה ל-APK ול-ELF נכשלו. האפליקציה הזו תופעל במצב תואם לגודל הדף. כדי לקבל את התאימות הטובה ביותר, צריך להדר מחדש (recompile) את האפליקציה לתמיכה בדפים בגודל 16KB. מידע נוסף זמין בכתובת &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+    <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"‏האפליקציה הזו לא תואמת לגודל דף של 16KB. בדיקות ההתאמה ל-APK נכשלה. האפליקציה הזו תופעל במצב תואם לגודל הדף. כדי לקבל את התאימות הטובה ביותר, צריך לקמפל את האפליקציה מחדש ולדאוג שהיא תומכת בגודל דף של 16KB. מידע נוסף זמין בכתובת &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+    <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"‏האפליקציה הזו לא תואמת לגודל דף של 16KB. בדיקות ההתאמה ל-ELF נכשלה. האפליקציה הזו תופעל במצב תואם לגודל הדף. כדי לקבל את התאימות הטובה ביותר, צריך לקמפל את האפליקציה מחדש ולדאוג שהיא תומכת בגודל דף של 16KB. מידע נוסף זמין בכתובת &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+    <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"‏האפליקציה הזו לא תואמת לגודל דף של 16KB. בדיקות ההתאמה ל-APK ול-ELF נכשלו. האפליקציה הזו תופעל במצב תואם לגודל הדף. כדי לקבל את התאימות הטובה ביותר, צריך לקמפל את האפליקציה מחדש ולדאוג שהיא תומכת בגודל דף של 16KB. מידע נוסף זמין בכתובת &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"השירות לא הוקצה."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"אינך יכול לשנות את הגדרת זיהוי המתקשר."</string>
     <string name="auto_data_switch_title" msgid="3286350716870518297">"הנתונים עברו אל <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -88,6 +88,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"לא ניתן להתחבר לרשת הסלולרית"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"אפשר לנסות לשנות את הרשת המועדפת. יש להקיש כדי לשנות אותה."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"שיחות חירום לא זמינות"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"כדי לבצע שיחות חירום, צריך להתחבר לרשת סלולרית"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"התראות"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"העברת שיחות"</string>
@@ -1779,6 +1781,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"מצב שימוש ביד אחת"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"מעומעם במיוחד"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"מכשירי שמיעה"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"לחצני עוצמת הקול נלחצו בלחיצה ארוכה. שירות <xliff:g id="SERVICE_NAME">%1$s</xliff:g> הופעל."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"לחצני עוצמת הקול נלחצו בלחיצה ארוכה. שירות <xliff:g id="SERVICE_NAME">%1$s</xliff:g> הושבת."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"אפשר לשחרר את מקש עוצמת הקול. כדי להפעיל את השירות <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, צריך ללחוץ לחיצה ארוכה על שני המקשים של עוצמת הקול שוב במשך 3 שניות."</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 3d7c91c..05adb2b 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -73,7 +73,7 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"既定: 発信者番号通知、次の発信: 通知"</string>
     <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"このアプリは 16 KB アライメントではありません。APK のアライメント チェックが失敗しました。このアプリはページサイズ互換モードを使用して実行されます。最適な互換性を実現するには、16 KB をサポートするようにアプリケーションを再コンパイルしてください。詳しくは、&lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; をご覧ください。"</string>
     <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"このアプリは 16 KB アライメントではありません。ELF のアライメント チェックに失敗しました。このアプリはページサイズ互換モードを使用して実行されます。最適な互換性を実現するには、16 KB をサポートするようにアプリケーションを再コンパイルしてください。詳しくは、&lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; をご覧ください。"</string>
-    <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"このアプリは 16 KB アライメントではありません。APK と ELF のアライメント チェックが失敗しました。このアプリはページサイズ互換モードを使用して実行されます。最適な互換性を実現するには、16 KB をサポートするようにアプリケーションを再コンパイルしてください。詳しくは、&lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; をご覧ください。"</string>
+    <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"このアプリは 16 KB アライメントではありません。APK と ELF のアライメント チェックに失敗しました。このアプリはページサイズ互換モードを使用して実行されます。最適な互換性を実現するには、16 KB をサポートするようにアプリケーションを再コンパイルしてください。詳しくは、&lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; をご覧ください。"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"提供可能なサービスがありません。"</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"発信者番号の設定は変更できません。"</string>
     <string name="auto_data_switch_title" msgid="3286350716870518297">"データが <xliff:g id="CARRIERDISPLAY">%s</xliff:g> に切り替わりました"</string>
@@ -87,6 +87,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"モバイル ネットワークにアクセスできません"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"タップして、優先ネットワークを変更してください。"</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"緊急通報は利用できません"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"緊急通報にはモバイル ネットワークが必要です"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"通知"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"電話の転送"</string>
@@ -1778,6 +1780,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"片手モード"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"さらに輝度を下げる"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"補聴器"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"音量ボタンを長押ししました。<xliff:g id="SERVICE_NAME">%1$s</xliff:g> が ON になりました。"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"音量ボタンを長押ししました。<xliff:g id="SERVICE_NAME">%1$s</xliff:g> が OFF になりました。"</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"音量ボタンを離してください。<xliff:g id="SERVICE_NAME">%1$s</xliff:g> を有効にするには音量大と音量小の両方のボタンを 3 秒ほど長押ししてください。"</string>
diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml
index 10cb0c3..552f52e 100644
--- a/core/res/res/values-ka/strings.xml
+++ b/core/res/res/values-ka/strings.xml
@@ -87,6 +87,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"მობილურ ქსელთან დაკავშირება ვერ ხერხდება"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"ცადეთ უპირატესი ქსელის შეცვლა. შეეხეთ შესაცვლელად."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"გადაუდებელი ზარი მიუწვდომელია"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"გადაუდებელი ზარები საჭიროებს მობილურ ქსელს"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"გაფრთხილებები"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"ზარის გადამისამართება"</string>
@@ -1778,6 +1780,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"ცალი ხელის რეჟიმი"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"დამატებითი დაბინდვა"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"სმენის აპარატები"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"ხანგრძლივად დააჭირეთ ხმის ღილაკებს. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ჩართულია."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"ხანგრძლივად დააჭირეთ ხმის ღილაკებს. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> გამორთულია."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"ხელი აუშვით ხმის ღილაკებს. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>-ის ჩასართველად, ხელმეორედ ხანგრძლივად დააჭირეთ ორივე ხმის ღილაკს 3 წამის განმავლობაში."</string>
diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml
index 49b4bfc..3c118ac 100644
--- a/core/res/res/values-kk/strings.xml
+++ b/core/res/res/values-kk/strings.xml
@@ -87,6 +87,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"Мобильдік желіге қосылу мүмкін емес"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Таңдаулы желіні өзгертіп көріңіз. Өзгерту үшін түртіңіз."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Жедел қызметке қоңырау шалу мүмкін емес"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"Құтқару қызметіне қоңырау шалу үшін мобильдік желі қажет."</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"Дабылдар"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"Қоңырауды басқа нөмірге бағыттау"</string>
@@ -1778,6 +1780,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Бір қолмен басқару режимі"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Экранды қарайту"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Есту аппараттары"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Пайдаланушы дыбыс деңгейі пернелерін басып ұстап тұрды. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> қосулы."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Дыбыс деңгейі пернелерін басып тұрған соң, <xliff:g id="SERVICE_NAME">%1$s</xliff:g> өшірілді."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Дыбыс деңгейі пернелерін жіберіңіз. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> қызметін қосу үшін дыбыс деңгейі пернесінің екеуін де қайтадан 3 секундқа басып тұрыңыз."</string>
diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml
index 2db0fcd..737fe88 100644
--- a/core/res/res/values-km/strings.xml
+++ b/core/res/res/values-km/strings.xml
@@ -87,6 +87,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"មិន​អាច​ភ្ជាប់​បណ្ដាញ​ទូរសព្ទ​ចល័តបានទេ"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"សាកល្បង​ប្ដូរ​ទៅបណ្ដាញ​ដែល​ចង់ប្រើ។ សូមចុច​ដើម្បីផ្លាស់​ប្ដូរ។"</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"មិន​អាច​ប្រើ​ការ​ហៅ​បន្ទាន់​បានទេ"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"ការហៅទៅលេខសង្គ្រោះបន្ទាន់តម្រូវឱ្យមានបណ្ដាញ​ទូរសព្ទ​ចល័ត"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"ការជូនដំណឹង"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"ការបញ្ជូន​ការហៅ​ទូរសព្ទ​បន្ត"</string>
@@ -1778,6 +1780,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"មុខងារប្រើដៃម្ខាង"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"ងងឹតខ្លាំង"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"ឧបករណ៍ជំនួយការស្ដាប់"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"បានសង្កត់​គ្រាប់ចុច​កម្រិតសំឡេង​ជាប់។ បាន​បើក <xliff:g id="SERVICE_NAME">%1$s</xliff:g>។"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"បានសង្កត់​គ្រាប់ចុច​កម្រិតសំឡេង​ជាប់។ បាន​បិទ <xliff:g id="SERVICE_NAME">%1$s</xliff:g>។"</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"លែង​គ្រាប់ចុចកម្រិតសំឡេង។ ដើម្បីបើក <xliff:g id="SERVICE_NAME">%1$s</xliff:g> សូមចុច​គ្រាប់ចុចកម្រិតសំឡេងទាំងពីរឱ្យជាប់ម្ដងទៀត​រយៈពេល 3 វិនាទី។"</string>
diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml
index ad62f42..87a0bef 100644
--- a/core/res/res/values-kn/strings.xml
+++ b/core/res/res/values-kn/strings.xml
@@ -87,6 +87,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"ಮೊಬೈಲ್‌ ನೆಟ್‌ವರ್ಕ್ ತಲುಪಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"ಆದ್ಯತೆಗೊಳಿಸಿದ ನೆಟ್‌ವರ್ಕ್‌ಗಳನ್ನು ಬದಲಾಯಿಸಲು ಪ್ರಯತ್ನಿಸಿ. ಬದಲಾಯಿಸಲು ಟ್ಯಾಪ್‌ ಮಾಡಿ."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"ತುರ್ತು ಕರೆ ಮಾಡುವಿಕೆ ಲಭ್ಯವಿಲ್ಲ"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"ತುರ್ತು ಕರೆಗಳಿಗೆ ಮೊಬೈಲ್ ನೆಟ್‌ವರ್ಕ್‌ನ ಅಗತ್ಯವಿದೆ"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"ಎಚ್ಚರಿಕೆಗಳು"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"ಕರೆ ಫಾರ್ವರ್ಡ್‌ ಮಾಡುವಿಕೆ"</string>
@@ -1778,6 +1780,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"ಒಂದು ಕೈ ಮೋಡ್‌"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"ಇನ್ನಷ್ಟು ಮಬ್ಬು"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"ಶ್ರವಣ ಸಾಧನಗಳು"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"ವಾಲ್ಯೂಮ್ ಕೀಗಳನ್ನು ಹಿಡಿದುಕೊಳ್ಳಿ. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ಅನ್ನು ಆನ್ ಮಾಡಲಾಗಿದೆ."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"ವಾಲ್ಯೂಮ್ ಕೀಗಳನ್ನು ಹಿಡಿದಿಟ್ಟುಕೊಳ್ಳಲಾಗಿದೆ. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, ಆಫ್ ಮಾಡಲಾಗಿದೆ."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"ವಾಲ್ಯೂಮ್ ಕೀಗಳನ್ನು ಬಿಡುಗಡೆ ಮಾಡಿ. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ಅನ್ನು ಆನ್ ಮಾಡಲು, ಎರಡೂ ವಾಲ್ಯೂಮ್ ಕೀಗಳನ್ನು ಮತ್ತೊಮ್ಮೆ 3 ಸೆಕೆಂಡ್‌ಗಳ ಕಾಲ ಒತ್ತಿ ಹಿಡಿದುಕೊಳ್ಳಿ."</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 38e5bb5..51921be 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -87,6 +87,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"모바일 네트워크에 연결할 수 없습니다."</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"기본 네트워크를 변경해 보세요. 탭하여 변경할 수 있습니다."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"긴급 전화를 사용할 수 없습니다."</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"긴급 전화를 걸려면 모바일 네트워크 연결이 필요함"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"알림"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"착신전환"</string>
@@ -1778,6 +1780,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"한 손 모드"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"더 어둡게"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"청각 보조 기기"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"볼륨 키를 길게 눌렀습니다. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>이(가) 사용 설정되었습니다."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"볼륨 키를 길게 눌렀습니다. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>이(가) 사용 중지되었습니다."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"볼륨 키에서 손을 뗍니다. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>을 켜려면 볼륨 키 2개를 3초 동안 길게 누르세요."</string>
diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml
index 60e2749..7ca2092 100644
--- a/core/res/res/values-ky/strings.xml
+++ b/core/res/res/values-ky/strings.xml
@@ -73,7 +73,7 @@
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Номурду аныктоонун демейки абалы \"чектелбейт\" деп коюлган. Кийинки чалуу: Чектелбейт"</string>
     <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Бул колдонмо 16 Кб өлчөмүнө туура келбейт. APK\'дин тегизделиши тейшерилбей калды. Колдонмо беттин өлчөмүнө туура келген режимде иштейт. Эң жакшы шайкештик үчүн колдонмону 16 Кб колдоосу менен кайра түзүңүз. Кеңири маалымат: &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
     <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Бул колдонмо 16 Кб өлчөмүнө туура келбейт. ELF\'тин тегизделиши тейшерилбей калды. Колдонмо беттин өлчөмүнө туура келген режимде иштейт. Эң жакшы шайкештик үчүн колдонмону 16 Кб колдоосу менен кайра түзүңүз. Кеңири маалымат: &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
-    <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Бул колдонмо 16 Кб өлчөмүнө туура келбейт. APK менен ELF\'тин тегизделиши тейшерилбей калды. Колдонмо беттин өлчөмүнө туура келген режимде иштейт. Эң жакшы шайкештик үчүн колдонмону 16 Кб колдоосу менен кайра түзүңүз. Кеңири маалымат: &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+    <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Бул колдонмо 16 Кб өлчөмүнө туура келбейт. APK менен ELF\'тин тегизделиши текшерилбей калды. Колдонмо беттин өлчөмүнө туура келген режимде иштейт. Эң жакшы шайкештик үчүн колдонмону 16 Кб колдоосу менен кайра түзүңүз. Кеңири маалымат: &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"Кызмат камсыздалган эмес."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"Чалуучунун далдаштырма дайындары параметрлерин өзгөртө албайсыз."</string>
     <string name="auto_data_switch_title" msgid="3286350716870518297">"Мобилдик Интернет <xliff:g id="CARRIERDISPLAY">%s</xliff:g> которулду"</string>
@@ -87,6 +87,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"Мобилдик тармакка туташпай жатат"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Тандалган тармакты өзгөртүп көрүңүз. Өзгөртүү үчүн таптаңыз."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Шашылыш чалуу жеткиликсиз"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"Шашылыш чалуу үчүн мобилдик тармак талап кылынат"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"Шашылыш билдирүүлөр"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"Чалууну башка номерге багыттоо"</string>
@@ -1778,6 +1780,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Бир кол режими"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Кошумча караңгылатуу"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Угуу түзмөктөрү"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Үндү катуулатуу/акырындатуу баскычтары басылып, <xliff:g id="SERVICE_NAME">%1$s</xliff:g> күйгүзүлдү."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Үндү катуулатуу/акырындатуу баскычтары басылып, <xliff:g id="SERVICE_NAME">%1$s</xliff:g> өчүрүлдү."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Үн баскычтарын коё бериңиз. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> кызматын күйгүзүү үчүн үн баскычтарын кайра 3 секунд коё бербей басып туруңуз."</string>
diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml
index da2d1ad..32b0605 100644
--- a/core/res/res/values-lo/strings.xml
+++ b/core/res/res/values-lo/strings.xml
@@ -87,6 +87,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"ບໍ່ສາມາດຕິດຕໍ່ເຄືອຂ່າຍມືຖືໄດ້"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"ໃຫ້ລອງປ່ຽນເຄືອຂ່າຍທີ່ຕ້ອງການ. ແຕະເພື່ອປ່ຽນ."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"ບໍ່ສາມາດໃຊ້ການໂທສຸກເສີນໄດ້"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"ໂທ​ສຸກ​ເສີນຕ້ອງໃຊ້ເຄືອຂ່າຍມືຖື"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"ການເຕືອນ"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"ການໂອນສາຍ"</string>
@@ -1778,6 +1780,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"ໂໝດມືດຽວ"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"ຫຼຸດແສງເປັນພິເສດ"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"ອຸປະກອນຊ່ວຍຟັງ"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"ກົດປຸ່ມລະດັບສຽງຄ້າງໄວ້. ເປີດໃຊ້ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ແລ້ວ."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"ກົດປຸ່ມລະດັບສຽງຄ້າງໄວ້. ປິດ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ໄວ້ແລ້ວ."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"ປ່ອຍປຸ່ມລະດັບສຽງ. ເພື່ອເປີດ <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, ໃຫ້ກົດປຸ່ມລະດັບສຽງທັງສອງຄ້າງໄວ້ 3 ວິນາທີ."</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 5de9881..ff7919e 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -89,6 +89,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"Nepavyko pasiekti mobiliojo ryšio tinklo"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Pabandykite pakeisti pageidaujamą tinklą. Palieskite, kad pakeistumėte."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Skambučių pagalbos numeriu paslauga nepasiekiama"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"Kad būtų galima skambinti pagalbos numeriais, būtina naudoti mobiliojo ryšio tinklą"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"Įspėjimai"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"Skambučio peradresavimas"</string>
@@ -1780,6 +1782,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Vienos rankos režimas"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Itin blanku"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Klausos įrenginiai"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Laikomi garsumo klavišai. „<xliff:g id="SERVICE_NAME">%1$s</xliff:g>“ įjungta."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Laikomi garsumo klavišai. „<xliff:g id="SERVICE_NAME">%1$s</xliff:g>“ išjungta."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Atleiskite garsumo klavišus. Kad įjungtumėte „<xliff:g id="SERVICE_NAME">%1$s</xliff:g>“, paspauskite ir 3 sekundes palaikykite garsumo klavišus."</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index ac109a8..ab2c242 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -88,6 +88,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"Nevar sasniegt mobilo tīklu"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Mēģiniet nomainīt vēlamo tīklu. Pieskarieties, lai to mainītu."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Nav pieejami ārkārtas izsaukumi"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"Lai veiktu ārkārtas zvanus, ir nepieciešams mobilais tīkls."</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"Brīdinājumi"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"Zvanu pāradresācija"</string>
@@ -1779,6 +1781,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Vienas rokas režīms"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Papildu aptumšošana"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Dzirdes aparāti"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Turējāt nospiestas skaļuma pogas. Pakalpojums <xliff:g id="SERVICE_NAME">%1$s</xliff:g> tika ieslēgts."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Turējāt nospiestas skaļuma pogas. Pakalpojums <xliff:g id="SERVICE_NAME">%1$s</xliff:g> tika izslēgts."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Atlaidiet skaļuma pogas. Lai ieslēgtu pakalpojumu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, vēlreiz nospiediet un trīs sekundes turiet nospiestas abas skaļuma pogas."</string>
diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml
index b626194..c10b1ef 100644
--- a/core/res/res/values-mk/strings.xml
+++ b/core/res/res/values-mk/strings.xml
@@ -71,9 +71,9 @@
     <string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"Стандардно, ID на повикувач е скриен. Следен повик: не е скриен"</string>
     <string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"Стандардно, ID на повикувач не е скриен. Следен повик: скриен"</string>
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"Стандардно, ID на повикувач не е скриен. Следен повик: не е скриен"</string>
-    <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Апликацијава не е компатибилна со 16 KB. Проверката за усогласување на АПК не успеа. Апликацијава ќе се извршува со режим компатибилен со големината на страницата. За најдобра компатибилност, рекомпилирајте ја апликацијата со поддршка за 16 KB. За повеќе информации, одете на &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+    <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Апликацијава не е компатибилна со 16 KB. Проверката за усогласување на APK не успеа. Апликацијава ќе се извршува со режим компатибилен со големината на страницата. За најдобра компатибилност, рекомпилирајте ја апликацијата со поддршка за 16 KB. За повеќе информации, одете на &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
     <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Апликацијава не е компатибилна со 16 KB. Проверката за усогласување на ELF не успеа. Апликацијава ќе се извршува со режим компатибилен со големината на страницата. За најдобра компатибилност, рекомпилирајте ја апликацијата со поддршка за 16 KB. За повеќе информации, одете на &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
-    <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Апликацијава не е компатибилна со 16 KB. Проверките за усогласување на АПК и ELF се неуспешни. Апликацијава ќе се извршува со режим компатибилен со големината на страницата. За најдобра компатибилност, рекомпилирајте ја апликацијата со поддршка за 16 KB. За повеќе информации, одете на &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+    <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Апликацијава не е компатибилна со 16 KB. Проверките за усогласување на APK и ELF се неуспешни. Апликацијава ќе се извршува со режим компатибилен со големината на страницата. За најдобра компатибилност, рекомпилирајте ја апликацијата со поддршка за 16 KB. За повеќе информации, одете на &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"Услугата не е предвидена."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"Не може да го промените поставувањето за ID на повикувач."</string>
     <string name="auto_data_switch_title" msgid="3286350716870518297">"Мобилниот интернет се префрли на <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -87,6 +87,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"Мобилната мрежа е недостапна"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Сменете ја претпочитаната мрежа. Допрете за промена."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Итните повици се недостапни"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"За итните повици е потребна мобилна мрежа"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"Предупредувања"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"Проследување повик"</string>
@@ -1778,6 +1780,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Режим со една рака"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Дополнително затемнување"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Слушни помагала"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Ги задржавте копчињата за јачина на звук. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> е вклучена."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Ги задржавте копчињата за јачина на звук. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> е исклучена."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Ослободете ги копчињата за јачина на звукот. Притиснете ги и задржете ги двете копчиња за јачина на звукот во траење од 3 секунди за да вклучите <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml
index 7f8027d..3eef4930 100644
--- a/core/res/res/values-ml/strings.xml
+++ b/core/res/res/values-ml/strings.xml
@@ -87,6 +87,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"മൊബൈൽ നെറ്റ്‌വർക്കിലേക്ക് കണക്റ്റ് ചെയ്യാനാവുന്നില്ല"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"തിര‌ഞ്ഞെടുത്ത നെറ്റ്‌വർക്ക് മാറ്റുന്നത് പരീക്ഷിക്കുക. മാറ്റാൻ ടാപ്പ് ചെയ്യുക."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"എമർജൻസി കോളിംഗ് ലഭ്യമല്ല"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"എമർജൻസി കോളുകൾ ചെയ്യാൻ ഒരു മൊബൈൽ നെറ്റ്‌വർക്ക് ആവശ്യമാണ്"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"അലേർട്ടുകൾ"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"കോൾ ഫോർവേഡിംഗ്"</string>
@@ -1778,6 +1780,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"ഒറ്റക്കൈ മോഡ്"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"കൂടുതൽ ഡിം ചെയ്യൽ"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"ശ്രവണ ഉപകരണങ്ങൾ"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"വോളിയം കീകൾ പിടിച്ചു. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ഓണാക്കി."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"വോളിയം കീകൾ അമർത്തിപ്പിടിച്ചു. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ഓഫാക്കി."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"വോളിയം കീകൾ വിടുക. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ഓണാക്കാൻ, രണ്ട് വോളിയം കീകളും വീണ്ടും മൂന്ന് സെക്കൻഡ് അമർത്തിപ്പിടിക്കുക."</string>
diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml
index 8043bae..f81cff8 100644
--- a/core/res/res/values-mn/strings.xml
+++ b/core/res/res/values-mn/strings.xml
@@ -87,6 +87,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"Хөдөлгөөнт холбооны сүлжээнд холбогдох боломжгүй байна"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Сонгосон сүлжээг өөрчлөхөөр оролдоно уу. Өөрчлөхийн тулд товшино уу."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Яаралтай дуудлага хийх боломжгүй"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"Яаралтай дуудлагуудад хөдөлгөөнт холбооны сүлжээ шаардлагатай"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"Сануулга"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"Дуудлага шилжүүлэх"</string>
@@ -1778,6 +1780,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Нэг гарын горим"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Хэт бүүдгэр"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Сонсголын төхөөрөмжүүд"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Дууны түвшний түлхүүрийг удаан дарсан. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>-г асаалаа."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Дууны түвшний түлхүүрийг удаан дарсан. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>-г унтраалаа."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Дууны түвшний товчнуудыг суллана уу. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>-г асаахын тулд дууны түвшний 2 товчийг зэрэг 3 секундийн турш удаан дарна уу."</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 7bdd942..98fedef 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -87,6 +87,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"मोबाइल नेटवर्क उपलब्ध नाही"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"प्राधान्य दिलेले नेटवर्क बदलण्याचा प्रयत्न करा. बदलण्यासाठी टॅप करा."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"आणीबाणी कॉलिंग अनुपलब्ध"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"आणीबाणी कॉलसाठी मोबाइल नेटवर्क आवश्यक आहे"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"अलर्ट"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"कॉल फॉरवर्डिंग"</string>
@@ -1778,6 +1780,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"एकहाती मोड"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"आणखी डिम"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"श्रवणयंत्रे"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"धरून ठेवलेल्या व्हॉल्यूम की. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> सुरू केला आहे."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"धरून ठेवलेल्या व्हॉल्यूम की. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> बंद केले आहे."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"व्हॉल्यूम की रिलीझ करा. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> सुरू करण्यासाठी, दोन्ही व्हॉल्यूम की पुन्हा प्रेस करा आणि तीन सेकंदांसाठी धरून ठेवा."</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 21925f8..2ce54d0 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -87,6 +87,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"Tidak dapat mencapai rangkaian mudah alih"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Cuba tukar rangkaian pilihan. Ketik untuk menukar."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Panggilan kecemasan tidak tersedia"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"Rangkaian mudah alih diperlukan untuk membuat panggilan kecemasan"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"Makluman"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"Pemajuan panggilan"</string>
@@ -1778,6 +1780,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Mod sebelah tangan"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Amat malap"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Peranti pendengaran"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Kekunci kelantangan ditahan. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> dihidupkan."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Kekunci kelantangan ditahan. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> dimatikan."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Lepaskan kekunci kelantangan. Untuk menghidupkan <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, sila tekan dan tahan kedua-dua kekunci kelantangan sekali lagi selama 3 saat."</string>
diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml
index 61af771..3e0187a 100644
--- a/core/res/res/values-my/strings.xml
+++ b/core/res/res/values-my/strings.xml
@@ -72,8 +72,8 @@
     <string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"ပုံသေအားဖြင့် ခေါ်ဆိုသူအိုင်ဒီ(Caller ID)အား ကန့်သတ်မထားပါ။ နောက်ထပ်အဝင်ခေါ်ဆိုမှု-ကန့်သတ်ထားသည်။"</string>
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"ပုံသေအားဖြင့် ခေါ်ဆိုသူအိုင်ဒီ(Caller ID)အား ကန့်သတ်မထားပါ။ နောက်ထပ်အဝင်ခေါ်ဆိုမှု-ကန့်သတ်မထားပါ။"</string>
     <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"ဤအက်ပ်သည် ၁၆ KB နှင့် တွဲမသုံးနိုင်ပါ။ APK ချိန်ညှိခြင်း စစ်ဆေးမှု မအောင်မြင်ပါ။ ဤအက်ပ်သည် တွဲသုံးနိုင်သော စာမျက်နှာအရွယ်အစားမုဒ်သုံး၍ လုပ်ဆောင်ပါမည်။ အကောင်းဆုံး တွဲသုံးနိုင်မှုအတွက် အပလီကေးရှင်းကို ၁၆ KB ပံ့ပိုးမှုဖြင့် ပြန်လည်တည်ဆောက်ပါ။ နောက်ထပ်အချက်အလက်အတွက် &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; တွင် ကြည့်ပါ"</string>
-    <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"ဤအက်ပ်သည် ၁၆ KB နှင့် တွဲမသုံးနိုင်ပါ။ ELF ချိန်ညှိခြင်း စစ်ဆေးမှု မအောင်မြင်ပါ။ ဤအက်ပ်သည် တွဲသုံးနိုင်သော စာမျက်နှာအရွယ်အစားမုဒ်သုံး၍ လုပ်ဆောင်ပါမည်။ အကောင်းဆုံး တွဲသုံးနိုင်မှုအတွက် အပလီကေးရှင်းကို ၁၆ KB ပံ့ပိုးမှုဖြင့် ပြန်လည်တည်ဆောက်ပါ။ နောက်ထပ်အချက်အလက်အတွက် &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; တွင် ကြည့်ပါ"</string>
-    <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"ဤအက်ပ်သည် ၁၆ KB နှင့် တွဲမသုံးနိုင်ပါ။ APK နှင့် ELF ချိန်ညှိခြင်း စစ်ဆေးမှု မအောင်မြင်ပါ။ ဤအက်ပ်သည် တွဲသုံးနိုင်သော စာမျက်နှာအရွယ်အစားမုဒ်သုံး၍ လုပ်ဆောင်ပါမည်။ အကောင်းဆုံး တွဲသုံးနိုင်မှုအတွက် အပလီကေးရှင်းကို ၁၆ KB ပံ့ပိုးမှုဖြင့် ပြန်လည်တည်ဆောက်ပါ။ နောက်ထပ်အချက်အလက်အတွက် &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; တွင် ကြည့်ပါ"</string>
+    <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"ဤအက်ပ်ကို ၁၆ KB နှင့် တွဲမသုံးနိုင်ပါ။ ELF ချိန်ညှိခြင်း စစ်ဆေးမှု မအောင်မြင်ပါ။ ဤအက်ပ်ကို တွဲသုံးနိုင်သော စာမျက်နှာအရွယ်အစားမုဒ်သုံး၍ လုပ်ဆောင်ပါမည်။ အကောင်းဆုံး တွဲသုံးနိုင်မှုအတွက် အပလီကေးရှင်းကို ၁၆ KB ပံ့ပိုးမှုဖြင့် ပြန်လည်တည်ဆောက်ပါ။ နောက်ထပ်အချက်အလက်အတွက် &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; တွင် ကြည့်ပါ"</string>
+    <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"ဤအက်ပ်ကို ၁၆ KB နှင့် တွဲမသုံးနိုင်ပါ။ APK နှင့် ELF ချိန်ညှိခြင်း စစ်ဆေးမှု မအောင်မြင်ပါ။ ဤအက်ပ်ကို တွဲသုံးနိုင်သော စာမျက်နှာအရွယ်အစားမုဒ်သုံး၍ လုပ်ဆောင်ပါမည်။ အကောင်းဆုံး တွဲသုံးနိုင်မှုအတွက် အပလီကေးရှင်းကို ၁၆ KB ပံ့ပိုးမှုဖြင့် ပြန်လည်တည်ဆောက်ပါ။ နောက်ထပ်အချက်အလက်အတွက် &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; တွင် ကြည့်ပါ"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"ဝန်ဆောင်မှုအား ကန့်သတ်မထားပါ"</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"သင်သည် ခေါ်ဆိုသူ ID ဆက်တင်ကို မပြောင်းလဲနိုင်ပါ။"</string>
     <string name="auto_data_switch_title" msgid="3286350716870518297">"ဒေတာကို <xliff:g id="CARRIERDISPLAY">%s</xliff:g> သို့ ပြောင်းထားသည်"</string>
@@ -87,6 +87,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"မိုဘိုင်းကွန်ရက် လိုင်းမရပါ"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"ဦးစားပေးကွန်ရက်သို့ ပြောင်းကြည့်ပါ။ ပြောင်းရန် တို့ပါ။"</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"အရေးပေါ်ခေါ်ဆိုမှု မရနိုင်ပါ"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"အရေးပေါ်ဖုန်းခေါ်ရန် မိုဘိုင်းကွန်ရက် လိုအပ်သည်"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"သတိပေးချက်များ"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"အဝင်ခေါ်ဆိုမှုအား ထပ်ဆင့်ပို့ခြင်း"</string>
@@ -1778,6 +1780,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"လက်တစ်ဖက်သုံးမုဒ်"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"ပိုမှိန်ခြင်း"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"နားကြားကိရိယာ"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"အသံခလုတ်များကို ဖိထားသည်။ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ဖွင့်လိုက်သည်။"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"အသံခလုတ်များကို ဖိထားသည်။ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ပိတ်လိုက်သည်။"</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"အသံထိန်းခလုတ်များကို လွှတ်လိုက်ပါ။ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ဖွင့်ရန် အသံထိန်းခလုတ်နှစ်ခုစလုံးကို ၃ စက္ကန့်ကြာအောင် ထပ်နှိပ်ပါ။"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 0c7d868..d21296d 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -87,6 +87,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"Får ikke kontakt med mobilnettverket"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Prøv å endre foretrukket nettverk. Trykk for å endre."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Nødanrop er utilgjengelig"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"Du må være koblet til et mobilnettverk for å utføre nødanrop"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"Varsler"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"Viderekobling"</string>
@@ -1778,6 +1780,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Enhåndsmodus"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Ekstra dimmet"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Høreapparater"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Volumtastene holdes inne. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> er slått på."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Volumtastene holdes inne. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> er slått av."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Slipp opp volumtastene. For å slå på <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, trykk og hold på begge volumtastene igjen i 3 sekunder."</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 3414644..c524a48 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -87,6 +87,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"मोबाइल नेटवर्कमाथि पहुँच राख्न सकिएन"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"रुचाइएको नेटवर्क परिवर्तन गरी हेर्नुहोस्‌। परिवर्तन गर्न ट्याप गर्नुहोस्‌।"</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"आपत्‌कालीन कल सेवा अनुपलब्ध छ"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"आपत्‌कालीन कलहरू गर्न मोबाइल नेटवर्क चाहिन्छ"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"अलर्टहरू"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"कल फर्वार्ड गर्ने सेवा"</string>
@@ -1778,6 +1780,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"एक हाते मोड"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"अझै मधुरो"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"हियरिङ डिभाइसहरू"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"तपाईंले भोल्युम बटनहरू थिचिराख्नुभयो। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> अन भयो।"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"तपाईंले भोल्युम बटनहरू थिचिराख्नुभयो। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> अफ भयो।"</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"भोल्युम बटनहरू थिच्न छाड्नुहोस्। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> अन गर्न दुवै भोल्युम बटन फेरि ३ सेकेन्डसम्म थिचिराख्नुहोस्।"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index e88384b..609d642 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -87,6 +87,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"Kan mobiel netwerk niet bereiken"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Probeer een ander voorkeursnetwerk. Tik om te wijzigen."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Noodoproepen niet beschikbaar"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"Voor noodoproepen is een mobiel netwerk vereist"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"Meldingen"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"Gesprek doorschakelen"</string>
@@ -1778,6 +1780,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Bediening met 1 hand"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Extra dimmen"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Hoortoestellen"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Volumetoetsen ingedrukt gehouden. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> staat aan."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Volumetoetsen ingedrukt gehouden. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> staat uit."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Laat de volumeknoppen los. Als je <xliff:g id="SERVICE_NAME">%1$s</xliff:g> wilt aanzetten, houd je beide volumeknoppen weer 3 seconden ingedrukt."</string>
diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml
index 9960691..51c4e31 100644
--- a/core/res/res/values-or/strings.xml
+++ b/core/res/res/values-or/strings.xml
@@ -71,9 +71,9 @@
     <string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"କଲର୍ ଆଇଡି ଡିଫଲ୍ଟ ଭାବରେ ପ୍ରତିବନ୍ଧିତ। ପରବର୍ତ୍ତୀ କଲ୍: ପ୍ରତିବନ୍ଧିତ ନୁହେଁ"</string>
     <string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"କଲର୍ ଆଇଡି ଡିଫଲ୍ଟ ଭାବରେ ପ୍ରତିବନ୍ଧିତ ନୁହେଁ। ପରବର୍ତ୍ତୀ କଲ୍: ପ୍ରତିବନ୍ଧିତ"</string>
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"କଲର୍ ଆଇଡି ଡିଫଲ୍ଟ ଭାବରେ ପ୍ରତିବନ୍ଧିତ ନୁହେଁ। ପରବର୍ତ୍ତୀ କଲ୍: ପ୍ରତିବନ୍ଧିତ ନୁହେଁ"</string>
-    <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"ଏହି ଆପ 16 KB କମ୍ପାଟିବଲ ନୁହେଁ। APK ଆଲାଇନମେଣ୍ଟ ଯାଞ୍ଚ ବିଫଳ ହୋଇଛି। ପୃଷ୍ଠା ସାଇଜ କମ୍ପାଟିବଲ ମୋଡ ବ୍ୟବହାର କରି ଏହି ଆପକୁ ଚଲାଯିବ। ସର୍ବୋତ୍ତମ କମ୍ପାଟିବିଲିଟୀ ପାଇଁ ଦୟାକରି 16 KB ସପୋର୍ଟ ସହ ଆପ୍ଲିକେସନକୁ ପୁଣି କମ୍ପାଇଲ କରନ୍ତୁ। ଅଧିକ ସୂଚନା ପାଇଁ &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;କୁ ଦେଖନ୍ତୁ"</string>
-    <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"ଏହି ଆପ 16 KB କମ୍ପାଟିବଲ ନୁହେଁ। ELF ଆଲାଇନମେଣ୍ଟ ଯାଞ୍ଚ ବିଫଳ ହୋଇଛି। ପୃଷ୍ଠା ସାଇଜ କମ୍ପାଟିବଲ ମୋଡ ବ୍ୟବହାର କରି ଏହି ଆପକୁ ଚଲାଯିବ। ସର୍ବୋତ୍ତମ କମ୍ପାଟିବିଲିଟୀ ପାଇଁ ଦୟାକରି 16 KB ସପୋର୍ଟ ସହ ଆପ୍ଲିକେସନକୁ ପୁଣି କମ୍ପାଇଲ କରନ୍ତୁ। ଅଧିକ ସୂଚନା ପାଇଁ &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;କୁ ଦେଖନ୍ତୁ"</string>
-    <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"ଏହି ଆପ 16 KB କମ୍ପାଟିବଲ ନୁହେଁ। APK ଏବଂ ELF ଆଲାଇନମେଣ୍ଟ ଯାଞ୍ଚଗୁଡ଼ିକ ବିଫଳ ହୋଇଛି। ପୃଷ୍ଠା ସାଇଜ କମ୍ପାଟିବଲ ମୋଡ ବ୍ୟବହାର କରି ଏହି ଆପକୁ ଚଲାଯିବ। ସର୍ବୋତ୍ତମ କମ୍ପାଟିବିଲିଟୀ ପାଇଁ ଦୟାକରି 16 KB ସପୋର୍ଟ ସହ ଆପ୍ଲିକେସନକୁ ପୁଣି କମ୍ପାଇଲ କରନ୍ତୁ। ଅଧିକ ସୂଚନା ପାଇଁ &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;କୁ ଦେଖନ୍ତୁ"</string>
+    <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"ଏହି ଆପ 16 KB ପାଇଁ କମ୍ପାଟିବିଲ ନୁହେଁ। APK ଏବଂ ELF ଆଲାଇନମେଣ୍ଟ ଯାଞ୍ଚଗୁଡ଼ିକ ବିଫଳ ହୋଇଛି। ପୃଷ୍ଠା ସାଇଜ କମ୍ପାଟିବିଲ ମୋଡ ବ୍ୟବହାର କରି ଏହି ଆପକୁ ଚଲାଯିବ। ସର୍ବୋତ୍ତମ କମ୍ପାଟିବିଲିଟୀ ପାଇଁ ଦୟାକରି 16 KB ସପୋର୍ଟ ସହ ଆପ୍ଲିକେସନକୁ ପୁଣି କମ୍ପାଇଲ କରନ୍ତୁ। ଅଧିକ ସୂଚନା ପାଇଁ &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;କୁ ଦେଖନ୍ତୁ"</string>
+    <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"ଏହି ଆପ 16 KB ପାଇଁ କମ୍ପାଟିବିଲ ନୁହେଁ। APK ଏବଂ ELF ଆଲାଇନମେଣ୍ଟ ଯାଞ୍ଚଗୁଡ଼ିକ ବିଫଳ ହୋଇଛି। ପୃଷ୍ଠା ସାଇଜ କମ୍ପାଟିବିଲ ମୋଡ ବ୍ୟବହାର କରି ଏହି ଆପକୁ ଚଲାଯିବ। ସର୍ବୋତ୍ତମ କମ୍ପାଟିବିଲିଟୀ ପାଇଁ ଦୟାକରି 16 KB ସପୋର୍ଟ ସହ ଆପ୍ଲିକେସନକୁ ପୁଣି କମ୍ପାଇଲ କରନ୍ତୁ। ଅଧିକ ସୂଚନା ପାଇଁ &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;କୁ ଦେଖନ୍ତୁ"</string>
+    <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"ଏହି ଆପ 16 KB ପାଇଁ କମ୍ପାଟିବିଲ ନୁହେଁ। APK ଏବଂ ELF ଆଲାଇନମେଣ୍ଟ ଯାଞ୍ଚଗୁଡ଼ିକ ବିଫଳ ହୋଇଛି। ପୃଷ୍ଠା ସାଇଜ କମ୍ପାଟିବିଲ ମୋଡ ବ୍ୟବହାର କରି ଏହି ଆପକୁ ଚଲାଯିବ। ସର୍ବୋତ୍ତମ କମ୍ପାଟିବିଲିଟୀ ପାଇଁ ଦୟାକରି 16 KB ସପୋର୍ଟ ସହ ଆପ୍ଲିକେସନକୁ ପୁଣି କମ୍ପାଇଲ କରନ୍ତୁ। ଅଧିକ ସୂଚନା ପାଇଁ &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;କୁ ଦେଖନ୍ତୁ"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"ସେବାର ସୁବିଧା ନାହିଁ।"</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"ଆପଣ କଲର୍‍ ID ସେଟିଙ୍ଗ ବଦଳାଇପାରିବେ ନାହିଁ।"</string>
     <string name="auto_data_switch_title" msgid="3286350716870518297">"<xliff:g id="CARRIERDISPLAY">%s</xliff:g>କୁ ଡାଟା ସ୍ୱିଚ କରାଯାଇଛି"</string>
@@ -87,6 +87,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"ମୋବାଇଲ୍‌ ନେଟ୍‌ୱର୍କ ମିଳୁନାହିଁ"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"ନିଜ ପସନ୍ଦର ନେଟ୍‌ୱର୍କକୁ ଯିବାପାଇଁ ଚେଷ୍ଟା କରନ୍ତୁ। ବଦଳାଇବା ପାଇଁ ଟାପ୍ କରନ୍ତୁ।"</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"ଜରୁରୀକାଳୀନ କଲ୍ ଉପଲବ୍ଧ ନାହିଁ"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"ଜରୁରୀକାଳୀନ କଲ କରିବା ପାଇଁ ଏକ ମୋବାଇଲ ନେଟୱାର୍କ ଆବଶ୍ୟକ"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"ଆଲର୍ଟ"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"କଲ୍‌ ଫରୱାର୍ଡିଂ"</string>
@@ -1778,6 +1780,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"ଏକ-ହାତ ମୋଡ୍"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"ଅତ୍ୟଧିକ ଡିମ"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"ଶ୍ରବଣ ଡିଭାଇସଗୁଡ଼ିକ"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"ଭଲ୍ୟୁମ୍ କୀ\'ଗୁଡ଼ିକୁ ଧରି ରଖାଯାଇଛି। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ଚାଲୁ ହୋଇଛି।"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"ଭଲ୍ୟୁମ୍ କୀ\'ଗୁଡ଼ିକୁ ଧରି ରଖାଯାଇଛି। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ବନ୍ଦ ହୋଇଛି।"</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"ଭଲ୍ୟୁମ କୀ\'ଗୁଡ଼ିକୁ ରିଲିଜ କରନ୍ତୁ। <xliff:g id="SERVICE_NAME">%1$s</xliff:g>କୁ ଚାଲୁ କରିବା ପାଇଁ ଉଭୟ ଭଲ୍ୟୁମ କୀ\'କୁ ପୁଣି 3 ସେକେଣ୍ଡ ପାଇଁ ଦବାଇ ଧରି ରଖନ୍ତୁ।"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 6d92e01..12f4e90 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -87,6 +87,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"ਮੋਬਾਈਲ ਨੈੱਟਵਰਕ ਤੱਕ ਪਹੁੰਚ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕਦੀ"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"ਤਰਜੀਹੀ ਨੈੱਟਵਰਕ ਨੂੰ ਬਦਲ ਕੇ ਦੇਖੋ। ਬਦਲਣ ਲਈ ਟੈਪ ਕਰੋ।"</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"ਸੰਕਟਕਾਲੀਨ ਕਾਲਿੰਗ ਉਪਲਬਧ ਨਹੀਂ"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"ਐਮਰਜੈਂਸੀ ਕਾਲਾਂ ਲਈ ਕਿਸੇ ਮੋਬਾਈਲ ਨੈੱਟਵਰਕ ਦੀ ਲੋੜ ਹੁੰਦੀ ਹੈ"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"ਅਲਰਟ"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"ਕਾਲ ਫਾਰਵਰਡਿੰਗ"</string>
@@ -1778,6 +1780,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"ਇੱਕ ਹੱਥ ਮੋਡ"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"ਜ਼ਿਆਦਾ ਘੱਟ ਚਮਕ"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"ਸੁਣਨ ਵਾਲੇ ਡੀਵਾਈਸ"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"ਅਵਾਜ਼ੀ ਕੁੰਜੀਆਂ ਦਬਾ ਕੇ ਰੱਖੀਆਂ ਗਈਆਂ। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ਨੂੰ ਚਾਲੂ ਕੀਤਾ ਗਿਆ।"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"ਅਵਾਜ਼ੀ ਕੁੰਜੀਆਂ ਦਬਾ ਕੇ ਰੱਖੀਆਂ ਗਈਆਂ। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ਨੂੰ ਬੰਦ ਕੀਤਾ ਗਿਆ।"</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"ਅਵਾਜ਼ ਕੁੰਜੀਆਂ ਨੂੰ ਛੱਡੋ। <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ਨੂੰ ਚਾਲੂ ਕਰਨ ਲਈ, ਦੋਵੇਂ ਅਵਾਜ਼ ਕੁੰਜੀਆਂ ਨੂੰ 3 ਸਕਿੰਟਾਂ ਲਈ ਦੁਬਾਰਾ ਦਬਾਈ ਰੱਖੋ।"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index ec37251..113ffa3 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -73,9 +73,9 @@
     <string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"ID rozmówcy ustawiony jest domyślnie na „zastrzeżony”. Następne połączenie: nie zastrzeżony"</string>
     <string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"ID rozmówcy ustawiony jest domyślnie na „nie zastrzeżony”. Następne połączenie: zastrzeżony"</string>
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"ID rozmówcy ustawiony jest domyślnie na „nie zastrzeżony”. Następne połączenie: nie zastrzeżony"</string>
-    <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Ta aplikacja nie jest zgodna z trybem 16 KB. Sprawdzenie zgodności pliku APK się nie powiodło. Ta aplikacja będzie działać w trybie zgodnym z rozmiarem strony. Aby zapewnić najlepszą zgodność, rekompiluj aplikację, żeby obsługiwała 16 KB. Więcej informacji znajdziesz na stronie &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
-    <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Ta aplikacja nie jest zgodna z trybem 16 KB. Sprawdzenie zgodności ELF się nie powiodło. Ta aplikacja będzie działać w trybie zgodnym z rozmiarem strony. Aby zapewnić najlepszą zgodność, rekompiluj aplikację, żeby obsługiwała 16 KB. Więcej informacji znajdziesz na stronie &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
-    <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Ta aplikacja nie jest zgodna z trybem 16 KB. Sprawdzenie zgodności pliku APK i ELF się nie powiodło. Ta aplikacja będzie działać w trybie zgodnym z rozmiarem strony. Aby zapewnić najlepszą zgodność, rekompiluj aplikację, żeby obsługiwała 16 KB. Więcej informacji znajdziesz na stronie &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+    <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"Ta aplikacja nie jest zgodna z trybem 16 KB. Sprawdzenie zgodności pliku APK się nie powiodło. Aplikacja będzie działać w trybie zgodnym z rozmiarem strony. Aby zapewnić najlepszą zgodność, dokonaj ponownej kompilacji, żeby aplikacja obsługiwała 16 KB. Więcej informacji znajdziesz na stronie &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+    <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"Ta aplikacja nie jest zgodna z trybem 16 KB. Sprawdzenie zgodności ELF się nie powiodło. Aplikacja będzie działać w trybie zgodnym z rozmiarem strony. Aby zapewnić najlepszą zgodność, dokonaj ponownej kompilacji, żeby aplikacja obsługiwała 16 KB. Więcej informacji znajdziesz na stronie &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+    <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"Ta aplikacja nie jest zgodna z trybem 16 KB. Sprawdzenie zgodności pliku APK i ELF się nie powiodło. Aplikacja będzie działać w trybie zgodnym z rozmiarem strony. Aby zapewnić najlepszą zgodność, dokonaj ponownej kompilacji, żeby aplikacja obsługiwała 16 KB. Więcej informacji znajdziesz na stronie &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"Usługa nie jest świadczona."</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"Nie możesz zmienić ustawienia ID rozmówcy."</string>
     <string name="auto_data_switch_title" msgid="3286350716870518297">"Przełączono mobilną transmisję danych na: <xliff:g id="CARRIERDISPLAY">%s</xliff:g>"</string>
@@ -89,6 +89,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"Brak zasięgu sieci komórkowej"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Spróbuj zmienić preferowaną sieć. Kliknij, by zmienić."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Połączenia alarmowe są niedostępne"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"Połączenia alarmowe wymagają sieci komórkowej"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"Alerty"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"Przekierowanie połączeń"</string>
@@ -1780,6 +1782,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Tryb jednej ręki"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Dodatkowe przyciemnienie"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Urządzenia słuchowe"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Przytrzymano klawisze głośności. Usługa <xliff:g id="SERVICE_NAME">%1$s</xliff:g> została włączona."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Przytrzymano klawisze głośności. Usługa <xliff:g id="SERVICE_NAME">%1$s</xliff:g> została wyłączona."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Zwolnij przyciski głośności. Aby włączyć usługę <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, naciśnij i przytrzymaj oba przyciski głośności przez 3 sekundy."</string>
diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml
index 5ff383d..2daa730 100644
--- a/core/res/res/values-pt-rBR/strings.xml
+++ b/core/res/res/values-pt-rBR/strings.xml
@@ -88,6 +88,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"Não foi possível acessar a rede móvel"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Tente alterar a rede preferencial. Toque para alterar."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Chamadas de emergência indisponíveis"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"As chamadas de emergência exigem uma rede móvel"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"Alertas"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"Encaminhamento de chamada"</string>
@@ -1779,6 +1781,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Modo para uma mão"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Escurecer ainda mais a tela"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Aparelhos auditivos"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Teclas de volume pressionadas. Serviço <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ativado."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Teclas de volume pressionadas. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> desativado."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Solte as teclas de volume. Para ativar o serviço <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, toque e pressione as duas teclas de volume por três segundos."</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 38a071a..5572de1 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -88,6 +88,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"Não é possível estabelecer ligação à rede móvel."</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Experimente alterar a rede preferida. Toque para alterar."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Chamadas de emergência indisponíveis"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"As chamadas de emergência requerem uma rede móvel"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"Alertas"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"Reencaminhamento de chamadas"</string>
@@ -1779,6 +1781,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Modo para uma mão"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Mais escuro"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Dispositivos auditivos"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Teclas do volume premidas. Serviço <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ativado."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Teclas de volume premidas. Serviço <xliff:g id="SERVICE_NAME">%1$s</xliff:g> desativado."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Solte as teclas de volume. Para ativar o serviço <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, prima sem soltar ambas as teclas de volume novamente durante 3 segundos."</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 5ff383d..2daa730 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -88,6 +88,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"Não foi possível acessar a rede móvel"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Tente alterar a rede preferencial. Toque para alterar."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Chamadas de emergência indisponíveis"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"As chamadas de emergência exigem uma rede móvel"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"Alertas"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"Encaminhamento de chamada"</string>
@@ -1779,6 +1781,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Modo para uma mão"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Escurecer ainda mais a tela"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Aparelhos auditivos"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Teclas de volume pressionadas. Serviço <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ativado."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Teclas de volume pressionadas. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> desativado."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Solte as teclas de volume. Para ativar o serviço <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, toque e pressione as duas teclas de volume por três segundos."</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 738951e..cf0f771 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -88,6 +88,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"Nu se poate stabili conexiunea la rețeaua mobilă"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Încearcă să schimbi rețeaua preferată. Atinge pentru a schimba."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Apelurile de urgență nu sunt disponibile"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"Apelurile de urgență necesită o rețea mobilă"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"Alerte"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"Redirecționarea apelurilor"</string>
@@ -1779,6 +1781,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Modul cu o mână"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Luminozitate redusă suplimentar"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Aparate auditive"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"S-au apăsat lung tastele de volum. S-a activat <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"S-au apăsat lung tastele de volum. S-a dezactivat <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Eliberează butoanele de volum. Pentru a activa <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, apasă lung pe ambele butoane de volum timp de trei secunde încă o dată."</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 285e941..387cd33 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -89,6 +89,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"Мобильная сеть недоступна"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Нажмите, чтобы выбрать другую сеть."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Экстренные вызовы недоступны"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"Для экстренных вызовов нужна мобильная сеть."</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"Оповещения"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"Переадресация вызовов"</string>
@@ -1551,7 +1553,7 @@
     <string name="forward_intent_to_work" msgid="3620262405636021151">"Вы перешли в рабочий профиль"</string>
     <string name="input_method_binding_label" msgid="1166731601721983656">"Способ ввода"</string>
     <string name="sync_binding_label" msgid="469249309424662147">"Синхр."</string>
-    <string name="accessibility_binding_label" msgid="1974602776545801715">"Спец. возможности"</string>
+    <string name="accessibility_binding_label" msgid="1974602776545801715">"Специальные возможности"</string>
     <string name="wallpaper_binding_label" msgid="1197440498000786738">"Фоновый рисунок"</string>
     <string name="chooser_wallpaper" msgid="3082405680079923708">"Сменить обои"</string>
     <string name="notification_listener_binding_label" msgid="2702165274471499713">"Служба просмотра уведомлений"</string>
@@ -1780,6 +1782,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Режим управления одной рукой"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Дополнительное уменьшение яркости"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Слуховые аппараты"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Использован жест с кнопками регулировки громкости. Функция \"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\" включена."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Использован жест с кнопками регулировки громкости. Функция \"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\" отключена."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Отпустите кнопки громкости. Чтобы включить <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, нажмите и удерживайте обе кнопки регулировки громкости в течение трех секунд."</string>
diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml
index 0cb6f30..9fd63e4 100644
--- a/core/res/res/values-si/strings.xml
+++ b/core/res/res/values-si/strings.xml
@@ -87,6 +87,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"ජංගම ජාලය වෙත ළඟා විය නොහැකිය"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"කැමති ජාලය වෙනස් කිරීමට උත්සාහ කරන්න. වෙනස් කිරීමට තට්ටු කරන්න."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"හදිසි ඇමතුම් ලබා ගත නොහැකිය"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"හදිසි ඇමතුම් සඳහා ජංගම ජාලයක් අවශ්‍ය වේ"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"ඇඟවීම්"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"ඇමතුම ප්‍රතියොමු කිරීම"</string>
@@ -1778,6 +1780,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"තනි අත් ප්‍රකාරය"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"තවත් අඳුරු"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"ශ්‍රවණ උපාංග"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"හඬ පරිමා යතුරු අල්ලා ගන්න <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ක්‍රියාත්මකයි."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"හඬ පරිමා යතුරු අල්ලා ගන්න <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ක්‍රියාවිරහිතයි."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"හඬ පරිමා යතුරු මුදා හරින්න. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> සක්‍රීය කිරීමට, හඬ පරිමා යතුරු දෙකම නැවත තත්පර 3ක් ඔබා අල්ලා සිටින්න."</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index 9d0efef..3528367 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -89,6 +89,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"Nepodarilo sa pripojiť k mobilnej sieti"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Skúste zmeniť predvolenú sieť. Zmeníte ju klepnutím."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Tiesňové volania nie sú k dispozícii"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"Tiesňové volania vyžadujú mobilnú sieť"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"Upozornenia"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"Presmerovanie hovorov"</string>
@@ -1780,6 +1782,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Režim jednej ruky"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Mimoriadne stmavenie"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Načúvacie zariadenia"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Pridržali ste tlačidlá hlasitosti. Služba <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je zapnutá."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Pridržali ste tlačidlá hlasitosti. Služba <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je vypnutá."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Uvoľnite tlačidlá hlasitosti. Ak chcete zapnúť službu <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, znova pridržte tri sekundy obe tlačidlá hlasitosti."</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index 2cadf4b..af6c95c 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -89,6 +89,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"Mobilnega omrežja ni mogoče doseči"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Poskusite spremeniti prednostno omrežje. Dotaknite se, če ga želite spremeniti."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Klicanje v sili ni na voljo"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"Za klice v sili potrebujete mobilno omrežje"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"Opozorila"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"Preusmerjanje klicev"</string>
@@ -1780,6 +1782,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Enoročni način"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Zelo zatemnjen zaslon"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Slušni pripomočki"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Tipki za glasnost sta pridržani. Storitev <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je vklopljena."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Tipki za glasnost sta pridržani. Storitev <xliff:g id="SERVICE_NAME">%1$s</xliff:g> je izklopljena."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Spustite gumba za glasnost. Če želite vklopiti storitev <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, znova pritisnite in 3 sekunde pridržite oba gumba za glasnost."</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 744b557..d8fdb9a 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -87,6 +87,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"Rrjeti celular është i paarritshëm"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Provo të ndryshosh rrjetin e preferuar. Trokit për ta ndryshuar."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Telefonatat e urgjencës nuk ofrohen"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"Telefonatat e urgjencës kërkojnë një rrjet celular"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"Sinjalizimet"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"Transferimi i telefonatave"</string>
@@ -1778,6 +1780,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Modaliteti i përdorimit me një dorë"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Shumë më i zbehtë"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Pajisjet e dëgjimit"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Tastet e volumit të mbajtura shtypur. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> i aktivizuar."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Tastet e volumit të mbajtura shtypur. U çaktivizua \"<xliff:g id="SERVICE_NAME">%1$s</xliff:g>\"."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Lësho tastet e volumit. Për të aktivizuar <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, shtyp dhe mbaj shtypur të dy tastet e volumit sërish për 3 sekonda."</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 45fb8ee..146855e 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -88,6 +88,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"Повезивање са мобилном мрежом није успело"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Пробајте да промените жељену мрежу. Додирните да бисте променили."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Хитни позиви нису доступни"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"Хитни позиви захтевају мобилну мрежу"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"Упозорења"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"Преусмеравање позива"</string>
@@ -1779,6 +1781,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Режим једном руком"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Додатно затамни"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Слушни апарати"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Држали сте тастере за јачину звука. Услуга <xliff:g id="SERVICE_NAME">%1$s</xliff:g> је укључена."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Држали сте тастере за јачину звука. Услуга <xliff:g id="SERVICE_NAME">%1$s</xliff:g> је искључена."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Пустите тастере за јачину звука. Да бисте укључили <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, поново притисните и задржите оба тастера за јачину звука 3 секунде."</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 4b22b7d..ecb3c79 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -87,6 +87,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"Det går inte att nå mobilnätverket"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Testa att byta föredraget nätverk. Tryck om du vill ändra."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Det går inte att ringa nödsamtal"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"Mobilnätverk krävs för att ringa nödsamtal"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"Aviseringar"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"Vidarekoppla samtal"</string>
@@ -1778,6 +1780,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Enhandsläge"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Extradimmat"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Hörhjälpmedel"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Volymknapparna har tryckts ned. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> har aktiverats."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Volymknapparna har tryckts ned. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> har inaktiverats."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Släpp volymknapparna. Du kan aktivera <xliff:g id="SERVICE_NAME">%1$s</xliff:g> genom att hålla båda volymknapparna nedtryckta i tre sekunder igen."</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index d6565bf..98c9731 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -87,6 +87,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"Imeshindwa kufikia mtandao wa simu"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Jaribu kutumia mtandao unaopendelea. Gusa ili ubadilishe."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Huduma ya kupiga simu za dharura haipatikani"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"Huduma ya kupiga simu za dharura inahitaji mtandao wa simu"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"Arifa"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"Kupeleka simu kwenye namba nyingine"</string>
@@ -1778,6 +1780,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Hali ya kutumia kwa mkono mmoja"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Kipunguza mwangaza zaidi"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Vifaa vya kusaidia kusikia"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Vitufe vya sauti vilivyoshikiliwa. Umewasha <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Vitufe vya sauti vimeshikiliwa. Umezima <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Achilia vitufe vya sauti. Ili uwashe <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, bonyeza na ushikilie tena vitufe vyote vya sauti kwa sekunde 3."</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 17119a6..f8b2bbc 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -87,6 +87,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"மொபைல் நெட்வொர்க் கிடைக்கவில்லை"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"விருப்ப நெட்வொர்க்கை மாற்றவும். மாற்ற, தட்டவும்."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"அவசர அழைப்பைச் செய்ய முடியாது"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"அவசர அழைப்புகளுக்கு மொபைல் நெட்வொர்க் தேவை"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"விழிப்பூட்டல்கள்"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"அழைப்பு திருப்பிவிடுதல்"</string>
@@ -1778,6 +1780,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"ஒற்றைக் கைப் பயன்முறை"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"மிகக் குறைவான வெளிச்சம்"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"செவித்துணைக் கருவிகள்"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"ஒலியளவுக்கான விசைகளைப் பிடித்திருந்தீர்கள். <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ஆன் செய்யப்பட்டது."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"ஒலியளவுக்கான விசைகளைப் பிடித்திருந்தீர்கள். <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ஆஃப் செய்யப்பட்டது."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"ஒலியளவு பட்டன்களை அழுத்துவதை நிறுத்துங்கள். <xliff:g id="SERVICE_NAME">%1$s</xliff:g> சேவையை இயக்க, ஒலியளவு பட்டன்கள் இரண்டையும் 3 வினாடிகளுக்கு மீண்டும் அழுத்திப் பிடிக்கவும்."</string>
diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml
index 2b97029..bcd2865 100644
--- a/core/res/res/values-te/strings.xml
+++ b/core/res/res/values-te/strings.xml
@@ -87,6 +87,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"మొబైల్ నెట్‌వర్క్ అందుబాటులో లేదు"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"ప్రాధాన్య నెట్‌వర్క్‌ను మార్చుకోవడానికి ప్రయత్నించండి. మార్చడానికి నొక్కండి."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"అత్యవసర కాలింగ్ అందుబాటులో లేదు"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"ఎమర్జెన్సీ కాల్స్‌కు మొబైల్ నెట్‌వర్క్ అవసరమవుతుంది"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"అలర్ట్‌లు"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"కాల్ ఫార్వార్డింగ్"</string>
@@ -1778,6 +1780,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"వన్-హ్యాండెడ్ మోడ్"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"ఎక్స్‌ట్రా డిమ్"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"వినికిడి పరికరాలు"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"వాల్యూమ్ కీలు నొక్కి ఉంచబడ్డాయి. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ఆన్ చేయబడింది"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"వాల్యూమ్ కీలు నొక్కి ఉంచబడ్డాయి. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ఆఫ్ చేయబడింది"</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"వాల్యూమ్ కీలను రిలీజ్ చేయండి. <xliff:g id="SERVICE_NAME">%1$s</xliff:g>‌ను ఆన్ చేయడానికి, రెండు వాల్యూమ్ కీలను మళ్లీ 3 సెకన్ల పాటు నొక్కి పట్టుకోండి."</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 7e70a3b..0e66e37 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -71,9 +71,9 @@
     <string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"หมายเลขผู้โทรได้รับการตั้งค่าเริ่มต้นเป็นถูกจำกัด การโทรครั้งต่อไป: ไม่จำกัด"</string>
     <string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"หมายเลขผู้โทรได้รับการตั้งค่าเริ่มต้นเป็นไม่จำกัด การโทรครั้งต่อไป: ถูกจำกัด"</string>
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"หมายเลขผู้โทรได้รับการตั้งค่าเริ่มต้นเป็นไม่จำกัด การโทรครั้งต่อไป: ไม่จำกัด"</string>
-    <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"แอปนี้ไม่รองรับขนาดหน้า 16 KB ผลการตรวจสอบการจัดตำแหน่ง APK คือไม่ผ่าน ด้วยเหตุนี้ แอปจะทำงานโดยใช้โหมดการใช้งานร่วมกับขนาดหน้าได้ เพื่อให้ใช้งานร่วมกันได้อย่างดีที่สุด โปรดคอมไพล์แอปพลิเคชันอีกครั้งโดยมีการรองรับขนาดหน้า 16 KB ดูข้อมูลเพิ่มเติมได้ที่ &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
-    <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"แอปนี้ไม่รองรับขนาดหน้า 16 KB ผลการตรวจสอบการจัดตำแหน่ง ELF คือไม่ผ่าน ด้วยเหตุนี้ แอปจะทำงานโดยใช้โหมดการใช้งานร่วมกับขนาดหน้าได้ เพื่อให้ใช้งานร่วมกันได้อย่างดีที่สุด โปรดคอมไพล์แอปพลิเคชันอีกครั้งโดยมีการรองรับขนาดหน้า 16 KB ดูข้อมูลเพิ่มเติมได้ที่ &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
-    <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"แอปนี้ไม่รองรับขนาดหน้า 16 KB ผลการตรวจสอบการจัดตำแหน่งของ APK และ ELF คือไม่ผ่าน ด้วยเหตุนี้ แอปจะทำงานโดยใช้โหมดการใช้งานร่วมกับขนาดหน้าได้ เพื่อให้ใช้งานร่วมกันได้อย่างดีที่สุด โปรดคอมไพล์แอปพลิเคชันอีกครั้งโดยมีการรองรับขนาดหน้า 16 KB ดูข้อมูลเพิ่มเติมได้ที่ &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+    <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"แอปนี้ไม่รองรับขนาดหน้า 16 KB ผลตรวจสอบการจัดตำแหน่ง APK คือไม่ผ่าน ด้วยเหตุนี้แอปจะทำงานโดยใช้โหมดเข้ากันได้กับขนาดหน้า โปรดคอมไพล์แอปพลิเคชันอีกครั้งโดยมีการรองรับขนาดหน้า 16 KB เพื่อให้ใช้งานร่วมกันได้อย่างดีที่สุด ดูข้อมูลเพิ่มเติมได้ที่ &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+    <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"แอปนี้ไม่รองรับขนาดหน้า 16 KB ผลตรวจสอบการจัดตำแหน่ง ELF คือไม่ผ่าน ด้วยเหตุนี้แอปจะทำงานโดยใช้โหมดเข้ากันได้กับขนาดหน้า โปรดคอมไพล์แอปพลิเคชันอีกครั้งโดยมีการรองรับขนาดหน้า 16 KB เพื่อให้ใช้งานร่วมกันได้อย่างดีที่สุด ดูข้อมูลเพิ่มเติมได้ที่ &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
+    <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"แอปนี้ไม่รองรับขนาดหน้า 16 KB ผลตรวจสอบการจัดตำแหน่ง APK และ ELF คือไม่ผ่าน ด้วยเหตุนี้แอปจะทำงานโดยใช้โหมดเข้ากันได้กับขนาดหน้า โปรดคอมไพล์แอปพลิเคชันอีกครั้งโดยมีการรองรับขนาดหน้า 16 KB เพื่อให้ใช้งานร่วมกันได้อย่างดีที่สุด ดูข้อมูลเพิ่มเติมได้ที่ &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"ไม่มีการนำเสนอบริการ"</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"คุณไม่สามารถเปลี่ยนการตั้งค่าหมายเลขผู้โทร"</string>
     <string name="auto_data_switch_title" msgid="3286350716870518297">"เปลี่ยนไปใช้อินเทอร์เน็ตมือถือของ <xliff:g id="CARRIERDISPLAY">%s</xliff:g> แล้ว"</string>
@@ -87,6 +87,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"เชื่อมต่อเครือข่ายมือถือไม่ได้"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"ลองเปลี่ยนเครือข่ายที่ต้องการ แตะเพื่อเปลี่ยน"</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"โทรหาหมายเลขฉุกเฉินไม่ได้"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"การโทรหาหมายเลขฉุกเฉินต้องใช้เครือข่ายมือถือ"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"การแจ้งเตือน"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"การโอนสาย"</string>
@@ -1778,6 +1780,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"โหมดมือเดียว"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"หรี่แสงเพิ่มเติม"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"เครื่องช่วยฟัง"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"กดปุ่มปรับระดับเสียงค้างไว้แล้ว เปิด <xliff:g id="SERVICE_NAME">%1$s</xliff:g> แล้ว"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"กดปุ่มปรับระดับเสียงค้างไว้แล้ว ปิด <xliff:g id="SERVICE_NAME">%1$s</xliff:g> แล้ว"</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"ปล่อยปุ่มปรับระดับเสียง หากต้องการเปิด <xliff:g id="SERVICE_NAME">%1$s</xliff:g> ให้กดปุ่มปรับระดับเสียงทั้ง 2 ปุ่มค้างไว้อีกครั้งเป็นเวลา 3 วินาที"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 6fd93bc..0ab4fb2 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -87,6 +87,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"Hindi makakonekta sa mobile network"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Subukang baguhin ang gustong network. I-tap para baguhin."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Hindi available ang pang-emergency na pagtawag"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"Kailangan ng mobile network para sa mga emergency na tawag"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"Mga Alerto"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"Pagpasa ng tawag"</string>
@@ -1778,6 +1780,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"One-Hand mode"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Extra dim"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Mga hearing device"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Pinindot nang matagal ang volume keys. Na-on ang <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Pinindot nang matagal ang volume keys. Na-off ang <xliff:g id="SERVICE_NAME">%1$s</xliff:g>."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Bitawan ang mga volume key. Para i-on ang <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, muling pindutin nang matagal ang dalawang volume key sa loob ng 3 segundo."</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 16915b5..78ce9d4 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -87,6 +87,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"Mobil ağa erişilemiyor"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Tercih edilen ağı değiştirmeyi deneyin. Değiştirmek için dokunun."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Acil durum çağrısı kullanılamaz"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"Acil durum aramaları için mobil ağ gereklidir"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"Uyarılar"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"Çağrı yönlendirme"</string>
@@ -1778,6 +1780,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Tek El modu"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Ekstra loş"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"İşitme cihazları"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Ses tuşlarını basılı tuttunuz. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> açıldı."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Ses tuşlarını basılı tuttunuz. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> kapatıldı."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Ses seviyesi tuşlarını bırakın. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> hizmetini etkinleştirmek için her iki ses seviyesi tuşuna yeniden basıp 3 saniye boyunca basılı tutun."</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index dcf15a0..a98fda4 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -89,6 +89,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"Не вдається під’єднатися до мобільної мережі"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Спробуйте змінити вибрану мережу. Торкніться, щоб це зробити."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Екстрені виклики недоступні"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"Щоб здійснювати екстрені виклики, потрібне з’єднання з мобільною мережею"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"Сповіщення"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"Переадресація виклику"</string>
@@ -1780,6 +1782,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Режим керування однією рукою"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Додаткове зменшення яскравості"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Слухові апарати"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Утримано клавіші гучності. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> увімкнено."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Утримано клавіші гучності. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> вимкнено."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Відпустіть клавіші гучності. Щоб увімкнути сервіс <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, натисніть і втримуйте обидві клавіші гучності протягом 3 секунд."</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 8c75e81..0a00474 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -71,9 +71,9 @@
     <string name="CLIRDefaultOnNextCallOff" msgid="5036749051007098105">"‏کالر ID کی ڈیفالٹ ترتیب محدود کردہ ہے۔ اگلی کال: غیر محدود کردہ"</string>
     <string name="CLIRDefaultOffNextCallOn" msgid="1022781126694885017">"‏کالر ID کی ڈیفالٹ ترتیب غیر محدود کردہ ہے۔ اگلی کال: محدود کردہ"</string>
     <string name="CLIRDefaultOffNextCallOff" msgid="2491576172356463443">"‏کالر ID کی ڈیفالٹ ترتیب غیر محدود کردہ ہے۔ اگلی کال: غیر محدود کردہ"</string>
-    <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"‏یہ ایپ ‎16 KB کے موافق نہیں ہے۔ ‫APK الائنمنٹ چیک ناکام ہو گیا۔ یہ ایپ صفحہ کے سائز کے ساتھ موافقت رکھنے والے موڈ کے ساتھ چلے گی۔ بہترین موافقت کے لیے، براہ کرم ‎16 KB کے سپورٹ کے ساتھ ایپلیکیشن کو دوبارہ مرتب کریں۔ مزید معلومات کے لیے، ‎&lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;‎ دیکھیں"</string>
-    <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"‏یہ ایپ ‎16 KB کے موافق نہیں ہے۔ ‫ELF الائنمنٹ چیک ناکام ہو گیا۔ یہ ایپ صفحہ کے سائز کے ساتھ موافقت رکھنے والے موڈ کے ساتھ چلے گی۔ بہترین موافقت کے لیے، براہ کرم ‎16 KB کے سپورٹ کے ساتھ ایپلیکیشن کو دوبارہ مرتب کریں۔ مزید معلومات کے لیے، ‎&lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;‎ دیکھیں"</string>
-    <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"‏یہ ایپ ‎16 KB کے موافق نہیں ہے۔ ‫APK اور ELF الائنمنٹ چیکس ناکام ہو گئے۔ یہ ایپ صفحہ کے سائز کے ساتھ موافقت رکھنے والے موڈ کے ساتھ چلے گی۔ بہترین موافقت کے لیے، براہ کرم ‎16 KB کے سپورٹ کے ساتھ ایپلیکیشن کو دوبارہ مرتب کریں۔ مزید معلومات کے لیے، ‎&lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt;‎ دیکھیں"</string>
+    <string name="page_size_compat_apk_warning" msgid="2982396798449041224">"‏یہ ایپ ‎16 KB کے موافق نہیں ہے۔ ‫APK الائنمنٹ چیک ناکام ہو گیا۔ یہ ایپ صفحہ کے سائز کے ساتھ موافقت رکھنے والے موڈ کے ساتھ چلائی جائے گی۔ بہترین موافقت کے لیے، براہ کرم ‎16 KB کے سپورٹ کے ساتھ ایپلیکیشن کو دوبارہ کمپائل کریں۔ مزید معلومات کے لیے &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; دیکھیں"</string>
+    <string name="page_size_compat_elf_warning" msgid="6753874059564812651">"‏یہ ایپ ‎16 KB کے موافق نہیں ہے۔ ‫ELF الائنمنٹ چیک ناکام ہو گیا۔ یہ ایپ صفحہ کے سائز کے ساتھ موافقت رکھنے والے موڈ کے ساتھ چلائی جائے گی۔ بہترین موافقت کے لیے، براہ کرم ‎16 KB کے سپورٹ کے ساتھ ایپلیکیشن کو دوبارہ کمپائل کریں۔ مزید معلومات کے لیے &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; دیکھیں"</string>
+    <string name="page_size_compat_apk_and_elf_warning" msgid="7628675779500605390">"‏یہ ایپ ‎16 KB کے موافق نہیں ہے۔ ‫APK اور ELF الائنمنٹ چیکس ناکام ہو گئے۔ یہ ایپ صفحہ کے سائز کے ساتھ موافقت رکھنے والے موڈ کے ساتھ چلائی جائے گی۔ بہترین موافقت کے لیے، براہ کرم ‎16 KB کے سپورٹ کے ساتھ ایپلیکیشن کو دوبارہ کمپائل کریں۔ مزید معلومات کے لیے &lt;a href=\"https://developer.android.com/16kb-page-size\"&gt;https://developer.android.com/16kb-page-size&lt;/a&gt; دیکھیں"</string>
     <string name="serviceNotProvisioned" msgid="8289333510236766193">"سروس فراہم نہیں کی گئی۔"</string>
     <string name="CLIRPermanent" msgid="166443681876381118">"‏آپ کالر ID کی ترتیبات تبدیل نہیں کر سکتے ہیں۔"</string>
     <string name="auto_data_switch_title" msgid="3286350716870518297">"ڈیٹا <xliff:g id="CARRIERDISPLAY">%s</xliff:g> پر سوئچ کیا گیا"</string>
@@ -87,6 +87,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"موبائل نیٹ ورک تک رسائی نہیں ہو سکتی"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"ترجیحی نیٹ ورک تبدیل کر کے دیکھیں۔ تبدیل کرنے کے لیے تھپتھپائیں۔"</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"ایمرجنسی کالنگ دستیاب نہیں ہے"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"ہنگامی کالز کو موبائل نیٹ ورک کی ضرورت ہے"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"الرٹس"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"کال فارورڈنگ"</string>
@@ -1778,6 +1780,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"ایک ہاتھ کی وضع"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"اضافی مدھم"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"سماعتی آلات"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"والیوم کی کلیدوں کو دبائے رکھا گیا۔ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> آن ہے۔"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"والیوم کی کلیدوں کو دبائے رکھا گیا۔ <xliff:g id="SERVICE_NAME">%1$s</xliff:g> آف ہے۔"</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"والیوم کی کلیدوں کو ریلیز کریں <xliff:g id="SERVICE_NAME">%1$s</xliff:g> کو آن کرنے کے لیے، والیوم کی دونوں کلیدوں کو دوبارہ 3 سیکنڈ تک چھوئیں اور دبائے رکھیں۔"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 89e2e68..2f158f4 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -87,6 +87,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"Mobil tarmoqqa ulanib bo‘lmadi"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Tarmoq turini almashtiring. Almashtirish uchun bosing."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Favqulodda chaqiruv ishlamayapti"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"Favqulodda chaqiruvlar uchun mobil tarmoq zarur"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"Ogohlantirishlar"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"Chaqiruvlarni uzatish"</string>
@@ -1778,6 +1780,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Ixcham rejim"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Juda xira"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Eshitish qurilmalari"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Tovush tugmalari bosib turildi. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> yoqildi."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Tovush tugmalari bosib turildi. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> faolsizlantirildi."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Tovush tugmalarini qoʻyib yuboring. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> xizmatini yoqish uchun ikkala tovush tugmasini 3 soniya bosib turing."</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index ad0f42d..adb3e02 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -87,6 +87,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"Không thể kết nối với mạng di động"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Hãy thử thay đổi mạng ưu tiên. Nhấn để thay đổi."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Không có dịch vụ gọi khẩn cấp"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"Cần có mạng di động để thực hiện các cuộc gọi khẩn cấp"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"Thông báo"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"Chuyển tiếp cuộc gọi"</string>
@@ -1778,6 +1780,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Chế độ một tay"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Siêu tối"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Thiết bị trợ thính"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Bạn đã giữ các phím âm lượng. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> đã bật."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Bạn đã giữ các phím âm lượng. <xliff:g id="SERVICE_NAME">%1$s</xliff:g> đã tắt."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Thả phím âm lượng. Để bật <xliff:g id="SERVICE_NAME">%1$s</xliff:g>, hãy nhấn và giữ cả 2 phím âm lượng trong 3 giây một lần nữa."</string>
diff --git a/core/res/res/values-watch/config.xml b/core/res/res/values-watch/config.xml
index e6295ea..4ff3f88 100644
--- a/core/res/res/values-watch/config.xml
+++ b/core/res/res/values-watch/config.xml
@@ -101,6 +101,13 @@
          P.S this is a change only intended for wear devices. -->
     <bool name="config_enableViewGroupScalingFading">true</bool>
 
-    <!-- Allow the gesture to double tap the power button to trigger a target action. -->
-    <bool name="config_doubleTapPowerGestureEnabled">false</bool>
+    <!-- Controls the double tap power button gesture to trigger a target action.
+         0: Gesture is disabled
+         1: Launch camera mode, allowing the user to disable/enable the double tap power gesture
+            from launching the camera application.
+         2: Multi target mode, allowing the user to select one of the targets defined in
+            config_doubleTapPowerGestureMultiTargetDefaultAction and to disable/enable the double
+            tap power gesture from triggering the selected target action.
+    -->
+    <integer name="config_doubleTapPowerGestureMode">0</integer>
 </resources>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 120c08c..dd90e25 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -87,6 +87,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"无法连接到移动网络"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"请尝试更改首选网络。点按即可更改。"</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"无法使用紧急呼救服务"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"紧急呼叫需要使用移动网络"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"提醒"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"来电转接"</string>
@@ -1778,6 +1780,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"单手模式"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"极暗"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"助听装置"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"已按住音量键。<xliff:g id="SERVICE_NAME">%1$s</xliff:g>已开启。"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"已按住音量键。<xliff:g id="SERVICE_NAME">%1$s</xliff:g>已关闭。"</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"松开音量键。如要启用 <xliff:g id="SERVICE_NAME">%1$s</xliff:g>,请再次同时按住两个音量键 3 秒。"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 9d6a232..d34ed3d 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -87,6 +87,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"無法連線至流動網絡"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"請嘗試變更偏好的網絡。輕按即可變更。"</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"無法撥打緊急電話"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"撥打緊急電話需要使用流動網絡"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"通知"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"來電轉駁"</string>
@@ -1778,6 +1780,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"單手模式"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"超暗"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"助聽器"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"已按住音量鍵。<xliff:g id="SERVICE_NAME">%1$s</xliff:g> 已開啟。"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"已按住音量鍵。<xliff:g id="SERVICE_NAME">%1$s</xliff:g> 已關閉。"</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"鬆開音量鍵。如果要開 <xliff:g id="SERVICE_NAME">%1$s</xliff:g>,請同時㩒住兩個音量鍵 3 秒。"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 7f22ab4..09d1a7b 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -87,6 +87,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"無法連上行動網路"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"請嘗試變更偏好的網路。輕觸即可變更。"</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"無法撥打緊急電話"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"撥打緊急電話需要使用行動網路"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"快訊"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"來電轉接"</string>
@@ -1778,6 +1780,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"單手模式"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"超暗"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"助聽器"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"已按住音量鍵。「<xliff:g id="SERVICE_NAME">%1$s</xliff:g>」已開啟。"</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"已按住音量鍵。「<xliff:g id="SERVICE_NAME">%1$s</xliff:g>」已關閉。"</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"放開音量鍵。如要開啟 <xliff:g id="SERVICE_NAME">%1$s</xliff:g>,請同時按住音量調高鍵和調低鍵 3 秒。"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 0ac3e4a..cfc060a 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -87,6 +87,8 @@
     <string name="NetworkPreferenceSwitchTitle" msgid="1008329951315753038">"Ayikwazi ukufinyelela kunethiwekhi yeselula"</string>
     <string name="NetworkPreferenceSwitchSummary" msgid="2086506181486324860">"Zama ukushintsha inethiwekhi encanyelwayo. Thepha ukuze ushintshe."</string>
     <string name="EmergencyCallWarningTitle" msgid="1615688002899152860">"Ukushaya okuphuthumayo akutholakali"</string>
+    <!-- no translation found for emergency_calling_do_not_show_again (5034171343309733068) -->
+    <skip />
     <string name="EmergencyCallWarningSummary" msgid="9102799172089265268">"Amakholi aphuthumayo adinga inethiwekhi yeselula"</string>
     <string name="notification_channel_network_alert" msgid="4788053066033851841">"Izexwayiso"</string>
     <string name="notification_channel_call_forward" msgid="8230490317314272406">"Ukudlulisa ikholi"</string>
@@ -1778,6 +1780,14 @@
     <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"Imodi yesandla esisodwa"</string>
     <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"Ukufiphaza okwengeziwe"</string>
     <string name="hearing_aids_feature_name" msgid="1125892105105852542">"Amadivayizi okuzwa"</string>
+    <!-- no translation found for hearing_device_status_disconnected (497547752953543832) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_connected (2149385149669918764) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_active (4770378695482566032) -->
+    <skip />
+    <!-- no translation found for hearing_device_status_loading (5717083847663109747) -->
+    <skip />
     <string name="accessibility_shortcut_enabling_service" msgid="5473495203759847687">"Ubambe okhiye bevolumu. I-<xliff:g id="SERVICE_NAME">%1$s</xliff:g> ivuliwe."</string>
     <string name="accessibility_shortcut_disabling_service" msgid="8675244165062700619">"Ubambe okhiye bevolumu. I-<xliff:g id="SERVICE_NAME">%1$s</xliff:g> ivaliwe."</string>
     <string name="accessibility_shortcut_spoken_feedback" msgid="3760999147597564314">"Khipha okhiye bevolumu. Ukuze uvule i-<xliff:g id="SERVICE_NAME">%1$s</xliff:g>, cindezela bese ubamba bobabili okhiye bevolumu futhi imizuzwana emi-3."</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 728c856..8372aec 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -7572,25 +7572,31 @@
     <!-- NotificationProgressDrawable class -->
     <!-- ================================== -->
 
-    <!-- Drawable used to render a segmented bar, with segments and points. -->
+    <!-- Drawable used to render a notification progress bar, with segments and points. -->
     <!-- @hide internal use only -->
     <declare-styleable name="NotificationProgressDrawable">
-        <!-- Default color for the parts. -->
+        <!-- The gap between two segments. -->
         <attr name="segSegGap" format="dimension" />
+        <!-- The gap between a segment and a point. -->
         <attr name="segPointGap" format="dimension" />
     </declare-styleable>
 
     <!-- Used to config the segments of a NotificationProgressDrawable. -->
     <!-- @hide internal use only -->
     <declare-styleable name="NotificationProgressDrawableSegments">
-        <!-- Height of the solid segments -->
+        <!-- TODO: b/372908709 - maybe move this to NotificationProgressBar, because that's the only
+         place this is used actually. Same for NotificationProgressDrawable.segSegGap/segPointGap
+         above. -->
+        <!-- Minimum required drawing width. The drawing width refers to the width after
+         the original segments have been adjusted for the neighboring Points and gaps. This is
+         enforced by stretching the segments that are too short. -->
+        <attr name="minWidth" format="dimension" />
+        <!-- Height of the solid segments. -->
         <attr name="height" />
-        <!-- Height of the faded segments -->
-        <attr name="fadedHeight" format="dimension"/>
+        <!-- Height of the faded segments. -->
+        <attr name="fadedHeight" format="dimension" />
         <!-- Corner radius of the segment rect. -->
         <attr name="cornerRadius" format="dimension" />
-        <!-- Default color of the segment. -->
-        <attr name="color" />
     </declare-styleable>
 
     <!-- Used to config the points of a NotificationProgressDrawable. -->
@@ -7602,8 +7608,6 @@
         <attr name="inset" />
         <!-- Corner radius of the point rect. -->
         <attr name="cornerRadius"/>
-        <!-- Default color of the point rect. -->
-        <attr name="color" />
     </declare-styleable>
 
     <!-- ========================== -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 45a5d85..ce9a0c6 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2083,6 +2083,18 @@
          See com.android.server.timezonedetector.TimeZoneDetectorStrategy for more information. -->
     <bool name="config_supportTelephonyTimeZoneFallback" translatable="false">true</bool>
 
+    <!-- Whether the time notifications feature is enabled. Settings this to false means the feature
+         cannot be used. Setting this to true means the feature can be enabled on the device. -->
+    <bool name="config_enableTimeZoneNotificationsSupported" translatable="false">true</bool>
+
+    <!-- Whether the time zone notifications tracking feature is enabled. Settings this to false
+         means the feature cannot be used. -->
+    <bool name="config_enableTimeZoneNotificationsTrackingSupported" translatable="false">true</bool>
+
+    <!-- Whether the time zone manual change tracking feature is enabled. Settings this to false
+         means the feature cannot be used. -->
+    <bool name="config_enableTimeZoneManualChangeTrackingSupported" translatable="false">true</bool>
+
     <!-- Whether to enable network location overlay which allows network location provider to be
          replaced by an app at run-time. When disabled, only the
          config_networkLocationProviderPackageName package will be searched for network location
@@ -4244,12 +4256,19 @@
          is non-interactive. -->
     <bool name="config_cameraDoubleTapPowerGestureEnabled">true</bool>
 
-    <!-- Allow the gesture to double tap the power button to trigger a target action. -->
-    <bool name="config_doubleTapPowerGestureEnabled">true</bool>
-    <!-- Default target action for double tap of the power button gesture.
+    <!-- Controls the double tap power button gesture to trigger a target action.
+         0: Gesture is disabled
+         1: Launch camera mode, allowing the user to disable/enable the double tap power gesture
+            from launching the camera application.
+         2: Multi target mode, allowing the user to select one of the targets defined in
+            config_doubleTapPowerGestureMultiTargetDefaultAction and to disable/enable the double
+            tap power gesture from triggering the selected target action.
+    -->
+    <integer name="config_doubleTapPowerGestureMode">2</integer>
+    <!-- Default target action for double tap of the power button gesture in multi target mode.
          0: Launch camera
          1: Launch wallet -->
-    <integer name="config_defaultDoubleTapPowerGestureAction">0</integer>
+    <integer name="config_doubleTapPowerGestureMultiTargetDefaultAction">0</integer>
 
     <!-- Allow the gesture to quick tap the power button multiple times to start the emergency sos
          experience while the device is non-interactive. -->
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index a4735fe..d6b8704 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -408,6 +408,9 @@
     <!-- the padding of the expand icon in the notification header -->
     <dimen name="notification_2025_expand_button_horizontal_icon_padding">6dp</dimen>
 
+    <!-- a smaller padding for the end of the expand button, for use when showing the number -->
+    <dimen name="notification_2025_expand_button_reduced_end_padding">4dp</dimen>
+
     <!-- the size of the notification close button -->
     <dimen name="notification_close_button_size">16dp</dimen>
 
@@ -605,12 +608,23 @@
     <!-- Size of the feedback indicator for notifications -->
     <dimen name="notification_feedback_size">20dp</dimen>
 
+    <!-- Size of the (work) profile badge for notifications -->
+    <dimen name="notification_badge_size">12dp</dimen>
+
+    <!-- Size of the (work) profile badge for notifications (2025 redesign version).
+         Scales with font size. Chosen to look good alongside notification_subtext_size text. -->
+    <dimen name="notification_2025_badge_size">14sp</dimen>
+
+    <!-- Baseline for aligning icons in the top line (like the work profile icon or alerting icon)
+         to the text properly. This is equal to notification_2025_badge_size - 2sp. -->
+    <dimen name="notification_2025_badge_baseline">12sp</dimen>
+
+    <!-- Spacing for the top line icons (e.g. the work profile badge). -->
+    <dimen name="notification_2025_badge_margin">4dp</dimen>
+
     <!-- Size of the phishing alert for notifications -->
     <dimen name="notification_phishing_alert_size">@dimen/notification_badge_size</dimen>
 
-    <!-- Size of the profile badge for notifications -->
-    <dimen name="notification_badge_size">12dp</dimen>
-
     <!-- Size of the alerted icon for notifications -->
     <dimen name="notification_alerted_size">@dimen/notification_badge_size</dimen>
 
@@ -885,6 +899,8 @@
     <dimen name="notification_progress_segSeg_gap">4dp</dimen>
     <!-- The gap between a segment and a point in the notification progress bar -->
     <dimen name="notification_progress_segPoint_gap">4dp</dimen>
+    <!-- The minimum required drawing width of the notification progress bar segments -->
+    <dimen name="notification_progress_segments_min_width">16dp</dimen>
     <!-- The height of the notification progress bar segments -->
     <dimen name="notification_progress_segments_height">6dp</dimen>
     <!-- The height of the notification progress bar faded segments -->
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 33d3858..debc5e9 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -853,6 +853,9 @@
     <!-- Text shown when viewing channel settings for notifications related to vpn status -->
     <string name="notification_channel_vpn">VPN status</string>
 
+    <!-- Text shown when viewing channel settings for notifications related to system time -->
+    <string name="notification_channel_system_time">Time and time zones</string>
+
     <!-- Notification channel name. This channel sends high-priority alerts from the user's IT admin for key updates about the user's work device or work profile. -->
     <string name="notification_channel_device_admin">Alerts from your IT admin</string>
 
@@ -881,6 +884,10 @@
     <string name="notification_channel_accessibility_magnification">Magnification</string>
 
     <!-- Text shown when viewing channel settings for notifications related to accessibility
+     hearing device. [CHAR_LIMIT=NONE]-->
+    <string name="notification_channel_accessibility_hearing_device">Hearing device</string>
+
+    <!-- Text shown when viewing channel settings for notifications related to accessibility
          security policy. [CHAR_LIMIT=NONE]-->
     <string name="notification_channel_accessibility_security_policy">Accessibility usage</string>
 
@@ -3875,6 +3882,12 @@
     <string name="carrier_app_notification_title">New SIM inserted</string>
     <string name="carrier_app_notification_text">Tap to set it up</string>
 
+    <!-- Time zone notification strings -->
+    <!-- Title for time zone change notifications -->
+    <string name="time_zone_change_notification_title">Your time zone changed</string>
+    <!-- Body for time zone change notifications -->
+    <string name="time_zone_change_notification_body">You\'re now in <xliff:g id="time_zone_display_name">%1$s</xliff:g> (<xliff:g id="time_zone_offset">%2$s</xliff:g>)</string>
+
     <!-- Date/Time picker dialogs strings -->
 
     <!-- The title of the time picker dialog. [CHAR LIMIT=NONE] -->
@@ -4947,6 +4960,18 @@
     <!-- Title of hearing aids feature, shown in the warning dialog about the accessibility shortcut. [CHAR LIMIT=none] -->
     <string name="hearing_aids_feature_name">Hearing devices</string>
 
+    <!-- Text of hearing device disconnected state. [CHAR LIMIT=20] -->
+    <string name="hearing_device_status_disconnected">Disconnected</string>
+
+    <!-- Text of hearing device connected state. [CHAR LIMIT=20] -->
+    <string name="hearing_device_status_connected">Connected</string>
+
+    <!-- Text of hearing device active state. Active means device is currently connected and able to streaming the sound. [CHAR LIMIT=20] -->
+    <string name="hearing_device_status_active">Active</string>
+
+    <!-- Text of hearing device loading state. Loading means device is not available yet, it might be in connecting or disconnecting. [CHAR LIMIT=20] -->
+    <string name="hearing_device_status_loading">Loading</string>
+
     <!-- Text in toast to alert the user that the accessibility shortcut turned on an accessibility service. [CHAR LIMIT=none] -->
     <string name="accessibility_shortcut_enabling_service">Held volume keys. <xliff:g id="service_name" example="TalkBack">%1$s</xliff:g> turned on.</string>
 
@@ -4973,6 +4998,19 @@
     <!-- Text used to describe system navigation features, shown within a UI allowing a user to assign system magnification features to the Accessibility button in the navigation bar. -->
     <string name="accessibility_magnification_chooser_text">Magnification</string>
 
+    <!-- Notification title for switching input to the phone's microphone. It will be shown when making a phone call with the hearing device. [CHAR LIMIT=none] -->
+    <string name="hearing_device_switch_phone_mic_notification_title">Switch to phone mic?</string>
+    <!-- Notification title for switching input to the hearing device's microphone. It will be shown when making a phone call with the hearing device. [CHAR LIMIT=none] -->
+    <string name="hearing_device_switch_hearing_mic_notification_title">Switch to hearing aid mic?</string>
+    <!-- Notification content for switching input to the phone's microphone. It will be shown when making a phone call with the hearing device. [CHAR LIMIT=none] -->
+    <string name="hearing_device_switch_phone_mic_notification_text">For better sound or if your hearing aid battery is low. This only switches your mic during the call.</string>
+    <!-- Notification content for switching input to the hearing device's microphone. It will be shown when making a phone call with the hearing device. [CHAR LIMIT=none] -->
+    <string name="hearing_device_switch_hearing_mic_notification_text">You can use your hearing aid microphone for hands-free calling. This only switches your mic during the call.</string>
+    <!-- Notification action button. Click it will switch the input between phone's microphone and hearing device's microphone. It will be shown when making a phone call with the hearing device. [CHAR LIMIT=none] -->
+    <string name="hearing_device_notification_switch_button">Switch</string>
+    <!-- Notification action button. Click it will open the bluetooth device details page for this hearing device. It will be shown when making a phone call with the hearing device. [CHAR LIMIT=none] -->
+    <string name="hearing_device_notification_settings_button">Settings</string>
+
     <!-- Text spoken when the current user is switched if accessibility is enabled. [CHAR LIMIT=none] -->
     <string name="user_switched">Current user <xliff:g id="name" example="Bob">%1$s</xliff:g>.</string>
     <!-- Message shown when switching to a user [CHAR LIMIT=none] -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 4789624..6c014e9 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -575,6 +575,7 @@
   <java-symbol type="dimen" name="notification_top_pad_large_text" />
   <java-symbol type="dimen" name="notification_top_pad_large_text_narrow" />
   <java-symbol type="dimen" name="notification_badge_size" />
+  <java-symbol type="dimen" name="notification_2025_badge_size" />
   <java-symbol type="dimen" name="immersive_mode_cling_width" />
   <java-symbol type="dimen" name="accessibility_magnification_indicator_width" />
   <java-symbol type="dimen" name="circular_display_mask_thickness" />
@@ -2377,6 +2378,9 @@
   <java-symbol type="string" name="config_secondaryLocationTimeZoneProviderPackageName" />
   <java-symbol type="bool" name="config_enableTelephonyTimeZoneDetection" />
   <java-symbol type="bool" name="config_supportTelephonyTimeZoneFallback" />
+  <java-symbol type="bool" name="config_enableTimeZoneNotificationsSupported" />
+  <java-symbol type="bool" name="config_enableTimeZoneNotificationsTrackingSupported" />
+  <java-symbol type="bool" name="config_enableTimeZoneManualChangeTrackingSupported" />
   <java-symbol type="bool" name="config_autoResetAirplaneMode" />
   <java-symbol type="string" name="config_notificationAccessConfirmationActivity" />
   <java-symbol type="bool" name="config_preventImeStartupUnlessTextEditor" />
@@ -3167,8 +3171,8 @@
   <java-symbol type="integer" name="config_cameraLiftTriggerSensorType" />
   <java-symbol type="string" name="config_cameraLiftTriggerSensorStringType" />
   <java-symbol type="bool" name="config_cameraDoubleTapPowerGestureEnabled" />
-  <java-symbol type="bool" name="config_doubleTapPowerGestureEnabled" />
-  <java-symbol type="integer" name="config_defaultDoubleTapPowerGestureAction" />
+  <java-symbol type="integer" name="config_doubleTapPowerGestureMode" />
+  <java-symbol type="integer" name="config_doubleTapPowerGestureMultiTargetDefaultAction" />
   <java-symbol type="bool" name="config_emergencyGestureEnabled" />
   <java-symbol type="bool" name="config_defaultEmergencyGestureEnabled" />
   <java-symbol type="bool" name="config_defaultEmergencyGestureSoundEnabled" />
@@ -3247,6 +3251,8 @@
   <java-symbol type="dimen" name="notification_content_margin" />
   <java-symbol type="dimen" name="notification_2025_margin" />
   <java-symbol type="dimen" name="notification_2025_content_margin_top" />
+  <java-symbol type="dimen" name="notification_2025_expand_button_horizontal_icon_padding" />
+  <java-symbol type="dimen" name="notification_2025_expand_button_reduced_end_padding" />
   <java-symbol type="dimen" name="notification_progress_margin_horizontal" />
   <java-symbol type="dimen" name="notification_header_background_height" />
   <java-symbol type="dimen" name="notification_header_touchable_height" />
@@ -3821,14 +3827,29 @@
   <java-symbol type="drawable" name="ic_accessibility_color_correction" />
   <java-symbol type="drawable" name="ic_accessibility_generic" />
   <java-symbol type="drawable" name="ic_accessibility_hearing_aid" />
+  <java-symbol type="drawable" name="ic_accessibility_hearing_aid_disconnected" />
+  <java-symbol type="drawable" name="ic_accessibility_hearing_aid_green_dot" />
+  <java-symbol type="drawable" name="ic_accessibility_hearing_aid_blue_dot" />
   <java-symbol type="drawable" name="ic_accessibility_magnification" />
   <java-symbol type="drawable" name="ic_accessibility_reduce_bright_colors" />
   <java-symbol type="drawable" name="ic_accessibility_one_handed" />
 
+  <java-symbol type="string" name="hearing_device_status_disconnected" />
+  <java-symbol type="string" name="hearing_device_status_connected" />
+  <java-symbol type="string" name="hearing_device_status_active" />
+  <java-symbol type="string" name="hearing_device_status_loading" />
+
   <java-symbol type="string" name="hearing_aids_feature_name" />
   <java-symbol type="string" name="reduce_bright_colors_feature_name" />
   <java-symbol type="string" name="one_handed_mode_feature_name" />
 
+  <java-symbol type="string" name="hearing_device_switch_phone_mic_notification_title" />
+  <java-symbol type="string" name="hearing_device_switch_hearing_mic_notification_title" />
+  <java-symbol type="string" name="hearing_device_switch_phone_mic_notification_text" />
+  <java-symbol type="string" name="hearing_device_switch_hearing_mic_notification_text" />
+  <java-symbol type="string" name="hearing_device_notification_switch_button" />
+  <java-symbol type="string" name="hearing_device_notification_settings_button" />
+
   <!-- com.android.internal.widget.RecyclerView -->
   <java-symbol type="id" name="item_touch_helper_previous_elevation"/>
   <java-symbol type="dimen" name="item_touch_helper_max_drag_scroll_per_frame"/>
@@ -3929,6 +3950,7 @@
   <java-symbol type="dimen" name="notification_progress_tracker_height" />
   <java-symbol type="dimen" name="notification_progress_segSeg_gap" />
   <java-symbol type="dimen" name="notification_progress_segPoint_gap" />
+  <java-symbol type="dimen" name="notification_progress_segments_min_width" />
   <java-symbol type="dimen" name="notification_progress_segments_height" />
   <java-symbol type="dimen" name="notification_progress_segments_faded_height" />
   <java-symbol type="dimen" name="notification_progress_segments_corner_radius" />
@@ -4008,6 +4030,7 @@
   <java-symbol type="string" name="notification_channel_network_available" />
   <java-symbol type="array" name="config_defaultCloudSearchServices" />
   <java-symbol type="string" name="notification_channel_vpn" />
+  <java-symbol type="string" name="notification_channel_system_time" />
   <java-symbol type="string" name="notification_channel_device_admin" />
   <java-symbol type="string" name="notification_channel_alerts" />
   <java-symbol type="string" name="notification_channel_retail_mode" />
@@ -4015,8 +4038,11 @@
   <java-symbol type="string" name="notification_channel_heavy_weight_app" />
   <java-symbol type="string" name="notification_channel_system_changes" />
   <java-symbol type="string" name="notification_channel_accessibility_magnification" />
+  <java-symbol type="string" name="notification_channel_accessibility_hearing_device" />
   <java-symbol type="string" name="notification_channel_accessibility_security_policy" />
   <java-symbol type="string" name="notification_channel_display" />
+  <java-symbol type="string" name="time_zone_change_notification_title" />
+  <java-symbol type="string" name="time_zone_change_notification_body" />
   <java-symbol type="string" name="config_defaultAutofillService" />
   <java-symbol type="string" name="config_defaultFieldClassificationService" />
   <java-symbol type="string" name="config_defaultOnDeviceSpeechRecognitionService" />
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index bb5380e..06cd44e 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -34,7 +34,7 @@
          http://smscoin.net/software/engine/WordPress/Paid+SMS-registration/ -->
 
     <!-- Arab Emirates -->
-    <shortcode country="ae" pattern="\\d{1,5}" free="1017|1355|3214|6253|6568" />
+    <shortcode country="ae" pattern="\\d{1,5}" free="1017|1355|3214|6253|6568|999" />
 
     <!-- Albania: 5 digits, known short codes listed -->
     <shortcode country="al" pattern="\\d{5}" premium="15191|55[56]00" />
@@ -63,8 +63,8 @@
     <!-- Burkina Faso: 1-4 digits (standard system default, not country specific) -->
     <shortcode country="bf" pattern="\\d{1,4}" free="3558" />
 
-    <!-- Bulgaria: 4-5 digits, plus EU -->
-    <shortcode country="bg" pattern="\\d{4,5}" premium="18(?:16|423)|19(?:1[56]|35)" free="116\\d{3}|1988|1490" />
+    <!-- Bulgaria: 4-6 digits, plus EU -->
+    <shortcode country="bg" pattern="\\d{4,6}" premium="18(?:16|423)|19(?:1[56]|35)" free="116\\d{3}|1988|1490|162055" />
 
     <!-- Bahrain: 1-5 digits (standard system default, not country specific) -->
     <shortcode country="bh" pattern="\\d{1,5}" free="81181|85999" />
@@ -81,18 +81,21 @@
     <!-- Canada: 5-6 digits -->
     <shortcode country="ca" pattern="\\d{5,6}" premium="60999|88188|43030" standard="244444" free="455677|24470" />
 
+    <!-- DR Congo: 1-6 digits, known premium codes listed -->
+    <shortcode country="cd" pattern="\\d{1,6}" free="444123" />
+
     <!-- Switzerland: 3-5 digits: http://www.swisscom.ch/fxres/kmu/thirdpartybusiness_code_of_conduct_en.pdf -->
     <shortcode country="ch" pattern="[2-9]\\d{2,4}" premium="543|83111|30118" free="98765|30075|30047" />
 
     <!-- Chile: 4-5 digits (not confirmed), known premium codes listed -->
-    <shortcode country="cl" pattern="\\d{4,5}" free="9963|9240|1038" />
+    <shortcode country="cl" pattern="\\d{4,5}" free="9963|9240|1038|4848" />
 
     <!-- China: premium shortcodes start with "1066", free shortcodes start with "1065":
          http://clients.txtnation.com/entries/197192-china-premium-sms-short-code-requirements -->
     <shortcode country="cn" premium="1066.*" free="1065.*" />
 
     <!-- Colombia: 1-6 digits (not confirmed) -->
-    <shortcode country="co" pattern="\\d{1,6}" free="890350|908160|892255|898002|898880|899960|899948|87739|85517|491289" />
+    <shortcode country="co" pattern="\\d{1,6}" free="890350|908160|892255|898002|898880|899960|899948|87739|85517|491289|890119" />
 
     <!-- Costa Rica  -->
     <shortcode country="cr" pattern="\\d{1,6}" free="466453" />
@@ -116,6 +119,9 @@
     <!-- Dominican Republic: 1-6 digits (standard system default, not country specific) -->
     <shortcode country="do" pattern="\\d{1,6}" free="912892|912" />
 
+    <!-- Algeria: 1-5 digits, known premium codes listed -->
+    <shortcode country="dz" pattern="\\d{1,5}" free="63071" />
+
     <!-- Ecuador: 1-6 digits (standard system default, not country specific) -->
     <shortcode country="ec" pattern="\\d{1,6}" free="466453|18512" />
 
@@ -123,20 +129,23 @@
          http://www.tja.ee/public/documents/Elektrooniline_side/Oigusaktid/ENG/Estonian_Numbering_Plan_annex_06_09_2010.mht -->
     <shortcode country="ee" pattern="1\\d{2,4}" premium="90\\d{5}|15330|1701[0-3]" free="116\\d{3}|95034" />
 
-    <!-- Egypt: 4-5 digits, known codes listed -->
-    <shortcode country="eg" pattern="\\d{4,5}" free="1499|10020" />
+    <!-- Egypt: 4-6 digits, known codes listed -->
+    <shortcode country="eg" pattern="\\d{4,6}" free="1499|10020|100158" />
 
     <!-- Spain: 5-6 digits: 25xxx, 27xxx, 280xx, 35xxx, 37xxx, 795xxx, 797xxx, 995xxx, 997xxx, plus EU.
          http://www.legallink.es/?q=en/content/which-current-regulatory-status-premium-rate-services-spain -->
     <shortcode country="es" premium="[23][57]\\d{3}|280\\d{2}|[79]9[57]\\d{3}" free="116\\d{3}|22791|222145|22189" />
 
+    <!-- Ethiopia: 1-4 digits, known codes listed -->
+    <shortcode country="et" pattern="\\d{1,4}" free="8527" />
+
     <!-- Finland: 5-6 digits, premium 0600, 0700: http://en.wikipedia.org/wiki/Telephone_numbers_in_Finland -->
     <shortcode country="fi" pattern="\\d{5,6}" premium="0600.*|0700.*|171(?:59|63)" free="116\\d{3}|14789|17110" />
 
     <!-- France: 5 digits, free: 3xxxx, premium [4-8]xxxx, plus EU:
          http://clients.txtnation.com/entries/161972-france-premium-sms-short-code-requirements,
          visual voicemail code for Orange: 21101 -->
-    <shortcode country="fr" premium="[4-8]\\d{4}" free="3\\d{4}|116\\d{3}|21101|20366|555|2051|33033" />
+    <shortcode country="fr" premium="[4-8]\\d{4}" free="3\\d{4}|116\\d{3}|21101|20366|555|2051|33033|21727" />
 
     <!-- United Kingdom (Great Britain): 4-6 digits, common codes [5-8]xxxx, plus EU:
          http://www.short-codes.com/media/Co-regulatoryCodeofPracticeforcommonshortcodes170206.pdf,
@@ -179,17 +188,17 @@
     <shortcode country="il" pattern="\\d{1,5}" premium="4422|4545" free="37477|6681" />
 
     <!-- Iran: 4-8 digits, known premium codes listed -->
-    <shortcode country="ir" pattern="\\d{4,8}" free="700791|700792|100016|30008360" />
+    <shortcode country="ir" pattern="\\d{4,8}" free="700792|100016|30008360" />
 
     <!-- Italy: 5 digits (premium=41xxx,42xxx), plus EU:
          https://www.itu.int/dms_pub/itu-t/oth/02/02/T020200006B0001PDFE.pdf -->
     <shortcode country="it" pattern="\\d{5}" premium="44[0-4]\\d{2}|47[0-4]\\d{2}|48[0-4]\\d{2}|44[5-9]\\d{4}|47[5-9]\\d{4}|48[5-9]\\d{4}|455\\d{2}|499\\d{2}" free="116\\d{3}|4112503|40\\d{0,12}" standard="430\\d{2}|431\\d{2}|434\\d{4}|435\\d{4}|439\\d{7}" />
 
     <!-- Jordan: 1-5 digits (standard system default, not country specific) -->
-    <shortcode country="jo" pattern="\\d{1,5}" free="99066" />
+    <shortcode country="jo" pattern="\\d{1,5}" free="99066|99390" />
 
     <!-- Japan: 8083 used by SOFTBANK_DCB_2 -->
-    <shortcode country="jp" pattern="\\d{1,5}" free="8083" />
+    <shortcode country="jp" pattern="\\d{1,9}" free="8083|00050320" />
 
     <!-- Kenya: 5 digits, known premium codes listed -->
     <shortcode country="ke" pattern="\\d{5}" free="21725|21562|40520|23342|40023|24088|23054" />
@@ -206,6 +215,9 @@
     <!-- Kuwait: 1-5 digits (standard system default, not country specific) -->
     <shortcode country="kw" pattern="\\d{1,5}" free="1378|50420|94006|55991|50976|7112" />
 
+    <!-- Lesotho: 4-5 digits, known codes listed -->
+    <shortcode country="ls" pattern="\\d{4,5}" free="32012" />
+
     <!-- Lithuania: 3-5 digits, known premium codes listed, plus EU -->
     <shortcode country="lt" pattern="\\d{3,5}" premium="13[89]1|1394|16[34]5" free="116\\d{3}|1399|1324" />
 
@@ -222,11 +234,14 @@
     <!-- Macedonia: 1-6 digits (not confirmed), known premium codes listed -->
     <shortcode country="mk" pattern="\\d{1,6}" free="129005|122" />
 
+    <!-- Mali: 1-5 digits, known codes listed -->
+    <shortcode country="ml" pattern="\\d{1,5}" free="36098" />
+
     <!-- Mongolia : 1-6 digits (standard system default, not country specific) -->
     <shortcode country="mn" pattern="\\d{1,6}" free="44444|45678|445566" />
 
     <!-- Malawi: 1-5 digits (standard system default, not country specific) -->
-    <shortcode country="mw" pattern="\\d{1,5}" free="4276|4305" />
+    <shortcode country="mw" pattern="\\d{1,5}" free="4276|4305|4326" />
 
     <!-- Mozambique: 1-5 digits (standard system default, not country specific) -->
     <shortcode country="mz" pattern="\\d{1,5}" free="1714" />
@@ -323,11 +338,14 @@
     <!-- Tajikistan: 4 digits, known premium codes listed -->
     <shortcode country="tj" pattern="\\d{4}" premium="11[3-7]1|4161|4333|444[689]" />
 
-    <!-- Tanzania: 1-5 digits (standard system default, not country specific) -->
-    <shortcode country="tz" pattern="\\d{1,5}" free="15046|15234|15324|15610" />
+    <!-- Timor-Leste 1-5 digits, known codes listed  -->
+    <shortcode country="tl" pattern="\\d{1,5}" free="46645" />
 
-    <!-- Tunisia: 5 digits, known premium codes listed -->
-    <shortcode country="tn" pattern="\\d{5}" free="85799" />
+    <!-- Tanzania: 1-5 digits (standard system default, not country specific) -->
+    <shortcode country="tz" pattern="\\d{1,5}" free="15046|15324|15610" />
+
+    <!-- Tunisia: 1-6 digits, known premium codes listed -->
+    <shortcode country="tn" pattern="\\d{1,6}" free="85799|772024" />
 
     <!-- Turkey -->
     <shortcode country="tr" pattern="\\d{1,5}" free="7529|5528|6493|3193" />
@@ -336,7 +354,7 @@
     <shortcode country="ua" pattern="\\d{4}" premium="444[3-9]|70[579]4|7540" />
 
     <!-- Uganda(UG): 4 digits (standard system default, not country specific) -->
-    <shortcode country="ug" pattern="\\d{4}" free="8000|8009" />
+    <shortcode country="ug" pattern="\\d{4}" free="8009" />
 
     <!-- USA: 5-6 digits (premium codes from https://www.premiumsmsrefunds.com/ShortCodes.htm),
          visual voicemail code for T-Mobile: 122 -->
@@ -349,7 +367,7 @@
     <shortcode country="ve" pattern="\\d{1,6}" free="538352" />
 
     <!-- Vietnam: 1-6 digits (standard system default, not country specific) -->
-    <shortcode country="vn" pattern="\\d{1,6}" free="5001|9055|8079|90002|118989" />
+    <shortcode country="vn" pattern="\\d{1,6}" free="5001|9055|90002|118989|46645" />
 
     <!-- Mayotte (French Territory): 1-5 digits (not confirmed) -->
     <shortcode country="yt" pattern="\\d{1,5}" free="38600,36300,36303,959" />
diff --git a/core/tests/coretests/Android.bp b/core/tests/coretests/Android.bp
index 3ef3dfd..1b6746c 100644
--- a/core/tests/coretests/Android.bp
+++ b/core/tests/coretests/Android.bp
@@ -69,10 +69,12 @@
         "frameworks-base-testutils",
         "core-test-rules", // for libcore.dalvik.system.CloseGuardSupport
         "core-tests-support",
+        "cts-input-lib",
         "android-common",
         "frameworks-core-util-lib",
         "mockwebserver",
         "guava",
+        "guava-android-testlib",
         "android.app.usage.flags-aconfig-java",
         "android.view.accessibility.flags-aconfig-java",
         "androidx.core_core",
@@ -106,6 +108,7 @@
         "TestParameterInjector",
         "android.content.res.flags-aconfig-java",
         "android.security.flags-aconfig-java",
+        "mockito-kotlin2",
     ],
 
     libs: [
@@ -310,6 +313,7 @@
         "res/xml/power_profile_test_modem.xml",
     ],
     auto_gen_config: true,
+    team: "trendy_team_ravenwood",
 }
 
 test_module_config {
diff --git a/core/tests/coretests/src/android/app/BackgroundStartPrivilegesTest.java b/core/tests/coretests/src/android/app/BackgroundStartPrivilegesTest.java
index 931d646..f1925bb 100644
--- a/core/tests/coretests/src/android/app/BackgroundStartPrivilegesTest.java
+++ b/core/tests/coretests/src/android/app/BackgroundStartPrivilegesTest.java
@@ -29,6 +29,8 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
+import com.google.common.testing.EqualsTester;
+
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -122,12 +124,16 @@
 
     @Test
     public void backgroundStartPrivilege_equals_works() {
-        assertThat(NONE).isEqualTo(NONE);
-        assertThat(ALLOW_BAL).isEqualTo(ALLOW_BAL);
-        assertThat(ALLOW_FGS).isEqualTo(ALLOW_FGS);
-        assertThat(BSP_ALLOW_A).isEqualTo(BSP_ALLOW_A);
-        assertThat(NONE).isNotEqualTo(ALLOW_BAL);
-        assertThat(ALLOW_FGS).isNotEqualTo(ALLOW_BAL);
-        assertThat(BSP_ALLOW_A).isNotEqualTo(BSP_ALLOW_B);
+        Binder token = new Binder();
+        Binder anotherToken = new Binder();
+        new EqualsTester()
+                .addEqualityGroup(NONE)
+                .addEqualityGroup(ALLOW_BAL)
+                .addEqualityGroup(ALLOW_FGS)
+                .addEqualityGroup(BackgroundStartPrivileges.allowBackgroundActivityStarts(token),
+                        BackgroundStartPrivileges.allowBackgroundActivityStarts(token))
+                .addEqualityGroup(
+                        BackgroundStartPrivileges.allowBackgroundActivityStarts(anotherToken))
+                .testEquals();
     }
 }
diff --git a/core/tests/coretests/src/android/app/NotificationManagerTest.java b/core/tests/coretests/src/android/app/NotificationManagerTest.java
index 6538ce8..3d6e122 100644
--- a/core/tests/coretests/src/android/app/NotificationManagerTest.java
+++ b/core/tests/coretests/src/android/app/NotificationManagerTest.java
@@ -16,6 +16,8 @@
 
 package android.app;
 
+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;
@@ -25,8 +27,12 @@
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.pm.ParceledListSlice;
+import android.os.UserHandle;
 import android.platform.test.annotations.EnableFlags;
 import android.platform.test.annotations.Presubmit;
 import android.platform.test.flag.junit.SetFlagsRule;
@@ -35,6 +41,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -42,6 +49,7 @@
 
 import java.time.Instant;
 import java.time.InstantSource;
+import java.util.List;
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
@@ -50,14 +58,24 @@
     @Rule
     public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
 
-    private Context mContext;
     private NotificationManagerWithMockService mNotificationManager;
     private final FakeClock mClock = new FakeClock();
 
+    private PackageTestableContext mContext;
+
     @Before
     public void setUp() {
-        mContext = ApplicationProvider.getApplicationContext();
+        mContext = new PackageTestableContext(ApplicationProvider.getApplicationContext());
         mNotificationManager = new NotificationManagerWithMockService(mContext, mClock);
+
+        // Caches must be in test mode in order to be used in tests.
+        PropertyInvalidatedCache.setTestMode(true);
+        mNotificationManager.setChannelCacheToTestMode();
+    }
+
+    @After
+    public void tearDown() {
+        PropertyInvalidatedCache.setTestMode(false);
     }
 
     @Test
@@ -243,12 +261,161 @@
                 anyInt(), any(), anyInt());
     }
 
+    @Test
+    @EnableFlags(Flags.FLAG_NM_BINDER_PERF_CACHE_CHANNELS)
+    public void getNotificationChannel_cachedUntilInvalidated() throws Exception {
+        // Invalidate the cache first because the cache won't do anything until then
+        NotificationManager.invalidateNotificationChannelCache();
+
+        // It doesn't matter what the returned contents are, as long as we return a channel.
+        // This setup must set up getNotificationChannels(), as that's the method called.
+        when(mNotificationManager.mBackendService.getNotificationChannels(any(), any(),
+                anyInt())).thenReturn(new ParceledListSlice<>(List.of(exampleChannel())));
+
+        // ask for the same channel 100 times without invalidating the cache
+        for (int i = 0; i < 100; i++) {
+            NotificationChannel unused = mNotificationManager.getNotificationChannel("id");
+        }
+
+        // invalidate the cache; then ask again
+        NotificationManager.invalidateNotificationChannelCache();
+        NotificationChannel unused = mNotificationManager.getNotificationChannel("id");
+
+        verify(mNotificationManager.mBackendService, times(2))
+                .getNotificationChannels(any(), any(), anyInt());
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_NM_BINDER_PERF_CACHE_CHANNELS)
+    public void getNotificationChannel_sameApp_oneCall() throws Exception {
+        NotificationManager.invalidateNotificationChannelCache();
+
+        NotificationChannel c1 = new NotificationChannel("id1", "name1",
+                NotificationManager.IMPORTANCE_DEFAULT);
+        NotificationChannel c2 = new NotificationChannel("id2", "name2",
+                NotificationManager.IMPORTANCE_NONE);
+
+        when(mNotificationManager.mBackendService.getNotificationChannels(any(), any(),
+                anyInt())).thenReturn(new ParceledListSlice<>(List.of(c1, c2)));
+
+        assertThat(mNotificationManager.getNotificationChannel("id1")).isEqualTo(c1);
+        assertThat(mNotificationManager.getNotificationChannel("id2")).isEqualTo(c2);
+        assertThat(mNotificationManager.getNotificationChannel("id3")).isNull();
+
+        verify(mNotificationManager.mBackendService, times(1))
+                .getNotificationChannels(any(), any(), anyInt());
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_NM_BINDER_PERF_CACHE_CHANNELS)
+    public void getNotificationChannels_cachedUntilInvalidated() throws Exception {
+        NotificationManager.invalidateNotificationChannelCache();
+        when(mNotificationManager.mBackendService.getNotificationChannels(any(), any(),
+                anyInt())).thenReturn(new ParceledListSlice<>(List.of(exampleChannel())));
+
+        // ask for channels 100 times without invalidating the cache
+        for (int i = 0; i < 100; i++) {
+            List<NotificationChannel> unused = mNotificationManager.getNotificationChannels();
+        }
+
+        // invalidate the cache; then ask again
+        NotificationManager.invalidateNotificationChannelCache();
+        List<NotificationChannel> res = mNotificationManager.getNotificationChannels();
+
+        verify(mNotificationManager.mBackendService, times(2))
+                .getNotificationChannels(any(), any(), anyInt());
+        assertThat(res).containsExactlyElementsIn(List.of(exampleChannel()));
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_NM_BINDER_PERF_CACHE_CHANNELS)
+    public void getNotificationChannel_channelAndConversationLookup() throws Exception {
+        NotificationManager.invalidateNotificationChannelCache();
+
+        // Full list of channels: c1; conv1 = child of c1; c2 is unrelated
+        NotificationChannel c1 = new NotificationChannel("id", "name",
+                NotificationManager.IMPORTANCE_DEFAULT);
+        NotificationChannel conv1 = new NotificationChannel("", "name_conversation",
+                NotificationManager.IMPORTANCE_DEFAULT);
+        conv1.setConversationId("id", "id_conversation");
+        NotificationChannel c2 = new NotificationChannel("other", "name2",
+                NotificationManager.IMPORTANCE_DEFAULT);
+
+        when(mNotificationManager.mBackendService.getNotificationChannels(any(), any(), anyInt()))
+                .thenReturn(new ParceledListSlice<>(List.of(c1, conv1, c2)));
+
+        // Lookup for channel c1 and c2: returned as expected
+        assertThat(mNotificationManager.getNotificationChannel("id")).isEqualTo(c1);
+        assertThat(mNotificationManager.getNotificationChannel("other")).isEqualTo(c2);
+
+        // Lookup for conv1 should return conv1
+        assertThat(mNotificationManager.getNotificationChannel("id", "id_conversation")).isEqualTo(
+                conv1);
+
+        // Lookup for a different conversation channel that doesn't exist, whose parent channel id
+        // is "id", should return c1
+        assertThat(mNotificationManager.getNotificationChannel("id", "nonexistent")).isEqualTo(c1);
+
+        // Lookup of a nonexistent channel is null
+        assertThat(mNotificationManager.getNotificationChannel("id3")).isNull();
+
+        // All of that should have been one call to getNotificationChannels()
+        verify(mNotificationManager.mBackendService, times(1))
+                .getNotificationChannels(any(), any(), anyInt());
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_NM_BINDER_PERF_CACHE_CHANNELS)
+    public void getNotificationChannel_differentPackages() throws Exception {
+        NotificationManager.invalidateNotificationChannelCache();
+        final String pkg1 = "one";
+        final String pkg2 = "two";
+        final int userId = 0;
+        final int userId1 = 1;
+
+        // multiple channels with the same ID, but belonging to different packages/users
+        NotificationChannel channel1 = new NotificationChannel("id", "name1",
+                NotificationManager.IMPORTANCE_DEFAULT);
+        NotificationChannel channel2 = channel1.copy();
+        channel2.setName("name2");
+        NotificationChannel channel3 = channel1.copy();
+        channel3.setName("name3");
+
+        when(mNotificationManager.mBackendService.getNotificationChannels(any(), eq(pkg1),
+                eq(userId))).thenReturn(new ParceledListSlice<>(List.of(channel1)));
+        when(mNotificationManager.mBackendService.getNotificationChannels(any(), eq(pkg2),
+                eq(userId))).thenReturn(new ParceledListSlice<>(List.of(channel2)));
+        when(mNotificationManager.mBackendService.getNotificationChannels(any(), eq(pkg1),
+                eq(userId1))).thenReturn(new ParceledListSlice<>(List.of(channel3)));
+
+        // set our context to pretend to be from package 1 and userId 0
+        mContext.setParameters(pkg1, pkg1, userId);
+        assertThat(mNotificationManager.getNotificationChannel("id")).isEqualTo(channel1);
+
+        // now package 2
+        mContext.setParameters(pkg2, pkg2, userId);
+        assertThat(mNotificationManager.getNotificationChannel("id")).isEqualTo(channel2);
+
+        // now pkg1 for a different user
+        mContext.setParameters(pkg1, pkg1, userId1);
+        assertThat(mNotificationManager.getNotificationChannel("id")).isEqualTo(channel3);
+
+        // Those should have been three different calls
+        verify(mNotificationManager.mBackendService, times(3))
+                .getNotificationChannels(any(), any(), anyInt());
+    }
+
     private Notification exampleNotification() {
         return new Notification.Builder(mContext, "channel")
                 .setSmallIcon(android.R.drawable.star_big_on)
                 .build();
     }
 
+    private NotificationChannel exampleChannel() {
+        return new NotificationChannel("id", "channel_name",
+                NotificationManager.IMPORTANCE_DEFAULT);
+    }
+
     private static class NotificationManagerWithMockService extends NotificationManager {
 
         private final INotificationManager mBackendService;
@@ -264,6 +431,48 @@
         }
     }
 
+    // Helper context wrapper class where we can control just the return values of getPackageName,
+    // getOpPackageName, and getUserId (used in getNotificationChannels).
+    private static class PackageTestableContext extends ContextWrapper {
+        private String mPackage;
+        private String mOpPackage;
+        private Integer mUserId;
+
+        PackageTestableContext(Context base) {
+            super(base);
+        }
+
+        void setParameters(String packageName, String opPackageName, int userId) {
+            mPackage = packageName;
+            mOpPackage = opPackageName;
+            mUserId = userId;
+        }
+
+        @Override
+        public String getPackageName() {
+            if (mPackage != null) return mPackage;
+            return super.getPackageName();
+        }
+
+        @Override
+        public String getOpPackageName() {
+            if (mOpPackage != null) return mOpPackage;
+            return super.getOpPackageName();
+        }
+
+        @Override
+        public int getUserId() {
+            if (mUserId != null) return mUserId;
+            return super.getUserId();
+        }
+
+        @Override
+        public UserHandle getUser() {
+            if (mUserId != null) return UserHandle.of(mUserId);
+            return super.getUser();
+        }
+    }
+
     private static class FakeClock implements InstantSource {
 
         private long mNowMillis = 441644400000L;
diff --git a/core/tests/coretests/src/android/app/NotificationTest.java b/core/tests/coretests/src/android/app/NotificationTest.java
index ca6ad6f..7be6950 100644
--- a/core/tests/coretests/src/android/app/NotificationTest.java
+++ b/core/tests/coretests/src/android/app/NotificationTest.java
@@ -2504,6 +2504,21 @@
 
     @Test
     @EnableFlags(Flags.FLAG_API_RICH_ONGOING)
+    public void progressStyle_setProgressSegments() {
+        final List<Notification.ProgressStyle.Segment> segments = List.of(
+                new Notification.ProgressStyle.Segment(100).setColor(Color.WHITE),
+                new Notification.ProgressStyle.Segment(50).setColor(Color.RED),
+                new Notification.ProgressStyle.Segment(50).setColor(Color.BLUE)
+        );
+
+        final Notification.ProgressStyle progressStyle1 = new Notification.ProgressStyle();
+        progressStyle1.setProgressSegments(segments);
+
+        assertThat(progressStyle1.getProgressSegments()).isEqualTo(segments);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_API_RICH_ONGOING)
     public void progressStyle_addProgressPoint_dropsNegativePoints() {
         // GIVEN
         final Notification.ProgressStyle progressStyle = new Notification.ProgressStyle();
@@ -2532,6 +2547,21 @@
 
     @Test
     @EnableFlags(Flags.FLAG_API_RICH_ONGOING)
+    public void progressStyle_setProgressPoints() {
+        final List<Notification.ProgressStyle.Point> points = List.of(
+                new Notification.ProgressStyle.Point(0).setColor(Color.WHITE),
+                new Notification.ProgressStyle.Point(50).setColor(Color.RED),
+                new Notification.ProgressStyle.Point(100).setColor(Color.BLUE)
+        );
+
+        final Notification.ProgressStyle progressStyle1 = new Notification.ProgressStyle();
+        progressStyle1.setProgressPoints(points);
+
+        assertThat(progressStyle1.getProgressPoints()).isEqualTo(points);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_API_RICH_ONGOING)
     public void progressStyle_createProgressModel_ignoresPointsExceedingMax() {
         // GIVEN
         final Notification.ProgressStyle progressStyle = new Notification.ProgressStyle();
@@ -2673,11 +2703,58 @@
 
     @Test
     @EnableFlags(Flags.FLAG_API_RICH_ONGOING)
+    public void progressStyle_setProgressIndeterminate() {
+        final Notification.ProgressStyle progressStyle1 = new Notification.ProgressStyle();
+        progressStyle1.setProgressIndeterminate(true);
+        assertThat(progressStyle1.isProgressIndeterminate()).isTrue();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_API_RICH_ONGOING)
     public void progressStyle_styledByProgress_defaultValueTrue() {
         final Notification.ProgressStyle progressStyle1 = new Notification.ProgressStyle();
 
         assertThat(progressStyle1.isStyledByProgress()).isTrue();
     }
+
+    @Test
+    @EnableFlags(Flags.FLAG_API_RICH_ONGOING)
+    public void progressStyle_setStyledByProgress() {
+        final Notification.ProgressStyle progressStyle1 = new Notification.ProgressStyle();
+        progressStyle1.setStyledByProgress(false);
+        assertThat(progressStyle1.isStyledByProgress()).isFalse();
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_API_RICH_ONGOING)
+    public void progressStyle_point() {
+        final int id = 1;
+        final int position = 10;
+        final int color = Color.RED;
+
+        final Notification.ProgressStyle.Point point =
+                new Notification.ProgressStyle.Point(position).setId(id).setColor(color);
+
+        assertEquals(id, point.getId());
+        assertEquals(position, point.getPosition());
+        assertEquals(color, point.getColor());
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_API_RICH_ONGOING)
+    public void progressStyle_segment() {
+        final int id = 1;
+        final int length = 100;
+        final int color = Color.RED;
+
+        final Notification.ProgressStyle.Segment segment =
+                new Notification.ProgressStyle.Segment(length).setId(id).setColor(color);
+
+        assertEquals(id, segment.getId());
+        assertEquals(length, segment.getLength());
+        assertEquals(color, segment.getColor());
+    }
+
     private void assertValid(Notification.Colors c) {
         // Assert that all colors are populated
         assertThat(c.getBackgroundColor()).isNotEqualTo(Notification.COLOR_INVALID);
diff --git a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
index e9dfdd8..5da2564 100644
--- a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
+++ b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
@@ -194,38 +194,38 @@
                         new ServerQuery(tester));
 
         // Caches are enabled upon creation.
-        assertEquals(false, cache1.getDisabledState());
-        assertEquals(false, cache2.getDisabledState());
-        assertEquals(false, cache3.getDisabledState());
+        assertFalse(cache1.isDisabled());
+        assertFalse(cache2.isDisabled());
+        assertFalse(cache3.isDisabled());
 
         // Disable the cache1 instance.  Only cache1 is disabled
         cache1.disableInstance();
-        assertEquals(true, cache1.getDisabledState());
-        assertEquals(false, cache2.getDisabledState());
-        assertEquals(false, cache3.getDisabledState());
+        assertTrue(cache1.isDisabled());
+        assertFalse(cache2.isDisabled());
+        assertFalse(cache3.isDisabled());
 
         // Disable cache1.  This will disable cache1 and cache2 because they share the
         // same name.  cache3 has a different name and will not be disabled.
         cache1.disableLocal();
-        assertEquals(true, cache1.getDisabledState());
-        assertEquals(true, cache2.getDisabledState());
-        assertEquals(false, cache3.getDisabledState());
+        assertTrue(cache1.isDisabled());
+        assertTrue(cache2.isDisabled());
+        assertFalse(cache3.isDisabled());
 
         // Create a new cache1.  Verify that the new instance is disabled.
         cache1 = new PropertyInvalidatedCache<>(4, MODULE, API, "cache1",
                 new ServerQuery(tester));
-        assertEquals(true, cache1.getDisabledState());
+        assertTrue(cache1.isDisabled());
 
         // Remove the record of caches being locally disabled.  This is a clean-up step.
         cache1.forgetDisableLocal();
-        assertEquals(true, cache1.getDisabledState());
-        assertEquals(true, cache2.getDisabledState());
-        assertEquals(false, cache3.getDisabledState());
+        assertTrue(cache1.isDisabled());
+        assertTrue(cache2.isDisabled());
+        assertFalse(cache3.isDisabled());
 
         // Create a new cache1.  Verify that the new instance is not disabled.
         cache1 = new PropertyInvalidatedCache<>(4, MODULE, API, "cache1",
                 new ServerQuery(tester));
-        assertEquals(false, cache1.getDisabledState());
+        assertFalse(cache1.isDisabled());
     }
 
     private static class TestQuery
@@ -280,7 +280,7 @@
     public void testCacheRecompute() {
         TestCache cache = new TestCache();
         cache.invalidateCache();
-        assertEquals(cache.isDisabled(), false);
+        assertFalse(cache.isDisabled());
         assertEquals("foo5", cache.query(5));
         assertEquals(1, cache.getRecomputeCount());
         assertEquals("foo5", cache.query(5));
@@ -383,15 +383,15 @@
     @Test
     public void testLocalProcessDisable() {
         TestCache cache = new TestCache();
-        assertEquals(cache.isDisabled(), false);
+        assertFalse(cache.isDisabled());
         cache.invalidateCache();
         assertEquals("foo5", cache.query(5));
         assertEquals(1, cache.getRecomputeCount());
         assertEquals("foo5", cache.query(5));
         assertEquals(1, cache.getRecomputeCount());
-        assertEquals(cache.isDisabled(), false);
+        assertFalse(cache.isDisabled());
         cache.disableLocal();
-        assertEquals(cache.isDisabled(), true);
+        assertTrue(cache.isDisabled());
         assertEquals("foo5", cache.query(5));
         assertEquals("foo5", cache.query(5));
         assertEquals(3, cache.getRecomputeCount());
diff --git a/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java
index 37ef6cb..939bf2e 100644
--- a/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java
+++ b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheTest.java
@@ -207,7 +207,8 @@
         final ComponentInfo info = new ComponentInfo();
         info.applicationInfo = new ApplicationInfo();
         info.applicationInfo.uid = uid;
-        return new RegisteredServicesCache.ServiceInfo<>(type, info, null);
+        return new RegisteredServicesCache.ServiceInfo<>(type, info, null /* componentName */,
+                0 /* lastUpdateTime */);
     }
 
     private void assertNotEmptyFileCreated(TestServicesCache cache, int userId) {
@@ -301,7 +302,7 @@
 
         @Override
         protected ServiceInfo<TestServiceType> parseServiceInfo(
-                ResolveInfo resolveInfo) throws XmlPullParserException, IOException {
+                ResolveInfo resolveInfo, int userId) throws XmlPullParserException, IOException {
             int size = mServices.size();
             for (int i = 0; i < size; i++) {
                 Map<ResolveInfo, ServiceInfo<TestServiceType>> map = mServices.valueAt(i);
diff --git a/core/tests/coretests/src/android/content/pm/SystemFeaturesCacheTest.java b/core/tests/coretests/src/android/content/pm/SystemFeaturesCacheTest.java
new file mode 100644
index 0000000..ce4aa42
--- /dev/null
+++ b/core/tests/coretests/src/android/content/pm/SystemFeaturesCacheTest.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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;
+
+import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
+import static android.content.pm.PackageManager.FEATURE_WATCH;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Parcel;
+import android.util.ArrayMap;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class SystemFeaturesCacheTest {
+
+    private SystemFeaturesCache mCache;
+
+    @Test
+    public void testNoFeatures() throws Exception {
+        SystemFeaturesCache cache = new SystemFeaturesCache(new ArrayMap<String, FeatureInfo>());
+        assertThat(cache.maybeHasFeature("", 0)).isNull();
+        assertThat(cache.maybeHasFeature(FEATURE_WATCH, 0)).isFalse();
+        assertThat(cache.maybeHasFeature(FEATURE_PICTURE_IN_PICTURE, 0)).isFalse();
+        assertThat(cache.maybeHasFeature("com.missing.feature", 0)).isNull();
+    }
+
+    @Test
+    public void testNonSdkFeature() throws Exception {
+        ArrayMap<String, FeatureInfo> features = new ArrayMap<>();
+        features.put("custom.feature", createFeature("custom.feature", 0));
+        SystemFeaturesCache cache = new SystemFeaturesCache(features);
+
+        assertThat(cache.maybeHasFeature("custom.feature", 0)).isNull();
+    }
+
+    @Test
+    public void testSdkFeature() throws Exception {
+        ArrayMap<String, FeatureInfo> features = new ArrayMap<>();
+        features.put(FEATURE_WATCH, createFeature(FEATURE_WATCH, 0));
+        SystemFeaturesCache cache = new SystemFeaturesCache(features);
+
+        assertThat(cache.maybeHasFeature(FEATURE_WATCH, 0)).isTrue();
+        assertThat(cache.maybeHasFeature(FEATURE_WATCH, -1)).isTrue();
+        assertThat(cache.maybeHasFeature(FEATURE_WATCH, 1)).isFalse();
+        assertThat(cache.maybeHasFeature(FEATURE_WATCH, Integer.MIN_VALUE)).isTrue();
+        assertThat(cache.maybeHasFeature(FEATURE_WATCH, Integer.MAX_VALUE)).isFalse();
+
+        // Other SDK-declared features should be reported as unavailable.
+        assertThat(cache.maybeHasFeature(FEATURE_PICTURE_IN_PICTURE, 0)).isFalse();
+    }
+
+    @Test
+    public void testSdkFeatureHasMinVersion() throws Exception {
+        ArrayMap<String, FeatureInfo> features = new ArrayMap<>();
+        features.put(FEATURE_WATCH, createFeature(FEATURE_WATCH, Integer.MIN_VALUE));
+        SystemFeaturesCache cache = new SystemFeaturesCache(features);
+
+        assertThat(cache.maybeHasFeature(FEATURE_WATCH, 0)).isFalse();
+
+        // If both the query and the feature version itself happen to use MIN_VALUE, we can't
+        // reliably indicate availability, so it should report an indeterminate result.
+        assertThat(cache.maybeHasFeature(FEATURE_WATCH, Integer.MIN_VALUE)).isNull();
+    }
+
+    @Test
+    public void testParcel() throws Exception {
+        ArrayMap<String, FeatureInfo> features = new ArrayMap<>();
+        features.put(FEATURE_WATCH, createFeature(FEATURE_WATCH, 0));
+        SystemFeaturesCache cache = new SystemFeaturesCache(features);
+
+        Parcel parcel = Parcel.obtain();
+        SystemFeaturesCache parceledCache;
+        try {
+            parcel.writeParcelable(cache, 0);
+            parcel.setDataPosition(0);
+            parceledCache = parcel.readParcelable(getClass().getClassLoader());
+        } finally {
+            parcel.recycle();
+        }
+
+        assertThat(parceledCache.maybeHasFeature(FEATURE_WATCH, 0))
+                .isEqualTo(cache.maybeHasFeature(FEATURE_WATCH, 0));
+        assertThat(parceledCache.maybeHasFeature(FEATURE_PICTURE_IN_PICTURE, 0))
+                .isEqualTo(cache.maybeHasFeature(FEATURE_PICTURE_IN_PICTURE, 0));
+        assertThat(parceledCache.maybeHasFeature("custom.feature", 0))
+                .isEqualTo(cache.maybeHasFeature("custom.feature", 0));
+    }
+
+    private static FeatureInfo createFeature(String name, int version) {
+        FeatureInfo fi = new FeatureInfo();
+        fi.name = name;
+        fi.version = version;
+        return fi;
+    }
+}
diff --git a/core/tests/coretests/src/android/hardware/display/DisplayTopologyTest.kt b/core/tests/coretests/src/android/hardware/display/DisplayTopologyTest.kt
index 255d09b..008db5e 100644
--- a/core/tests/coretests/src/android/hardware/display/DisplayTopologyTest.kt
+++ b/core/tests/coretests/src/android/hardware/display/DisplayTopologyTest.kt
@@ -22,6 +22,7 @@
 import android.hardware.display.DisplayTopology.TreeNode.POSITION_LEFT
 import android.hardware.display.DisplayTopology.TreeNode.POSITION_TOP
 import android.hardware.display.DisplayTopology.TreeNode.POSITION_RIGHT
+import android.util.SparseArray
 import android.view.Display
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
@@ -642,13 +643,65 @@
         verifyDisplay(
                 root.children[0], id = 1, width = 30f, height = 30f, POSITION_RIGHT, offset = 10f,
                 noOfChildren = 1)
-        // In the case of corner adjacency, we prefer a left/right attachment.
         verifyDisplay(
                 root.children[0].children[0], id = 2, width = 29.5f, height = 30f, POSITION_BOTTOM,
                 offset = 30f, noOfChildren = 0)
     }
 
     @Test
+    fun rearrange_preferLessShiftInOverlapDimension() {
+        val root = rearrangeRects(
+            // '*' represents overlap
+            // Clamping requires moving display 2 and 1 slightly to avoid overlap with 0. We should
+            // shift the minimal amount to avoid overlap - e.g. display 2 shifts left (10 pixels)
+            // rather than up (20 pixels).
+            // 222
+            // 22*00
+            // 22*00
+            //   0**1
+            //    111
+            //    111
+            RectF(20f, 10f, 50f, 40f),
+            RectF(30f, 30f, 60f, 60f),
+            RectF(0f, 0f, 30f, 30f),
+        )
+
+        verifyDisplay(root, id = 0, width = 30f, height = 30f, noOfChildren = 2)
+        verifyDisplay(
+                root.children[0], id = 1, width = 30f, height = 30f, POSITION_BOTTOM, offset = 10f,
+                noOfChildren = 0)
+        verifyDisplay(
+                root.children[1], id = 2, width = 30f, height = 30f, POSITION_LEFT, offset = -10f,
+                noOfChildren = 0)
+    }
+
+    @Test
+    fun rearrange_doNotAttachCornerForShortOverlapOnLongEdgeBottom() {
+        val root = rearrangeRects(
+            RectF(0f, 0f, 1920f, 1080f),
+            RectF(1850f, 1070f, 3770f, 2150f),
+        )
+
+        verifyDisplay(root, id = 0, width = 1920f, height = 1080f, noOfChildren = 1)
+        verifyDisplay(
+                root.children[0], id = 1, width = 1920f, height = 1080f, POSITION_BOTTOM,
+                offset = 1850f, noOfChildren = 0)
+    }
+
+    @Test
+    fun rearrange_doNotAttachCornerForShortOverlapOnLongEdgeLeft() {
+        val root = rearrangeRects(
+            RectF(0f, 0f, 1080f, 1920f),
+            RectF(-1070f, -1880f, 10f, 40f),
+        )
+
+        verifyDisplay(root, id = 0, width = 1080f, height = 1920f, noOfChildren = 1)
+        verifyDisplay(
+                root.children[0], id = 1, width = 1080f, height = 1920f, POSITION_LEFT,
+                offset = -1880f, noOfChildren = 0)
+    }
+
+    @Test
     fun copy() {
         val display1 = DisplayTopology.TreeNode(/* displayId= */ 1, /* width= */ 200f,
             /* height= */ 600f, /* position= */ 0, /* offset= */ 0f)
@@ -687,6 +740,34 @@
             offset = 0f, noOfChildren = 0)
     }
 
+    @Test
+    fun coordinates() {
+        val display1 = DisplayTopology.TreeNode(/* displayId= */ 1, /* width= */ 200f,
+            /* height= */ 600f, /* position= */ 0, /* offset= */ 0f)
+
+        val display2 = DisplayTopology.TreeNode(/* displayId= */ 2, /* width= */ 600f,
+            /* height= */ 200f, POSITION_RIGHT, /* offset= */ 0f)
+        display1.addChild(display2)
+
+        val display3 = DisplayTopology.TreeNode(/* displayId= */ 3, /* width= */ 600f,
+            /* height= */ 200f, POSITION_RIGHT, /* offset= */ 400f)
+        display1.addChild(display3)
+
+        val display4 = DisplayTopology.TreeNode(/* displayId= */ 4, /* width= */ 200f,
+            /* height= */ 600f, POSITION_RIGHT, /* offset= */ 0f)
+        display2.addChild(display4)
+
+        topology = DisplayTopology(display1, /* primaryDisplayId= */ 1)
+        val coords = topology.absoluteBounds
+
+        val expectedCoords = SparseArray<RectF>()
+        expectedCoords.append(1, RectF(0f, 0f, 200f, 600f))
+        expectedCoords.append(2, RectF(200f, 0f, 800f, 200f))
+        expectedCoords.append(3, RectF(200f, 400f, 800f, 600f))
+        expectedCoords.append(4, RectF(800f, 0f, 1000f, 600f))
+        assertThat(coords.contentEquals(expectedCoords)).isTrue()
+    }
+
     /**
      * Runs the rearrange algorithm and returns the resulting tree as a list of nodes, with the
      * root at index 0. The number of nodes is inferred from the number of positions passed.
diff --git a/core/tests/coretests/src/android/os/BinderProxyTest.java b/core/tests/coretests/src/android/os/BinderProxyTest.java
index 335791c..5fff0b8 100644
--- a/core/tests/coretests/src/android/os/BinderProxyTest.java
+++ b/core/tests/coretests/src/android/os/BinderProxyTest.java
@@ -138,7 +138,7 @@
                     new Intent(mContext, BinderProxyService.class),
                     connection,
                     Context.BIND_AUTO_CREATE);
-            if (!bindLatch.await(500, TimeUnit.MILLISECONDS)) {
+            if (!bindLatch.await(1000, TimeUnit.MILLISECONDS)) {
                 fail(
                         "Timed out while binding service: "
                                 + BinderProxyService.class.getSimpleName());
diff --git a/core/tests/coretests/src/android/os/IpcDataCacheTest.java b/core/tests/coretests/src/android/os/IpcDataCacheTest.java
index fc04e64..74b32a1 100644
--- a/core/tests/coretests/src/android/os/IpcDataCacheTest.java
+++ b/core/tests/coretests/src/android/os/IpcDataCacheTest.java
@@ -19,6 +19,8 @@
 import static android.app.Flags.FLAG_PIC_ISOLATE_CACHE_BY_UID;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 import android.app.PropertyInvalidatedCache;
@@ -195,9 +197,9 @@
 
         try {
             testCache.query(9);
-            assertEquals(false, true);          // The code should not reach this point.
+            fail();          // The code should not reach this point.
         } catch (RuntimeException e) {
-            assertEquals(e.getCause() instanceof RemoteException, true);
+            assertTrue(e.getCause() instanceof RemoteException);
         }
         tester.verify(4);
     }
@@ -256,38 +258,38 @@
                         new ServerQuery(tester));
 
         // Caches are enabled upon creation.
-        assertEquals(false, cache1.getDisabledState());
-        assertEquals(false, cache2.getDisabledState());
-        assertEquals(false, cache3.getDisabledState());
+        assertFalse(cache1.isDisabled());
+        assertFalse(cache2.isDisabled());
+        assertFalse(cache3.isDisabled());
 
         // Disable the cache1 instance.  Only cache1 is disabled
         cache1.disableInstance();
-        assertEquals(true, cache1.getDisabledState());
-        assertEquals(false, cache2.getDisabledState());
-        assertEquals(false, cache3.getDisabledState());
+        assertTrue(cache1.isDisabled());
+        assertFalse(cache2.isDisabled());
+        assertFalse(cache3.isDisabled());
 
         // Disable cache1.  This will disable cache1 and cache2 because they share the
         // same name.  cache3 has a different name and will not be disabled.
         cache1.disableForCurrentProcess();
-        assertEquals(true, cache1.getDisabledState());
-        assertEquals(true, cache2.getDisabledState());
-        assertEquals(false, cache3.getDisabledState());
+        assertTrue(cache1.isDisabled());
+        assertTrue(cache2.isDisabled());
+        assertFalse(cache3.isDisabled());
 
         // Create a new cache1.  Verify that the new instance is disabled.
         cache1 = new IpcDataCache<>(4, MODULE, API, "cacheA",
                 new ServerQuery(tester));
-        assertEquals(true, cache1.getDisabledState());
+        assertTrue(cache1.isDisabled());
 
         // Remove the record of caches being locally disabled.  This is a clean-up step.
         cache1.forgetDisableLocal();
-        assertEquals(true, cache1.getDisabledState());
-        assertEquals(true, cache2.getDisabledState());
-        assertEquals(false, cache3.getDisabledState());
+        assertTrue(cache1.isDisabled());
+        assertTrue(cache2.isDisabled());
+        assertFalse(cache3.isDisabled());
 
         // Create a new cache1.  Verify that the new instance is not disabled.
         cache1 = new IpcDataCache<>(4, MODULE, API, "cacheA",
                 new ServerQuery(tester));
-        assertEquals(false, cache1.getDisabledState());
+        assertFalse(cache1.isDisabled());
     }
 
     private static class TestQuery
@@ -345,7 +347,7 @@
     public void testCacheRecompute() {
         TestCache cache = new TestCache();
         cache.invalidateCache();
-        assertEquals(cache.isDisabled(), false);
+        assertFalse(cache.isDisabled());
         assertEquals("foo5", cache.query(5));
         assertEquals(1, cache.getRecomputeCount());
         assertEquals("foo5", cache.query(5));
@@ -407,15 +409,15 @@
     @Test
     public void testLocalProcessDisable() {
         TestCache cache = new TestCache();
-        assertEquals(cache.isDisabled(), false);
+        assertFalse(cache.isDisabled());
         cache.invalidateCache();
         assertEquals("foo5", cache.query(5));
         assertEquals(1, cache.getRecomputeCount());
         assertEquals("foo5", cache.query(5));
         assertEquals(1, cache.getRecomputeCount());
-        assertEquals(cache.isDisabled(), false);
+        assertFalse(cache.isDisabled());
         cache.disableForCurrentProcess();
-        assertEquals(cache.isDisabled(), true);
+        assertTrue(cache.isDisabled());
         assertEquals("foo5", cache.query(5));
         assertEquals("foo5", cache.query(5));
         assertEquals(3, cache.getRecomputeCount());
@@ -434,20 +436,20 @@
         TestCache dc = new TestCache(d);
 
         a.disableForCurrentProcess();
-        assertEquals(ac.isDisabled(), true);
-        assertEquals(bc.isDisabled(), false);
-        assertEquals(cc.isDisabled(), false);
-        assertEquals(dc.isDisabled(), false);
+        assertTrue(ac.isDisabled());
+        assertFalse(bc.isDisabled());
+        assertFalse(cc.isDisabled());
+        assertFalse(dc.isDisabled());
 
         a.disableAllForCurrentProcess();
-        assertEquals(ac.isDisabled(), true);
-        assertEquals(bc.isDisabled(), false);
-        assertEquals(cc.isDisabled(), false);
-        assertEquals(dc.isDisabled(), true);
+        assertTrue(ac.isDisabled());
+        assertFalse(bc.isDisabled());
+        assertFalse(cc.isDisabled());
+        assertTrue(dc.isDisabled());
 
         IpcDataCache.Config e = a.child("nameE");
         TestCache ec = new TestCache(e);
-        assertEquals(ec.isDisabled(), true);
+        assertTrue(ec.isDisabled());
     }
 
 
diff --git a/core/tests/coretests/src/android/os/MessageQueueTest.java b/core/tests/coretests/src/android/os/MessageQueueTest.java
deleted file mode 100644
index 30f6636..0000000
--- a/core/tests/coretests/src/android/os/MessageQueueTest.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.os;
-
-import android.platform.test.ravenwood.RavenwoodRule;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.MediumTest;
-import androidx.test.filters.Suppress;
-
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-public class MessageQueueTest {
-
-}
diff --git a/core/tests/coretests/src/android/security/advancedprotection/OWNERS b/core/tests/coretests/src/android/security/advancedprotection/OWNERS
new file mode 100644
index 0000000..9bf5e58
--- /dev/null
+++ b/core/tests/coretests/src/android/security/advancedprotection/OWNERS
@@ -0,0 +1 @@
+file:platform/frameworks/base:main:/core/java/android/security/advancedprotection/OWNERS
diff --git a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
index 248db65..4a54f6b 100644
--- a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
+++ b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
@@ -133,7 +133,7 @@
             // Called once through the show flow.
             verify(mController).applyAnimation(
                     eq(WindowInsets.Type.ime()), eq(true) /* show */, eq(true) /* fromIme */,
-                    eq(statsToken));
+                    eq(false) /* skipsCallbacks */, eq(statsToken));
 
             // set control and verify visibility is applied.
             InsetsSourceControl control = new InsetsSourceControl(ID_IME,
@@ -142,10 +142,10 @@
             // IME show animation should be triggered when control becomes available.
             verify(mController).applyAnimation(
                     eq(WindowInsets.Type.ime()), eq(true) /* show */, eq(false) /* fromIme */,
-                    and(not(eq(statsToken)), notNull()));
+                    eq(false) /* skipsCallbacks */, and(not(eq(statsToken)), notNull()));
             verify(mController, never()).applyAnimation(
                     eq(WindowInsets.Type.ime()), eq(false) /* show */, eq(false) /* fromIme */,
-                    and(not(eq(statsToken)), notNull()));
+                    eq(false) /* skipsCallbacks */, and(not(eq(statsToken)), notNull()));
         });
     }
 
@@ -163,7 +163,7 @@
             // Called once through the show flow.
             verify(mController).applyAnimation(
                     eq(WindowInsets.Type.ime()), eq(true) /* show */, eq(true) /* fromIme */,
-                    eq(statsToken));
+                    eq(false) /* skipsCallbacks */, eq(statsToken));
             // Clear previous invocations to verify this is never called with control without leash.
             clearInvocations(mController);
 
@@ -175,10 +175,10 @@
             // as we have no leash.
             verify(mController, never()).applyAnimation(
                     eq(WindowInsets.Type.ime()), eq(true) /* show */, eq(false) /* fromIme */,
-                    and(not(eq(statsToken)), notNull()));
+                    eq(false) /* skipsCallbacks */, and(not(eq(statsToken)), notNull()));
             verify(mController, never()).applyAnimation(
                     eq(WindowInsets.Type.ime()), eq(false) /* show */, eq(false) /* fromIme */,
-                    and(not(eq(statsToken)), notNull()));
+                    eq(false) /* skipsCallbacks */, and(not(eq(statsToken)), notNull()));
 
             // set control with leash and verify visibility is applied.
             InsetsSourceControl controlWithLeash = new InsetsSourceControl(ID_IME,
@@ -187,10 +187,10 @@
             // IME show animation should be triggered when control with leash becomes available.
             verify(mController).applyAnimation(
                     eq(WindowInsets.Type.ime()), eq(true) /* show */, eq(false) /* fromIme */,
-                    and(not(eq(statsToken)), notNull()));
+                    eq(false) /* skipsCallbacks */, and(not(eq(statsToken)), notNull()));
             verify(mController, never()).applyAnimation(
                     eq(WindowInsets.Type.ime()), eq(false) /* show */, eq(false) /* fromIme */,
-                    and(not(eq(statsToken)), notNull()));
+                    eq(false) /* skipsCallbacks */, and(not(eq(statsToken)), notNull()));
         });
     }
 
@@ -223,7 +223,8 @@
                 // Called once through the show flow.
                 verify(mController).applyAnimation(eq(WindowInsets.Type.ime()),
                         eq(true) /* show */, eq(true) /* fromIme */,
-                        eq(false) /* skipAnim */, eq(statsToken));
+                        eq(false) /* skipsAnim */, eq(false) /* skipsCallbacks */,
+                        eq(statsToken));
             }
 
             // set control and verify visibility is applied.
@@ -241,7 +242,8 @@
                 // so the statsToken won't match.
                 verify(mController).applyAnimation(eq(WindowInsets.Type.ime()),
                         eq(true) /* show */, eq(false) /* fromIme */,
-                        eq(expectSkipAnim) /* skipAnim */, and(not(eq(statsToken)), notNull()));
+                        eq(expectSkipAnim) /* skipsAnim */, eq(false) /* skipsCallbacks */,
+                        and(not(eq(statsToken)), notNull()));
             }
 
             // If previously hasViewFocus is false, verify when requesting the IME visible next
@@ -252,14 +254,16 @@
                 // Called once through the show flow.
                 verify(mController).applyAnimation(eq(WindowInsets.Type.ime()),
                         eq(true) /* show */, eq(true) /* fromIme */,
-                        eq(false) /* skipAnim */, eq(statsTokenNext));
+                        eq(false) /* skipsAnim */, eq(false) /* skipsCallbacks */,
+                        eq(statsTokenNext));
                 mController.onControlsChanged(new InsetsSourceControl[]{ control });
                 // Verify IME show animation should be triggered when control becomes available and
                 // the animation will be skipped by getAndClearSkipAnimationOnce invoked.
                 verify(control).getAndClearSkipAnimationOnce();
                 verify(mController).applyAnimation(eq(WindowInsets.Type.ime()),
                         eq(true) /* show */, eq(false) /* fromIme */,
-                        eq(true) /* skipAnim */, and(not(eq(statsToken)), notNull()));
+                        eq(true) /* skipsAnim */, eq(false) /* skipsCallbacks */,
+                        and(not(eq(statsToken)), notNull()));
             }
         });
     }
diff --git a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
index d7f6a29..905d897 100644
--- a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
+++ b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
@@ -100,14 +100,14 @@
         topConsumer.setControl(
                 new InsetsSourceControl(ID_STATUS_BAR, WindowInsets.Type.statusBars(),
                         mStatusLeash, true, new Point(0, 0), Insets.of(0, 100, 0, 0)),
-                new int[1], new int[1], new int[1]);
+                new int[1], new int[1], new int[1], new int[1]);
 
         InsetsSourceConsumer navConsumer = new InsetsSourceConsumer(ID_NAVIGATION_BAR,
                 WindowInsets.Type.navigationBars(), mInsetsState, mMockController);
         navConsumer.setControl(
                 new InsetsSourceControl(ID_NAVIGATION_BAR, WindowInsets.Type.navigationBars(),
                         mNavLeash, true, new Point(400, 0), Insets.of(0, 0, 100, 0)),
-                new int[1], new int[1], new int[1]);
+                new int[1], new int[1], new int[1], new int[1]);
         mMockController.setRequestedVisibleTypes(0, WindowInsets.Type.navigationBars());
         navConsumer.applyLocalVisibilityOverride();
 
diff --git a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
index 3a8f7ee..45d66e8 100644
--- a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
@@ -117,7 +117,7 @@
         mConsumer.setControl(
                 new InsetsSourceControl(ID_STATUS_BAR, statusBars(), mLeash,
                         true /* initialVisible */, new Point(), Insets.NONE),
-                new int[1], new int[1], new int[1]);
+                new int[1], new int[1], new int[1], new int[1]);
     }
 
     @Test
@@ -129,7 +129,7 @@
             newControl.setInsetsHint(Insets.of(0, 0, 0, 100));
 
             int[] cancelTypes = {0};
-            mConsumer.setControl(newControl, new int[1], new int[1], cancelTypes);
+            mConsumer.setControl(newControl, new int[1], new int[1], cancelTypes, new int[1]);
 
             assertEquals(statusBars(), cancelTypes[0]);
         });
@@ -196,7 +196,7 @@
     @Test
     public void testRestore() {
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
-            mConsumer.setControl(null, new int[1], new int[1], new int[1]);
+            mConsumer.setControl(null, new int[1], new int[1], new int[1], new int[1]);
             mSurfaceParamsApplied = false;
             mController.setRequestedVisibleTypes(0 /* visibleTypes */, statusBars());
             assertFalse(mSurfaceParamsApplied);
@@ -204,7 +204,7 @@
             mConsumer.setControl(
                     new InsetsSourceControl(ID_STATUS_BAR, statusBars(), mLeash,
                             true /* initialVisible */, new Point(), Insets.NONE),
-                    new int[1], hideTypes, new int[1]);
+                    new int[1], hideTypes, new int[1], new int[1]);
             assertEquals(statusBars(), hideTypes[0]);
             assertFalse(mRemoveSurfaceCalled);
         });
@@ -214,7 +214,7 @@
     public void testRestore_noAnimation() {
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
             mController.setRequestedVisibleTypes(0 /* visibleTypes */, statusBars());
-            mConsumer.setControl(null, new int[1], new int[1], new int[1]);
+            mConsumer.setControl(null, new int[1], new int[1], new int[1], new int[1]);
             mLeash = new SurfaceControl.Builder(mSession)
                     .setName("testSurface")
                     .build();
@@ -223,7 +223,7 @@
             mConsumer.setControl(
                     new InsetsSourceControl(ID_STATUS_BAR, statusBars(), mLeash,
                             false /* initialVisible */, new Point(), Insets.NONE),
-                    new int[1], hideTypes, new int[1]);
+                    new int[1], hideTypes, new int[1], new int[1]);
             assertTrue(mRemoveSurfaceCalled);
             assertEquals(0, hideTypes[0]);
         });
@@ -252,7 +252,7 @@
             // Initial IME insets source control with its leash.
             imeConsumer.setControl(new InsetsSourceControl(ID_IME, ime(), mLeash,
                     false /* initialVisible */, new Point(), Insets.NONE), new int[1], new int[1],
-                    new int[1]);
+                    new int[1], new int[1]);
             mSurfaceParamsApplied = false;
 
             // Verify when the app requests controlling show IME animation, the IME leash
@@ -262,7 +262,7 @@
             assertEquals(ANIMATION_TYPE_USER, insetsController.getAnimationType(ime()));
             imeConsumer.setControl(new InsetsSourceControl(ID_IME, ime(), mLeash,
                     true /* initialVisible */, new Point(), Insets.NONE), new int[1], new int[1],
-                    new int[1]);
+                    new int[1], new int[1]);
             assertFalse(mSurfaceParamsApplied);
         });
     }
diff --git a/core/tests/coretests/src/android/view/MotionEventTest.java b/core/tests/coretests/src/android/view/MotionEventTest.java
index d0f9a38..419c05f 100644
--- a/core/tests/coretests/src/android/view/MotionEventTest.java
+++ b/core/tests/coretests/src/android/view/MotionEventTest.java
@@ -49,9 +49,14 @@
     private static final int ID_SOURCE_MASK = 0x3 << 30;
 
     private PointerCoords pointerCoords(float x, float y) {
+        return pointerCoords(x, y, 0f /*orientation*/);
+    }
+
+    private PointerCoords pointerCoords(float x, float y, float orientation) {
         final var coords = new PointerCoords();
         coords.x = x;
         coords.y = y;
+        coords.orientation = orientation;
         return coords;
     }
 
@@ -295,4 +300,24 @@
             // Expected
         }
     }
+
+    @Test
+    public void testAxesAreNotAffectedByFlagChanges() {
+        final int pointerCount = 1;
+        final var properties = new PointerProperties[]{fingerProperties(0)};
+        final var coords = new PointerCoords[]{pointerCoords(20, 60, 0.1234f)};
+
+        final MotionEvent event = MotionEvent.obtain(0 /* downTime */,
+                0 /* eventTime */, MotionEvent.ACTION_MOVE, pointerCount, properties, coords,
+                0 /* metaState */, 0 /* buttonState */, 1 /* xPrecision */, 1 /* yPrecision */,
+                0 /* deviceId */, 0 /* edgeFlags */, InputDevice.SOURCE_TOUCHSCREEN,
+                0 /* flags */);
+
+        // Mark the event as tainted to update the MotionEvent flags.
+        event.setTainted(true);
+
+        assertEquals(20f, event.getX(), 0.0001);
+        assertEquals(60f, event.getY(), 0.0001);
+        assertEquals(0.1234f, event.getOrientation(), 0.0001);
+    }
 }
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index 18ab52d..c40137f 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -51,6 +51,8 @@
 import static android.view.flags.Flags.toolkitFrameRateDefaultNormalReadOnly;
 import static android.view.flags.Flags.toolkitFrameRateVelocityMappingReadOnly;
 
+import static com.android.cts.input.inputeventmatchers.InputEventMatchersKt.withKeyCode;
+
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
 
@@ -91,8 +93,10 @@
 import androidx.test.platform.app.InstrumentationRegistry;
 
 import com.android.compatibility.common.util.ShellIdentityUtils;
+import com.android.cts.input.BlockingQueueEventVerifier;
 import com.android.window.flags.Flags;
 
+import org.hamcrest.Matcher;
 import org.junit.After;
 import org.junit.AfterClass;
 import org.junit.Before;
@@ -101,7 +105,9 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
 
 /**
@@ -121,7 +127,6 @@
 
     private ViewRootImpl mViewRootImpl;
     private View mView;
-    private volatile boolean mKeyReceived = false;
 
     private static Context sContext;
     private static Instrumentation sInstrumentation = InstrumentationRegistry.getInstrumentation();
@@ -1679,16 +1684,28 @@
         }
     }
 
-    class KeyView extends View {
-        KeyView(Context context) {
+    static class InputView extends View {
+        private final BlockingQueue<InputEvent> mEvents = new LinkedBlockingQueue<>();
+        private final BlockingQueueEventVerifier mVerifier =
+                new BlockingQueueEventVerifier(mEvents);
+
+        InputView(Context context) {
             super(context);
         }
 
         @Override
         public boolean dispatchKeyEventPreIme(KeyEvent event) {
-            mKeyReceived = true;
+            mEvents.add(event.copy());
             return true /*handled*/;
         }
+
+        public void assertReceivedKey(Matcher<KeyEvent> matcher) {
+            mVerifier.assertReceivedKey(matcher);
+        }
+
+        public void assertNoEvents() {
+            mVerifier.assertNoEvents();
+        }
     }
 
     /**
@@ -1697,7 +1714,7 @@
      * Next, inject an event into this view, and check whether it is received.
      */
     private void checkKeyEvent(Runnable setup, boolean shouldReceiveKey) {
-        final KeyView view = new KeyView(sContext);
+        final InputView view = new InputView(sContext);
         mView = view;
 
         attachViewToWindow(view);
@@ -1712,7 +1729,11 @@
             mViewRootImpl.dispatchInputEvent(event);
         });
         sInstrumentation.waitForIdleSync();
-        assertEquals(shouldReceiveKey, mKeyReceived);
+        if (shouldReceiveKey) {
+            view.assertReceivedKey(withKeyCode(KeyEvent.KEYCODE_A));
+        } else {
+            view.assertNoEvents();
+        }
     }
 
     private void attachViewToWindow(View view) {
diff --git a/core/tests/coretests/src/android/window/ConfigurationChangeSettingTest.kt b/core/tests/coretests/src/android/window/ConfigurationChangeSettingTest.kt
new file mode 100644
index 0000000..1234a82
--- /dev/null
+++ b/core/tests/coretests/src/android/window/ConfigurationChangeSettingTest.kt
@@ -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 android.window
+
+import android.os.Parcel
+import android.os.Parcelable
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
+import android.platform.test.annotations.Presubmit
+import android.platform.test.flag.junit.SetFlagsRule
+import android.view.Display.DEFAULT_DISPLAY
+import android.window.ConfigurationChangeSetting.SETTING_TYPE_UNKNOWN
+import android.window.ConfigurationChangeSetting.SETTING_TYPE_DISPLAY_DENSITY
+import android.window.ConfigurationChangeSetting.SETTING_TYPE_FONT_SCALE
+import android.window.ConfigurationChangeSetting.ConfigurationChangeSettingInternal
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.server.LocalServices
+import com.android.window.flags.Flags
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.AfterTest
+import kotlin.test.BeforeTest
+import org.junit.Assert.assertThrows
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.stub
+import org.mockito.kotlin.verify
+
+/**
+ * Build/Install/Run:
+ * atest FrameworksCoreTests:ConfigurationChangeSettingTest
+ */
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4::class)
+class ConfigurationChangeSettingTest {
+    @get:Rule
+    val setFlagsRule: SetFlagsRule = SetFlagsRule()
+
+    private val mMockConfigurationChangeSettingInternal = mock<ConfigurationChangeSettingInternal>()
+
+    @BeforeTest
+    fun setup() {
+        tearDownLocalService()
+        LocalServices.addService(
+            ConfigurationChangeSettingInternal::class.java,
+            mMockConfigurationChangeSettingInternal,
+        )
+    }
+
+    @AfterTest
+    fun tearDown() {
+        tearDownLocalService()
+    }
+
+    @Test(expected = IllegalStateException::class)
+    @DisableFlags(Flags.FLAG_CONDENSE_CONFIGURATION_CHANGE_FOR_SIMPLE_MODE)
+    fun settingCreation_whenFlagDisabled_throwsException() {
+        ConfigurationChangeSetting.DensitySetting(DEFAULT_DISPLAY, TEST_DENSITY)
+    }
+
+    @Test
+    fun invalidSettingType_appClient_throwsException() {
+        val parcel = Parcel.obtain()
+        try {
+            parcel.writeInt(SETTING_TYPE_UNKNOWN)
+            parcel.setDataPosition(0)
+
+            assertThrows(IllegalArgumentException::class.java) {
+                DEFAULT_CREATOR.createFromParcel(parcel)
+            }
+        } finally {
+            parcel.recycle()
+        }
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_CONDENSE_CONFIGURATION_CHANGE_FOR_SIMPLE_MODE)
+    fun densitySettingParcelable_appClient_recreatesSucceeds() {
+        val setting = ConfigurationChangeSetting.DensitySetting(DEFAULT_DISPLAY, TEST_DENSITY)
+
+        val recreated = setting.recreateFromParcel()
+
+        verify(mMockConfigurationChangeSettingInternal, never()).createImplFromParcel(any(), any())
+        assertThat(recreated).isEqualTo(setting)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_CONDENSE_CONFIGURATION_CHANGE_FOR_SIMPLE_MODE)
+    fun densitySettingParcelable_systemServer_createsImplFromInternal() {
+        val setting = ConfigurationChangeSetting.DensitySetting(DEFAULT_DISPLAY, TEST_DENSITY)
+        val mockDensitySetting = mock<ConfigurationChangeSetting.DensitySetting>()
+        mMockConfigurationChangeSettingInternal.stub {
+            on { createImplFromParcel(any(), any()) } doReturn mockDensitySetting
+        }
+
+        val recreated = setting.recreateFromParcel(TEST_SYSTEM_SERVER_CREATOR)
+
+        verify(mMockConfigurationChangeSettingInternal).createImplFromParcel(
+            eq(SETTING_TYPE_DISPLAY_DENSITY),
+            any(),
+        )
+        assertThat(recreated).isEqualTo(mockDensitySetting)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_CONDENSE_CONFIGURATION_CHANGE_FOR_SIMPLE_MODE)
+    fun fontScaleSettingParcelable_appClient_recreatesSucceeds() {
+        val setting = ConfigurationChangeSetting.FontScaleSetting(TEST_FONT_SCALE)
+
+        val recreated = setting.recreateFromParcel()
+
+        verify(mMockConfigurationChangeSettingInternal, never()).createImplFromParcel(any(), any())
+        assertThat(recreated).isEqualTo(setting)
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_CONDENSE_CONFIGURATION_CHANGE_FOR_SIMPLE_MODE)
+    fun fontScaleSettingParcelable_systemServer_createsImplFromInternal() {
+        val setting = ConfigurationChangeSetting.FontScaleSetting(TEST_FONT_SCALE)
+        val mockFontScaleSetting = mock<ConfigurationChangeSetting.FontScaleSetting>()
+        mMockConfigurationChangeSettingInternal.stub {
+            on { createImplFromParcel(any(), any()) } doReturn mockFontScaleSetting
+        }
+
+        val recreated = setting.recreateFromParcel(TEST_SYSTEM_SERVER_CREATOR)
+
+        verify(mMockConfigurationChangeSettingInternal).createImplFromParcel(
+            eq(SETTING_TYPE_FONT_SCALE),
+            any(),
+        )
+        assertThat(recreated).isEqualTo(mockFontScaleSetting)
+    }
+
+    companion object {
+        private const val TEST_DENSITY = 400
+        private const val TEST_FONT_SCALE = 1.5f
+
+        private val DEFAULT_CREATOR = ConfigurationChangeSetting.CREATOR
+        private val TEST_SYSTEM_SERVER_CREATOR =
+            ConfigurationChangeSetting.CreatorImpl(true /* isSystem */)
+
+        private fun tearDownLocalService() =
+            LocalServices.removeServiceForTest(ConfigurationChangeSettingInternal::class.java)
+
+        private fun ConfigurationChangeSetting.recreateFromParcel(
+            creator: Parcelable.Creator<ConfigurationChangeSetting> = DEFAULT_CREATOR,
+        ): ConfigurationChangeSetting {
+            val parcel = Parcel.obtain()
+            try {
+                writeToParcel(parcel, 0)
+                parcel.setDataPosition(0)
+                return creator.createFromParcel(parcel)
+            } finally {
+                parcel.recycle()
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/core/tests/coretests/src/com/android/internal/notification/SystemNotificationChannelsTest.java b/core/tests/coretests/src/com/android/internal/notification/SystemNotificationChannelsTest.java
index 0bf406c..2bd3f4d 100644
--- a/core/tests/coretests/src/com/android/internal/notification/SystemNotificationChannelsTest.java
+++ b/core/tests/coretests/src/com/android/internal/notification/SystemNotificationChannelsTest.java
@@ -17,6 +17,7 @@
 package com.android.internal.notification;
 
 import static com.android.internal.notification.SystemNotificationChannels.ABUSIVE_BACKGROUND_APPS;
+import static com.android.internal.notification.SystemNotificationChannels.ACCESSIBILITY_HEARING_DEVICE;
 import static com.android.internal.notification.SystemNotificationChannels.ACCESSIBILITY_MAGNIFICATION;
 import static com.android.internal.notification.SystemNotificationChannels.ACCESSIBILITY_SECURITY_POLICY;
 import static com.android.internal.notification.SystemNotificationChannels.ACCOUNT;
@@ -90,8 +91,8 @@
                         DEVELOPER_IMPORTANT, UPDATES, NETWORK_STATUS, NETWORK_ALERTS,
                         NETWORK_AVAILABLE, VPN, DEVICE_ADMIN, ALERTS, RETAIL_MODE, USB,
                         FOREGROUND_SERVICE, HEAVY_WEIGHT_APP, SYSTEM_CHANGES,
-                        ACCESSIBILITY_MAGNIFICATION, ACCESSIBILITY_SECURITY_POLICY,
-                        ABUSIVE_BACKGROUND_APPS);
+                        ACCESSIBILITY_MAGNIFICATION, ACCESSIBILITY_HEARING_DEVICE,
+                        ACCESSIBILITY_SECURITY_POLICY, ABUSIVE_BACKGROUND_APPS);
     }
 
     @Test
diff --git a/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java b/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java
index 5613caf..f105ec3 100644
--- a/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java
+++ b/core/tests/coretests/src/com/android/internal/widget/NotificationProgressBarTest.java
@@ -20,12 +20,13 @@
 
 import android.app.Notification.ProgressStyle;
 import android.graphics.Color;
+import android.util.Pair;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 
-import com.android.internal.widget.NotificationProgressDrawable.Part;
-import com.android.internal.widget.NotificationProgressDrawable.Point;
-import com.android.internal.widget.NotificationProgressDrawable.Segment;
+import com.android.internal.widget.NotificationProgressBar.Part;
+import com.android.internal.widget.NotificationProgressBar.Point;
+import com.android.internal.widget.NotificationProgressBar.Segment;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -37,181 +38,303 @@
 public class NotificationProgressBarTest {
 
     @Test(expected = IllegalArgumentException.class)
-    public void processAndConvertToDrawableParts_segmentsIsEmpty() {
+    public void processAndConvertToParts_segmentsIsEmpty() {
         List<ProgressStyle.Segment> segments = new ArrayList<>();
         List<ProgressStyle.Point> points = new ArrayList<>();
         int progress = 50;
         int progressMax = 100;
-        boolean isStyledByProgress = true;
 
-        NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress,
-                progressMax,
-                isStyledByProgress);
+        NotificationProgressBar.processAndConvertToViewParts(segments, points, progress,
+                progressMax);
     }
 
     @Test(expected = IllegalArgumentException.class)
-    public void processAndConvertToDrawableParts_segmentsLengthNotMatchingProgressMax() {
+    public void processAndConvertToParts_segmentsLengthNotMatchingProgressMax() {
         List<ProgressStyle.Segment> segments = new ArrayList<>();
         segments.add(new ProgressStyle.Segment(50));
         segments.add(new ProgressStyle.Segment(100));
         List<ProgressStyle.Point> points = new ArrayList<>();
         int progress = 50;
         int progressMax = 100;
-        boolean isStyledByProgress = true;
 
-        NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress,
-                progressMax,
-                isStyledByProgress);
+        NotificationProgressBar.processAndConvertToViewParts(segments, points, progress,
+                progressMax);
     }
 
     @Test(expected = IllegalArgumentException.class)
-    public void processAndConvertToDrawableParts_segmentLengthIsNegative() {
+    public void processAndConvertToParts_segmentLengthIsNegative() {
         List<ProgressStyle.Segment> segments = new ArrayList<>();
         segments.add(new ProgressStyle.Segment(-50));
         segments.add(new ProgressStyle.Segment(150));
         List<ProgressStyle.Point> points = new ArrayList<>();
         int progress = 50;
         int progressMax = 100;
-        boolean isStyledByProgress = true;
 
-        NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress,
-                progressMax,
-                isStyledByProgress);
+        NotificationProgressBar.processAndConvertToViewParts(segments, points, progress,
+                progressMax);
     }
 
     @Test(expected = IllegalArgumentException.class)
-    public void processAndConvertToDrawableParts_segmentLengthIsZero() {
+    public void processAndConvertToParts_segmentLengthIsZero() {
         List<ProgressStyle.Segment> segments = new ArrayList<>();
         segments.add(new ProgressStyle.Segment(0));
         segments.add(new ProgressStyle.Segment(100));
         List<ProgressStyle.Point> points = new ArrayList<>();
         int progress = 50;
         int progressMax = 100;
-        boolean isStyledByProgress = true;
 
-        NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress,
-                progressMax,
-                isStyledByProgress);
+        NotificationProgressBar.processAndConvertToViewParts(segments, points, progress,
+                progressMax);
     }
 
     @Test(expected = IllegalArgumentException.class)
-    public void processAndConvertToDrawableParts_progressIsNegative() {
+    public void processAndConvertToParts_progressIsNegative() {
         List<ProgressStyle.Segment> segments = new ArrayList<>();
         segments.add(new ProgressStyle.Segment(100));
         List<ProgressStyle.Point> points = new ArrayList<>();
         int progress = -50;
         int progressMax = 100;
-        boolean isStyledByProgress = true;
 
-        NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress,
-                progressMax,
-                isStyledByProgress);
+        NotificationProgressBar.processAndConvertToViewParts(segments, points, progress,
+                progressMax);
     }
 
     @Test
-    public void processAndConvertToDrawableParts_progressIsZero() {
+    public void processAndConvertToParts_progressIsZero() {
         List<ProgressStyle.Segment> segments = new ArrayList<>();
         segments.add(new ProgressStyle.Segment(100).setColor(Color.RED));
         List<ProgressStyle.Point> points = new ArrayList<>();
         int progress = 0;
         int progressMax = 100;
+
+        List<Part> parts = NotificationProgressBar.processAndConvertToViewParts(segments, points,
+                progress, progressMax);
+
+        List<Part> expectedParts = new ArrayList<>(List.of(new Segment(1f, Color.RED)));
+
+        assertThat(parts).isEqualTo(expectedParts);
+
+        float drawableWidth = 300;
+        float segSegGap = 4;
+        float segPointGap = 4;
+        float pointRadius = 6;
+        boolean hasTrackerIcon = true;
+
+        List<NotificationProgressDrawable.Part> drawableParts =
+                NotificationProgressBar.processAndConvertToDrawableParts(parts, drawableWidth,
+                        segSegGap, segPointGap, pointRadius, hasTrackerIcon
+                );
+
+        List<NotificationProgressDrawable.Part> expectedDrawableParts = new ArrayList<>(
+                List.of(new NotificationProgressDrawable.Segment(0, 300, Color.RED)));
+
+        assertThat(drawableParts).isEqualTo(expectedDrawableParts);
+
+        float segmentMinWidth = 16;
         boolean isStyledByProgress = true;
 
-        List<Part> parts = NotificationProgressBar.processAndConvertToDrawableParts(
-                segments, points, progress, progressMax, isStyledByProgress);
+        Pair<List<NotificationProgressDrawable.Part>, Float> p =
+                NotificationProgressBar.maybeStretchAndRescaleSegments(parts, drawableParts,
+                        segmentMinWidth, pointRadius, (float) progress / progressMax, 300,
+                        isStyledByProgress, hasTrackerIcon ? 0 : segSegGap);
 
-        int fadedRed = 0x7FFF0000;
-        List<Part> expected = new ArrayList<>(List.of(new Segment(1f, fadedRed, true)));
+        // Colors with 40% opacity
+        int fadedRed = 0x66FF0000;
+        expectedDrawableParts = new ArrayList<>(
+                List.of(new NotificationProgressDrawable.Segment(0, 300, fadedRed, true)));
 
-        assertThat(parts).isEqualTo(expected);
+        assertThat(p.second).isEqualTo(0);
+        assertThat(p.first).isEqualTo(expectedDrawableParts);
     }
 
     @Test
-    public void processAndConvertToDrawableParts_progressAtMax() {
+    public void processAndConvertToParts_progressAtMax() {
         List<ProgressStyle.Segment> segments = new ArrayList<>();
         segments.add(new ProgressStyle.Segment(100).setColor(Color.RED));
         List<ProgressStyle.Point> points = new ArrayList<>();
         int progress = 100;
         int progressMax = 100;
+
+        List<Part> parts = NotificationProgressBar.processAndConvertToViewParts(segments, points,
+                progress, progressMax);
+
+        List<Part> expectedParts = new ArrayList<>(List.of(new Segment(1f, Color.RED)));
+
+        assertThat(parts).isEqualTo(expectedParts);
+
+        float drawableWidth = 300;
+        float segSegGap = 4;
+        float segPointGap = 4;
+        float pointRadius = 6;
+        boolean hasTrackerIcon = true;
+
+        List<NotificationProgressDrawable.Part> drawableParts =
+                NotificationProgressBar.processAndConvertToDrawableParts(parts, drawableWidth,
+                        segSegGap, segPointGap, pointRadius, hasTrackerIcon
+                );
+
+        List<NotificationProgressDrawable.Part> expectedDrawableParts = new ArrayList<>(
+                List.of(new NotificationProgressDrawable.Segment(0, 300, Color.RED)));
+
+        assertThat(drawableParts).isEqualTo(expectedDrawableParts);
+
+        float segmentMinWidth = 16;
         boolean isStyledByProgress = true;
 
-        List<Part> parts = NotificationProgressBar.processAndConvertToDrawableParts(
-                segments, points, progress, progressMax, isStyledByProgress);
+        Pair<List<NotificationProgressDrawable.Part>, Float> p =
+                NotificationProgressBar.maybeStretchAndRescaleSegments(parts, drawableParts,
+                        segmentMinWidth, pointRadius, (float) progress / progressMax, 300,
+                        isStyledByProgress, hasTrackerIcon ? 0 : segSegGap);
 
-        List<Part> expected = new ArrayList<>(List.of(new Segment(1f, Color.RED)));
-
-        assertThat(parts).isEqualTo(expected);
+        assertThat(p.second).isEqualTo(300);
+        assertThat(p.first).isEqualTo(expectedDrawableParts);
     }
 
     @Test(expected = IllegalArgumentException.class)
-    public void processAndConvertToDrawableParts_progressAboveMax() {
+    public void processAndConvertToParts_progressAboveMax() {
         List<ProgressStyle.Segment> segments = new ArrayList<>();
         segments.add(new ProgressStyle.Segment(100));
         List<ProgressStyle.Point> points = new ArrayList<>();
         int progress = 150;
         int progressMax = 100;
-        boolean isStyledByProgress = true;
 
-        NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress,
-                progressMax, isStyledByProgress);
+        NotificationProgressBar.processAndConvertToViewParts(segments, points, progress,
+                progressMax);
     }
 
     @Test(expected = IllegalArgumentException.class)
-    public void processAndConvertToDrawableParts_pointPositionIsNegative() {
+    public void processAndConvertToParts_pointPositionIsNegative() {
         List<ProgressStyle.Segment> segments = new ArrayList<>();
         segments.add(new ProgressStyle.Segment(100));
         List<ProgressStyle.Point> points = new ArrayList<>();
         points.add(new ProgressStyle.Point(-50).setColor(Color.RED));
         int progress = 50;
         int progressMax = 100;
-        boolean isStyledByProgress = true;
 
-        NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress,
-                progressMax,
-                isStyledByProgress);
+        NotificationProgressBar.processAndConvertToViewParts(segments, points, progress,
+                progressMax);
     }
 
     @Test(expected = IllegalArgumentException.class)
-    public void processAndConvertToDrawableParts_pointPositionAboveMax() {
+    public void processAndConvertToParts_pointPositionAboveMax() {
         List<ProgressStyle.Segment> segments = new ArrayList<>();
         segments.add(new ProgressStyle.Segment(100));
         List<ProgressStyle.Point> points = new ArrayList<>();
         points.add(new ProgressStyle.Point(150).setColor(Color.RED));
         int progress = 50;
         int progressMax = 100;
-        boolean isStyledByProgress = true;
 
-        NotificationProgressBar.processAndConvertToDrawableParts(segments, points, progress,
-                progressMax,
-                isStyledByProgress);
+        NotificationProgressBar.processAndConvertToViewParts(segments, points, progress,
+                progressMax);
     }
 
     @Test
-    public void processAndConvertToDrawableParts_multipleSegmentsWithoutPoints() {
+    public void processAndConvertToParts_multipleSegmentsWithoutPoints() {
         List<ProgressStyle.Segment> segments = new ArrayList<>();
         segments.add(new ProgressStyle.Segment(50).setColor(Color.RED));
         segments.add(new ProgressStyle.Segment(50).setColor(Color.GREEN));
         List<ProgressStyle.Point> points = new ArrayList<>();
         int progress = 60;
         int progressMax = 100;
-        boolean isStyledByProgress = true;
 
-        List<Part> parts = NotificationProgressBar.processAndConvertToDrawableParts(
-                segments, points, progress, progressMax, isStyledByProgress);
+        List<Part> parts = NotificationProgressBar.processAndConvertToViewParts(
+                segments, points, progress, progressMax);
 
-        // Colors with 50% opacity
-        int fadedGreen = 0x7F00FF00;
-
-        List<Part> expected = new ArrayList<>(List.of(
+        List<Part> expectedParts = new ArrayList<>(List.of(
                 new Segment(0.50f, Color.RED),
-                new Segment(0.10f, Color.GREEN),
-                new Segment(0.40f, fadedGreen, true)));
+                new Segment(0.50f, Color.GREEN)));
 
-        assertThat(parts).isEqualTo(expected);
+        assertThat(parts).isEqualTo(expectedParts);
+
+        float drawableWidth = 300;
+        float segSegGap = 4;
+        float segPointGap = 4;
+        float pointRadius = 6;
+        boolean hasTrackerIcon = true;
+
+        List<NotificationProgressDrawable.Part> drawableParts =
+                NotificationProgressBar.processAndConvertToDrawableParts(parts, drawableWidth,
+                        segSegGap, segPointGap, pointRadius, hasTrackerIcon
+                );
+
+        List<NotificationProgressDrawable.Part> expectedDrawableParts = new ArrayList<>(
+                List.of(new NotificationProgressDrawable.Segment(0, 146, Color.RED),
+                        new NotificationProgressDrawable.Segment(150, 300, Color.GREEN)));
+
+        assertThat(drawableParts).isEqualTo(expectedDrawableParts);
+
+        float segmentMinWidth = 16;
+        boolean isStyledByProgress = true;
+        Pair<List<NotificationProgressDrawable.Part>, Float> p =
+                NotificationProgressBar.maybeStretchAndRescaleSegments(parts, drawableParts,
+                        segmentMinWidth, pointRadius, (float) progress / progressMax, 300,
+                        isStyledByProgress, hasTrackerIcon ? 0 : segSegGap);
+
+        // Colors with 40% opacity
+        int fadedGreen = 0x6600FF00;
+        expectedDrawableParts = new ArrayList<>(
+                List.of(new NotificationProgressDrawable.Segment(0, 146, Color.RED),
+                        new NotificationProgressDrawable.Segment(150, 180, Color.GREEN),
+                        new NotificationProgressDrawable.Segment(180, 300, fadedGreen, true)));
+
+        assertThat(p.second).isEqualTo(180);
+        assertThat(p.first).isEqualTo(expectedDrawableParts);
     }
 
     @Test
-    public void processAndConvertToDrawableParts_singleSegmentWithPoints() {
+    public void processAndConvertToParts_multipleSegmentsWithoutPoints_noTracker() {
+        List<ProgressStyle.Segment> segments = new ArrayList<>();
+        segments.add(new ProgressStyle.Segment(50).setColor(Color.RED));
+        segments.add(new ProgressStyle.Segment(50).setColor(Color.GREEN));
+        List<ProgressStyle.Point> points = new ArrayList<>();
+        int progress = 60;
+        int progressMax = 100;
+        List<Part> parts = NotificationProgressBar.processAndConvertToViewParts(segments, points,
+                progress, progressMax);
+
+        List<Part> expectedParts = new ArrayList<>(List.of(
+                new Segment(0.50f, Color.RED),
+                new Segment(0.50f, Color.GREEN)));
+
+        assertThat(parts).isEqualTo(expectedParts);
+
+        float drawableWidth = 300;
+        float segSegGap = 4;
+        float segPointGap = 4;
+        float pointRadius = 6;
+        boolean hasTrackerIcon = false;
+
+        List<NotificationProgressDrawable.Part> drawableParts =
+                NotificationProgressBar.processAndConvertToDrawableParts(parts, drawableWidth,
+                        segSegGap, segPointGap, pointRadius, hasTrackerIcon
+                );
+
+        List<NotificationProgressDrawable.Part> expectedDrawableParts = new ArrayList<>(
+                List.of(new NotificationProgressDrawable.Segment(0, 146, Color.RED),
+                        new NotificationProgressDrawable.Segment(150, 300, Color.GREEN)));
+
+        assertThat(drawableParts).isEqualTo(expectedDrawableParts);
+
+        float segmentMinWidth = 16;
+        boolean isStyledByProgress = true;
+        Pair<List<NotificationProgressDrawable.Part>, Float> p =
+                NotificationProgressBar.maybeStretchAndRescaleSegments(parts, drawableParts,
+                        segmentMinWidth, pointRadius, (float) progress / progressMax, 300,
+                        isStyledByProgress, hasTrackerIcon ? 0 : segSegGap);
+
+        // Colors with 40% opacity
+        int fadedGreen = 0x6600FF00;
+        expectedDrawableParts = new ArrayList<>(
+                List.of(new NotificationProgressDrawable.Segment(0, 146, Color.RED),
+                        new NotificationProgressDrawable.Segment(150, 176, Color.GREEN),
+                        new NotificationProgressDrawable.Segment(180, 300, fadedGreen, true)));
+
+        assertThat(p.second).isEqualTo(180);
+        assertThat(p.first).isEqualTo(expectedDrawableParts);
+    }
+
+    @Test
+    public void processAndConvertToParts_singleSegmentWithPoints() {
         List<ProgressStyle.Segment> segments = new ArrayList<>();
         segments.add(new ProgressStyle.Segment(100).setColor(Color.BLUE));
         List<ProgressStyle.Point> points = new ArrayList<>();
@@ -221,31 +344,77 @@
         points.add(new ProgressStyle.Point(75).setColor(Color.YELLOW));
         int progress = 60;
         int progressMax = 100;
+
+        List<Part> parts = NotificationProgressBar.processAndConvertToViewParts(segments, points,
+                progress, progressMax);
+
+        List<Part> expectedParts = new ArrayList<>(List.of(
+                new Segment(0.15f, Color.BLUE),
+                new Point(Color.RED),
+                new Segment(0.10f, Color.BLUE),
+                new Point(Color.BLUE),
+                new Segment(0.35f, Color.BLUE),
+                new Point(Color.BLUE),
+                new Segment(0.15f, Color.BLUE),
+                new Point(Color.YELLOW),
+                new Segment(0.25f, Color.BLUE)));
+
+        assertThat(parts).isEqualTo(expectedParts);
+
+        float drawableWidth = 300;
+        float segSegGap = 4;
+        float segPointGap = 4;
+        float pointRadius = 6;
+        boolean hasTrackerIcon = true;
+
+        List<NotificationProgressDrawable.Part> drawableParts =
+                NotificationProgressBar.processAndConvertToDrawableParts(parts, drawableWidth,
+                        segSegGap, segPointGap, pointRadius, hasTrackerIcon
+                );
+
+        List<NotificationProgressDrawable.Part> expectedDrawableParts = new ArrayList<>(
+                List.of(new NotificationProgressDrawable.Segment(0, 35, Color.BLUE),
+                        new NotificationProgressDrawable.Point(39, 51, Color.RED),
+                        new NotificationProgressDrawable.Segment(55, 65, Color.BLUE),
+                        new NotificationProgressDrawable.Point(69, 81, Color.BLUE),
+                        new NotificationProgressDrawable.Segment(85, 170, Color.BLUE),
+                        new NotificationProgressDrawable.Point(174, 186, Color.BLUE),
+                        new NotificationProgressDrawable.Segment(190, 215, Color.BLUE),
+                        new NotificationProgressDrawable.Point(219, 231, Color.YELLOW),
+                        new NotificationProgressDrawable.Segment(235, 300, Color.BLUE)));
+
+        assertThat(drawableParts).isEqualTo(expectedDrawableParts);
+
+        float segmentMinWidth = 16;
         boolean isStyledByProgress = true;
 
-        // Colors with 50% opacity
-        int fadedBlue = 0x7F0000FF;
-        int fadedYellow = 0x7FFFFF00;
+        Pair<List<NotificationProgressDrawable.Part>, Float> p =
+                NotificationProgressBar.maybeStretchAndRescaleSegments(parts, drawableParts,
+                        segmentMinWidth, pointRadius, (float) progress / progressMax, 300,
+                        isStyledByProgress, hasTrackerIcon ? 0 : segSegGap);
 
-        List<Part> expected = new ArrayList<>(List.of(
-                new Segment(0.15f, Color.BLUE),
-                new Point(null, Color.RED),
-                new Segment(0.10f, Color.BLUE),
-                new Point(null, Color.BLUE),
-                new Segment(0.35f, Color.BLUE),
-                new Point(null, Color.BLUE),
-                new Segment(0.15f, fadedBlue, true),
-                new Point(null, fadedYellow, true),
-                new Segment(0.25f, fadedBlue, true)));
+        // Colors with 40% opacity
+        int fadedBlue = 0x660000FF;
+        int fadedYellow = 0x66FFFF00;
+        expectedDrawableParts = new ArrayList<>(
+                List.of(new NotificationProgressDrawable.Segment(0, 34.219177F, Color.BLUE),
+                        new NotificationProgressDrawable.Point(38.219177F, 50.219177F, Color.RED),
+                        new NotificationProgressDrawable.Segment(54.219177F, 70.21918F, Color.BLUE),
+                        new NotificationProgressDrawable.Point(74.21918F, 86.21918F, Color.BLUE),
+                        new NotificationProgressDrawable.Segment(90.21918F, 172.38356F, Color.BLUE),
+                        new NotificationProgressDrawable.Point(176.38356F, 188.38356F, Color.BLUE),
+                        new NotificationProgressDrawable.Segment(192.38356F, 217.0137F, fadedBlue,
+                                true),
+                        new NotificationProgressDrawable.Point(221.0137F, 233.0137F, fadedYellow),
+                        new NotificationProgressDrawable.Segment(237.0137F, 300F, fadedBlue,
+                                true)));
 
-        List<Part> parts = NotificationProgressBar.processAndConvertToDrawableParts(
-                segments, points, progress, progressMax, isStyledByProgress);
-
-        assertThat(parts).isEqualTo(expected);
+        assertThat(p.second).isEqualTo(182.38356F);
+        assertThat(p.first).isEqualTo(expectedDrawableParts);
     }
 
     @Test
-    public void processAndConvertToDrawableParts_multipleSegmentsWithPoints() {
+    public void processAndConvertToParts_multipleSegmentsWithPoints() {
         List<ProgressStyle.Segment> segments = new ArrayList<>();
         segments.add(new ProgressStyle.Segment(50).setColor(Color.RED));
         segments.add(new ProgressStyle.Segment(50).setColor(Color.GREEN));
@@ -256,32 +425,81 @@
         points.add(new ProgressStyle.Point(75).setColor(Color.YELLOW));
         int progress = 60;
         int progressMax = 100;
-        boolean isStyledByProgress = true;
 
-        List<Part> parts = NotificationProgressBar.processAndConvertToDrawableParts(
-                segments, points, progress, progressMax, isStyledByProgress);
+        List<Part> parts = NotificationProgressBar.processAndConvertToViewParts(segments, points,
+                progress, progressMax);
 
-        // Colors with 50% opacity
-        int fadedGreen = 0x7F00FF00;
-        int fadedYellow = 0x7FFFFF00;
-
-        List<Part> expected = new ArrayList<>(List.of(
+        List<Part> expectedParts = new ArrayList<>(List.of(
                 new Segment(0.15f, Color.RED),
-                new Point(null, Color.RED),
+                new Point(Color.RED),
                 new Segment(0.10f, Color.RED),
-                new Point(null, Color.BLUE),
+                new Point(Color.BLUE),
                 new Segment(0.25f, Color.RED),
                 new Segment(0.10f, Color.GREEN),
-                new Point(null, Color.BLUE),
-                new Segment(0.15f, fadedGreen, true),
-                new Point(null, fadedYellow, true),
-                new Segment(0.25f, fadedGreen, true)));
+                new Point(Color.BLUE),
+                new Segment(0.15f, Color.GREEN),
+                new Point(Color.YELLOW),
+                new Segment(0.25f, Color.GREEN)));
 
-        assertThat(parts).isEqualTo(expected);
+        assertThat(parts).isEqualTo(expectedParts);
+
+        float drawableWidth = 300;
+        float segSegGap = 4;
+        float segPointGap = 4;
+        float pointRadius = 6;
+        boolean hasTrackerIcon = true;
+        List<NotificationProgressDrawable.Part> drawableParts =
+                NotificationProgressBar.processAndConvertToDrawableParts(parts, drawableWidth,
+                        segSegGap, segPointGap, pointRadius, hasTrackerIcon
+                );
+
+        List<NotificationProgressDrawable.Part> expectedDrawableParts = new ArrayList<>(
+                List.of(new NotificationProgressDrawable.Segment(0, 35, Color.RED),
+                        new NotificationProgressDrawable.Point(39, 51, Color.RED),
+                        new NotificationProgressDrawable.Segment(55, 65, Color.RED),
+                        new NotificationProgressDrawable.Point(69, 81, Color.BLUE),
+                        new NotificationProgressDrawable.Segment(85, 146, Color.RED),
+                        new NotificationProgressDrawable.Segment(150, 170, Color.GREEN),
+                        new NotificationProgressDrawable.Point(174, 186, Color.BLUE),
+                        new NotificationProgressDrawable.Segment(190, 215, Color.GREEN),
+                        new NotificationProgressDrawable.Point(219, 231, Color.YELLOW),
+                        new NotificationProgressDrawable.Segment(235, 300, Color.GREEN)));
+
+        assertThat(drawableParts).isEqualTo(expectedDrawableParts);
+
+        float segmentMinWidth = 16;
+        boolean isStyledByProgress = true;
+
+        Pair<List<NotificationProgressDrawable.Part>, Float> p =
+                NotificationProgressBar.maybeStretchAndRescaleSegments(parts, drawableParts,
+                        segmentMinWidth, pointRadius, (float) progress / progressMax, 300,
+                        isStyledByProgress, hasTrackerIcon ? 0 : segSegGap);
+
+        // Colors with 40% opacity
+        int fadedGreen = 0x6600FF00;
+        int fadedYellow = 0x66FFFF00;
+        expectedDrawableParts = new ArrayList<>(
+                List.of(new NotificationProgressDrawable.Segment(0, 34.095238F, Color.RED),
+                        new NotificationProgressDrawable.Point(38.095238F, 50.095238F, Color.RED),
+                        new NotificationProgressDrawable.Segment(54.095238F, 70.09524F, Color.RED),
+                        new NotificationProgressDrawable.Point(74.09524F, 86.09524F, Color.BLUE),
+                        new NotificationProgressDrawable.Segment(90.09524F, 148.9524F, Color.RED),
+                        new NotificationProgressDrawable.Segment(152.95238F, 172.7619F,
+                                Color.GREEN),
+                        new NotificationProgressDrawable.Point(176.7619F, 188.7619F, Color.BLUE),
+                        new NotificationProgressDrawable.Segment(192.7619F, 217.33333F,
+                                fadedGreen, true),
+                        new NotificationProgressDrawable.Point(221.33333F, 233.33333F,
+                                fadedYellow),
+                        new NotificationProgressDrawable.Segment(237.33333F, 299.99997F,
+                                fadedGreen, true)));
+
+        assertThat(p.second).isEqualTo(182.7619F);
+        assertThat(p.first).isEqualTo(expectedDrawableParts);
     }
 
     @Test
-    public void processAndConvertToDrawableParts_multipleSegmentsWithPoints_notStyledByProgress() {
+    public void processAndConvertToParts_multipleSegmentsWithPoints_notStyledByProgress() {
         List<ProgressStyle.Segment> segments = new ArrayList<>();
         segments.add(new ProgressStyle.Segment(50).setColor(Color.RED));
         segments.add(new ProgressStyle.Segment(50).setColor(Color.GREEN));
@@ -291,21 +509,251 @@
         points.add(new ProgressStyle.Point(75).setColor(Color.YELLOW));
         int progress = 60;
         int progressMax = 100;
-        boolean isStyledByProgress = false;
 
-        List<Part> parts = NotificationProgressBar.processAndConvertToDrawableParts(
-                segments, points, progress, progressMax, isStyledByProgress);
+        List<Part> parts = NotificationProgressBar.processAndConvertToViewParts(segments, points,
+                progress, progressMax);
 
-        List<Part> expected = new ArrayList<>(List.of(
+        List<Part> expectedParts = new ArrayList<>(List.of(
                 new Segment(0.15f, Color.RED),
-                new Point(null, Color.RED),
+                new Point(Color.RED),
                 new Segment(0.10f, Color.RED),
-                new Point(null, Color.BLUE),
+                new Point(Color.BLUE),
                 new Segment(0.25f, Color.RED),
                 new Segment(0.25f, Color.GREEN),
-                new Point(null, Color.YELLOW),
+                new Point(Color.YELLOW),
                 new Segment(0.25f, Color.GREEN)));
 
-        assertThat(parts).isEqualTo(expected);
+        assertThat(parts).isEqualTo(expectedParts);
+
+        float drawableWidth = 300;
+        float segSegGap = 4;
+        float segPointGap = 4;
+        float pointRadius = 6;
+        boolean hasTrackerIcon = true;
+
+        List<NotificationProgressDrawable.Part> drawableParts =
+                NotificationProgressBar.processAndConvertToDrawableParts(parts, drawableWidth,
+                        segSegGap, segPointGap, pointRadius, hasTrackerIcon
+                );
+
+        List<NotificationProgressDrawable.Part> expectedDrawableParts = new ArrayList<>(
+                List.of(new NotificationProgressDrawable.Segment(0, 35, Color.RED),
+                        new NotificationProgressDrawable.Point(39, 51, Color.RED),
+                        new NotificationProgressDrawable.Segment(55, 65, Color.RED),
+                        new NotificationProgressDrawable.Point(69, 81, Color.BLUE),
+                        new NotificationProgressDrawable.Segment(85, 146, Color.RED),
+                        new NotificationProgressDrawable.Segment(150, 215, Color.GREEN),
+                        new NotificationProgressDrawable.Point(219, 231, Color.YELLOW),
+                        new NotificationProgressDrawable.Segment(235, 300, Color.GREEN)));
+
+        assertThat(drawableParts).isEqualTo(expectedDrawableParts);
+
+        float segmentMinWidth = 16;
+        boolean isStyledByProgress = false;
+
+        Pair<List<NotificationProgressDrawable.Part>, Float> p =
+                NotificationProgressBar.maybeStretchAndRescaleSegments(parts, drawableParts,
+                        segmentMinWidth, pointRadius, (float) progress / progressMax, 300,
+                        isStyledByProgress, hasTrackerIcon ? 0 : segSegGap);
+
+        expectedDrawableParts = new ArrayList<>(
+                List.of(new NotificationProgressDrawable.Segment(0, 34.296295F, Color.RED),
+                        new NotificationProgressDrawable.Point(38.296295F, 50.296295F, Color.RED),
+                        new NotificationProgressDrawable.Segment(54.296295F, 70.296295F, Color.RED),
+                        new NotificationProgressDrawable.Point(74.296295F, 86.296295F, Color.BLUE),
+                        new NotificationProgressDrawable.Segment(90.296295F, 149.62962F, Color.RED),
+                        new NotificationProgressDrawable.Segment(153.62962F, 216.8148F,
+                                Color.GREEN),
+                        new NotificationProgressDrawable.Point(220.81482F, 232.81482F,
+                                Color.YELLOW),
+                        new NotificationProgressDrawable.Segment(236.81482F, 300, Color.GREEN)));
+
+        assertThat(p.second).isEqualTo(182.9037F);
+        assertThat(p.first).isEqualTo(expectedDrawableParts);
+    }
+
+    // The only difference from the `zeroWidthDrawableSegment` test below is the longer
+    // segmentMinWidth (= 16dp).
+    @Test
+    public void maybeStretchAndRescaleSegments_negativeWidthDrawableSegment() {
+        List<ProgressStyle.Segment> segments = new ArrayList<>();
+        segments.add(new ProgressStyle.Segment(100).setColor(Color.BLUE));
+        segments.add(new ProgressStyle.Segment(200).setColor(Color.BLUE));
+        segments.add(new ProgressStyle.Segment(300).setColor(Color.BLUE));
+        segments.add(new ProgressStyle.Segment(400).setColor(Color.BLUE));
+        List<ProgressStyle.Point> points = new ArrayList<>();
+        points.add(new ProgressStyle.Point(0).setColor(Color.BLUE));
+        int progress = 1000;
+        int progressMax = 1000;
+
+        List<Part> parts = NotificationProgressBar.processAndConvertToViewParts(
+                segments, points, progress, progressMax);
+
+        List<Part> expectedParts = new ArrayList<>(List.of(
+                new Point(Color.BLUE),
+                new Segment(0.1f, Color.BLUE),
+                new Segment(0.2f, Color.BLUE),
+                new Segment(0.3f, Color.BLUE),
+                new Segment(0.4f, Color.BLUE)));
+
+        assertThat(parts).isEqualTo(expectedParts);
+
+        float drawableWidth = 200;
+        float segSegGap = 4;
+        float segPointGap = 4;
+        float pointRadius = 6;
+        boolean hasTrackerIcon = true;
+        List<NotificationProgressDrawable.Part> drawableParts =
+                NotificationProgressBar.processAndConvertToDrawableParts(parts, drawableWidth,
+                        segSegGap, segPointGap, pointRadius, hasTrackerIcon
+                );
+
+        List<NotificationProgressDrawable.Part> expectedDrawableParts = new ArrayList<>(
+                List.of(new NotificationProgressDrawable.Point(0, 12, Color.BLUE),
+                        new NotificationProgressDrawable.Segment(16, 16, Color.BLUE),
+                        new NotificationProgressDrawable.Segment(20, 56, Color.BLUE),
+                        new NotificationProgressDrawable.Segment(60, 116, Color.BLUE),
+                        new NotificationProgressDrawable.Segment(120, 200, Color.BLUE)));
+
+        assertThat(drawableParts).isEqualTo(expectedDrawableParts);
+
+        float segmentMinWidth = 16;
+        boolean isStyledByProgress = true;
+
+        Pair<List<NotificationProgressDrawable.Part>, Float> p =
+                NotificationProgressBar.maybeStretchAndRescaleSegments(parts, drawableParts,
+                        segmentMinWidth, pointRadius, (float) progress / progressMax, 200,
+                        isStyledByProgress, hasTrackerIcon ? 0 : segSegGap);
+
+        expectedDrawableParts = new ArrayList<>(
+                List.of(new NotificationProgressDrawable.Point(0, 12, Color.BLUE),
+                        new NotificationProgressDrawable.Segment(16, 32, Color.BLUE),
+                        new NotificationProgressDrawable.Segment(36, 69.41936F, Color.BLUE),
+                        new NotificationProgressDrawable.Segment(73.41936F, 124.25807F, Color.BLUE),
+                        new NotificationProgressDrawable.Segment(128.25807F, 200, Color.BLUE)));
+
+        assertThat(p.second).isEqualTo(200);
+        assertThat(p.first).isEqualTo(expectedDrawableParts);
+    }
+
+    // The only difference from the `negativeWidthDrawableSegment` test above is the shorter
+    // segmentMinWidth (= 10dp).
+    @Test
+    public void maybeStretchAndRescaleSegments_zeroWidthDrawableSegment() {
+        List<ProgressStyle.Segment> segments = new ArrayList<>();
+        segments.add(new ProgressStyle.Segment(100).setColor(Color.BLUE));
+        segments.add(new ProgressStyle.Segment(200).setColor(Color.BLUE));
+        segments.add(new ProgressStyle.Segment(300).setColor(Color.BLUE));
+        segments.add(new ProgressStyle.Segment(400).setColor(Color.BLUE));
+        List<ProgressStyle.Point> points = new ArrayList<>();
+        points.add(new ProgressStyle.Point(0).setColor(Color.BLUE));
+        int progress = 1000;
+        int progressMax = 1000;
+
+        List<Part> parts = NotificationProgressBar.processAndConvertToViewParts(
+                segments, points, progress, progressMax);
+
+        List<Part> expectedParts = new ArrayList<>(List.of(
+                new Point(Color.BLUE),
+                new Segment(0.1f, Color.BLUE),
+                new Segment(0.2f, Color.BLUE),
+                new Segment(0.3f, Color.BLUE),
+                new Segment(0.4f, Color.BLUE)));
+
+        assertThat(parts).isEqualTo(expectedParts);
+
+        float drawableWidth = 200;
+        float segSegGap = 4;
+        float segPointGap = 4;
+        float pointRadius = 6;
+        boolean hasTrackerIcon = true;
+        List<NotificationProgressDrawable.Part> drawableParts =
+                NotificationProgressBar.processAndConvertToDrawableParts(parts, drawableWidth,
+                        segSegGap, segPointGap, pointRadius, hasTrackerIcon
+                );
+
+        List<NotificationProgressDrawable.Part> expectedDrawableParts = new ArrayList<>(
+                List.of(new NotificationProgressDrawable.Point(0, 12, Color.BLUE),
+                        new NotificationProgressDrawable.Segment(16, 16, Color.BLUE),
+                        new NotificationProgressDrawable.Segment(20, 56, Color.BLUE),
+                        new NotificationProgressDrawable.Segment(60, 116, Color.BLUE),
+                        new NotificationProgressDrawable.Segment(120, 200, Color.BLUE)));
+
+        assertThat(drawableParts).isEqualTo(expectedDrawableParts);
+
+        float segmentMinWidth = 10;
+        boolean isStyledByProgress = true;
+
+        Pair<List<NotificationProgressDrawable.Part>, Float> p =
+                NotificationProgressBar.maybeStretchAndRescaleSegments(parts, drawableParts,
+                        segmentMinWidth, pointRadius, (float) progress / progressMax, 200,
+                        isStyledByProgress, hasTrackerIcon ? 0 : segSegGap);
+
+        expectedDrawableParts = new ArrayList<>(
+                List.of(new NotificationProgressDrawable.Point(0, 12, Color.BLUE),
+                        new NotificationProgressDrawable.Segment(16, 26, Color.BLUE),
+                        new NotificationProgressDrawable.Segment(30, 64.169014F, Color.BLUE),
+                        new NotificationProgressDrawable.Segment(68.169014F, 120.92958F,
+                                Color.BLUE),
+                        new NotificationProgressDrawable.Segment(124.92958F, 200, Color.BLUE)));
+
+        assertThat(p.second).isEqualTo(200);
+        assertThat(p.first).isEqualTo(expectedDrawableParts);
+    }
+
+    @Test
+    public void maybeStretchAndRescaleSegments_noStretchingNecessary() {
+        List<ProgressStyle.Segment> segments = new ArrayList<>();
+        segments.add(new ProgressStyle.Segment(200).setColor(Color.BLUE));
+        segments.add(new ProgressStyle.Segment(100).setColor(Color.BLUE));
+        segments.add(new ProgressStyle.Segment(300).setColor(Color.BLUE));
+        segments.add(new ProgressStyle.Segment(400).setColor(Color.BLUE));
+        List<ProgressStyle.Point> points = new ArrayList<>();
+        points.add(new ProgressStyle.Point(0).setColor(Color.BLUE));
+        int progress = 1000;
+        int progressMax = 1000;
+
+        List<Part> parts = NotificationProgressBar.processAndConvertToViewParts(
+                segments, points, progress, progressMax);
+
+        List<Part> expectedParts = new ArrayList<>(List.of(
+                new Point(Color.BLUE),
+                new Segment(0.2f, Color.BLUE),
+                new Segment(0.1f, Color.BLUE),
+                new Segment(0.3f, Color.BLUE),
+                new Segment(0.4f, Color.BLUE)));
+
+        assertThat(parts).isEqualTo(expectedParts);
+
+        float drawableWidth = 200;
+        float segSegGap = 4;
+        float segPointGap = 4;
+        float pointRadius = 6;
+        boolean hasTrackerIcon = true;
+
+        List<NotificationProgressDrawable.Part> drawableParts =
+                NotificationProgressBar.processAndConvertToDrawableParts(parts, drawableWidth,
+                        segSegGap, segPointGap, pointRadius, hasTrackerIcon
+                );
+
+        List<NotificationProgressDrawable.Part> expectedDrawableParts = new ArrayList<>(
+                List.of(new NotificationProgressDrawable.Point(0, 12, Color.BLUE),
+                        new NotificationProgressDrawable.Segment(16, 36, Color.BLUE),
+                        new NotificationProgressDrawable.Segment(40, 56, Color.BLUE),
+                        new NotificationProgressDrawable.Segment(60, 116, Color.BLUE),
+                        new NotificationProgressDrawable.Segment(120, 200, Color.BLUE)));
+
+        assertThat(drawableParts).isEqualTo(expectedDrawableParts);
+
+        float segmentMinWidth = 10;
+        boolean isStyledByProgress = true;
+
+        Pair<List<NotificationProgressDrawable.Part>, Float> p =
+                NotificationProgressBar.maybeStretchAndRescaleSegments(parts, drawableParts,
+                        segmentMinWidth, pointRadius, (float) progress / progressMax, 200,
+                        isStyledByProgress, hasTrackerIcon ? 0 : segSegGap);
+
+        assertThat(p.second).isEqualTo(200);
+        assertThat(p.first).isEqualTo(expectedDrawableParts);
     }
 }
diff --git a/core/tests/systemproperties/Android.bp b/core/tests/systemproperties/Android.bp
index ed99a1f..9197dec 100644
--- a/core/tests/systemproperties/Android.bp
+++ b/core/tests/systemproperties/Android.bp
@@ -44,4 +44,5 @@
         "src/**/*.java",
     ],
     auto_gen_config: true,
+    team: "trendy_team_ravenwood",
 }
diff --git a/core/tests/timetests/src/android/app/time/TimeZoneCapabilitiesTest.java b/core/tests/timetests/src/android/app/time/TimeZoneCapabilitiesTest.java
index e368d28..cb8b5ce 100644
--- a/core/tests/timetests/src/android/app/time/TimeZoneCapabilitiesTest.java
+++ b/core/tests/timetests/src/android/app/time/TimeZoneCapabilitiesTest.java
@@ -48,12 +48,14 @@
                 .setConfigureAutoDetectionEnabledCapability(CAPABILITY_POSSESSED)
                 .setUseLocationEnabled(true)
                 .setConfigureGeoDetectionEnabledCapability(CAPABILITY_POSSESSED)
-                .setSetManualTimeZoneCapability(CAPABILITY_POSSESSED);
+                .setSetManualTimeZoneCapability(CAPABILITY_POSSESSED)
+                .setConfigureNotificationsEnabledCapability(CAPABILITY_POSSESSED);
         TimeZoneCapabilities.Builder builder2 = new TimeZoneCapabilities.Builder(TEST_USER_HANDLE)
                 .setConfigureAutoDetectionEnabledCapability(CAPABILITY_POSSESSED)
                 .setUseLocationEnabled(true)
                 .setConfigureGeoDetectionEnabledCapability(CAPABILITY_POSSESSED)
-                .setSetManualTimeZoneCapability(CAPABILITY_POSSESSED);
+                .setSetManualTimeZoneCapability(CAPABILITY_POSSESSED)
+                .setConfigureNotificationsEnabledCapability(CAPABILITY_POSSESSED);
         {
             TimeZoneCapabilities one = builder1.build();
             TimeZoneCapabilities two = builder2.build();
@@ -115,6 +117,13 @@
             TimeZoneCapabilities two = builder2.build();
             assertEquals(one, two);
         }
+
+        builder1.setConfigureNotificationsEnabledCapability(CAPABILITY_NOT_ALLOWED);
+        {
+            TimeZoneCapabilities one = builder1.build();
+            TimeZoneCapabilities two = builder2.build();
+            assertNotEquals(one, two);
+        }
     }
 
     @Test
@@ -123,7 +132,8 @@
                 .setConfigureAutoDetectionEnabledCapability(CAPABILITY_POSSESSED)
                 .setUseLocationEnabled(true)
                 .setConfigureGeoDetectionEnabledCapability(CAPABILITY_POSSESSED)
-                .setSetManualTimeZoneCapability(CAPABILITY_POSSESSED);
+                .setSetManualTimeZoneCapability(CAPABILITY_POSSESSED)
+                .setConfigureNotificationsEnabledCapability(CAPABILITY_POSSESSED);
         assertRoundTripParcelable(builder.build());
 
         builder.setConfigureAutoDetectionEnabledCapability(CAPABILITY_NOT_ALLOWED);
@@ -137,6 +147,9 @@
 
         builder.setSetManualTimeZoneCapability(CAPABILITY_NOT_ALLOWED);
         assertRoundTripParcelable(builder.build());
+
+        builder.setConfigureNotificationsEnabledCapability(CAPABILITY_NOT_ALLOWED);
+        assertRoundTripParcelable(builder.build());
     }
 
     @Test
@@ -151,6 +164,7 @@
                 .setUseLocationEnabled(true)
                 .setConfigureGeoDetectionEnabledCapability(CAPABILITY_POSSESSED)
                 .setSetManualTimeZoneCapability(CAPABILITY_POSSESSED)
+                .setConfigureNotificationsEnabledCapability(CAPABILITY_POSSESSED)
                 .build();
 
         TimeZoneConfiguration configChange = new TimeZoneConfiguration.Builder()
@@ -175,6 +189,7 @@
                 .setUseLocationEnabled(true)
                 .setConfigureGeoDetectionEnabledCapability(CAPABILITY_NOT_ALLOWED)
                 .setSetManualTimeZoneCapability(CAPABILITY_NOT_ALLOWED)
+                .setConfigureNotificationsEnabledCapability(CAPABILITY_NOT_ALLOWED)
                 .build();
 
         TimeZoneConfiguration configChange = new TimeZoneConfiguration.Builder()
@@ -191,6 +206,7 @@
                 .setUseLocationEnabled(true)
                 .setConfigureGeoDetectionEnabledCapability(CAPABILITY_NOT_ALLOWED)
                 .setSetManualTimeZoneCapability(CAPABILITY_NOT_ALLOWED)
+                .setConfigureNotificationsEnabledCapability(CAPABILITY_NOT_ALLOWED)
                 .build();
 
         {
@@ -204,6 +220,7 @@
                             .setUseLocationEnabled(true)
                             .setConfigureGeoDetectionEnabledCapability(CAPABILITY_NOT_ALLOWED)
                             .setSetManualTimeZoneCapability(CAPABILITY_NOT_ALLOWED)
+                            .setConfigureNotificationsEnabledCapability(CAPABILITY_NOT_ALLOWED)
                             .build();
 
             assertThat(updatedCapabilities).isEqualTo(expectedCapabilities);
@@ -221,6 +238,7 @@
                             .setUseLocationEnabled(false)
                             .setConfigureGeoDetectionEnabledCapability(CAPABILITY_NOT_ALLOWED)
                             .setSetManualTimeZoneCapability(CAPABILITY_NOT_ALLOWED)
+                            .setConfigureNotificationsEnabledCapability(CAPABILITY_NOT_ALLOWED)
                             .build();
 
             assertThat(updatedCapabilities).isEqualTo(expectedCapabilities);
@@ -238,6 +256,7 @@
                             .setUseLocationEnabled(true)
                             .setConfigureGeoDetectionEnabledCapability(CAPABILITY_POSSESSED)
                             .setSetManualTimeZoneCapability(CAPABILITY_NOT_ALLOWED)
+                            .setConfigureNotificationsEnabledCapability(CAPABILITY_NOT_ALLOWED)
                             .build();
 
             assertThat(updatedCapabilities).isEqualTo(expectedCapabilities);
@@ -255,6 +274,25 @@
                             .setUseLocationEnabled(true)
                             .setConfigureGeoDetectionEnabledCapability(CAPABILITY_NOT_ALLOWED)
                             .setSetManualTimeZoneCapability(CAPABILITY_POSSESSED)
+                            .setConfigureNotificationsEnabledCapability(CAPABILITY_NOT_ALLOWED)
+                            .build();
+
+            assertThat(updatedCapabilities).isEqualTo(expectedCapabilities);
+        }
+
+        {
+            TimeZoneCapabilities updatedCapabilities =
+                    new TimeZoneCapabilities.Builder(capabilities)
+                            .setConfigureNotificationsEnabledCapability(CAPABILITY_POSSESSED)
+                            .build();
+
+            TimeZoneCapabilities expectedCapabilities =
+                    new TimeZoneCapabilities.Builder(TEST_USER_HANDLE)
+                            .setConfigureAutoDetectionEnabledCapability(CAPABILITY_NOT_ALLOWED)
+                            .setUseLocationEnabled(true)
+                            .setConfigureGeoDetectionEnabledCapability(CAPABILITY_NOT_ALLOWED)
+                            .setSetManualTimeZoneCapability(CAPABILITY_NOT_ALLOWED)
+                            .setConfigureNotificationsEnabledCapability(CAPABILITY_POSSESSED)
                             .build();
 
             assertThat(updatedCapabilities).isEqualTo(expectedCapabilities);
diff --git a/core/tests/timetests/src/android/app/time/TimeZoneConfigurationTest.java b/core/tests/timetests/src/android/app/time/TimeZoneConfigurationTest.java
index 4ad3e41..345e912 100644
--- a/core/tests/timetests/src/android/app/time/TimeZoneConfigurationTest.java
+++ b/core/tests/timetests/src/android/app/time/TimeZoneConfigurationTest.java
@@ -43,9 +43,11 @@
         TimeZoneConfiguration completeConfig = new TimeZoneConfiguration.Builder()
                 .setAutoDetectionEnabled(true)
                 .setGeoDetectionEnabled(true)
+                .setNotificationsEnabled(true)
                 .build();
         assertTrue(completeConfig.isComplete());
         assertTrue(completeConfig.hasIsGeoDetectionEnabled());
+        assertTrue(completeConfig.hasIsNotificationsEnabled());
     }
 
     @Test
diff --git a/core/tests/utiltests/Android.bp b/core/tests/utiltests/Android.bp
index 7cf49ab..5011f7a 100644
--- a/core/tests/utiltests/Android.bp
+++ b/core/tests/utiltests/Android.bp
@@ -69,4 +69,5 @@
         "src/com/android/internal/util/**/*.java",
     ],
     auto_gen_config: true,
+    team: "trendy_team_ravenwood",
 }
diff --git a/core/tests/utiltests/src/android/util/AtomicFileBufferedOutputStreamTest.java b/core/tests/utiltests/src/android/util/AtomicFileBufferedOutputStreamTest.java
new file mode 100644
index 0000000..81eac6d
--- /dev/null
+++ b/core/tests/utiltests/src/android/util/AtomicFileBufferedOutputStreamTest.java
@@ -0,0 +1,94 @@
+/*
+ * 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.util;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.nio.file.Files;
+
+@RunWith(AndroidJUnit4.class)
+public class AtomicFileBufferedOutputStreamTest {
+    private static final String BASE_NAME = "base";
+    private File mBaseFile;
+
+    @Before
+    public void setUp() throws Exception {
+        File mDirectory = Files.createTempDirectory("AtomicFile").toFile();
+        mBaseFile = new File(mDirectory, BASE_NAME);
+    }
+
+    @After
+    public void deleteFiles() {
+        mBaseFile.delete();
+    }
+
+    @Test
+    public void testAtomicFileBufferedWrite() {
+        AtomicFile atomicFile = new AtomicFile(mBaseFile);
+        try (var oStream = new AtomicFileBufferedOutputStream(atomicFile)) {
+            oStream.write(0);
+            oStream.write(new byte[]{1, 2});
+            oStream.write(new byte[]{1, 2, 3}, /*off=*/ 2, /*len=*/ 1);
+            oStream.markSuccess();
+        } catch (IOException e) {
+            // Should never happen
+            throw new RuntimeException(e);
+        }
+
+        try {
+            assertThat(atomicFile.readFully()).isEqualTo(new byte[]{0, 1, 2, 3});
+        } catch (IOException e) {
+            // Should never happen
+            throw new RuntimeException(e);
+        }
+    }
+
+
+    @Test(expected = FileNotFoundException.class)
+    public void testAtomicFileBufferedNotFinishedWriting() throws FileNotFoundException {
+        AtomicFile atomicFile = new AtomicFile(mBaseFile);
+        try (var oStream = new AtomicFileBufferedOutputStream(atomicFile)) {
+            oStream.write(0);
+            oStream.write(new byte[]{1, 2});
+            oStream.write(new byte[]{1, 2, 3}, /*off=*/ 2, /*len=*/ 1);
+        } catch (IOException e) {
+            // Should never happen
+            throw new RuntimeException(e);
+        }
+
+        try {
+            // openRead should throw FileNotFoundException.
+            // close should never be called, but added here just in case openRead never throws.
+            atomicFile.openRead().close();
+        } catch (FileNotFoundException e) {
+            throw e;
+        } catch (IOException e) {
+            // Should never happen
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/core/tests/utiltests/src/android/util/AtomicFileBufferedPrintWriterTest.java b/core/tests/utiltests/src/android/util/AtomicFileBufferedPrintWriterTest.java
new file mode 100644
index 0000000..73d1443
--- /dev/null
+++ b/core/tests/utiltests/src/android/util/AtomicFileBufferedPrintWriterTest.java
@@ -0,0 +1,94 @@
+/*
+ * 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.util;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+
+@RunWith(AndroidJUnit4.class)
+public class AtomicFileBufferedPrintWriterTest {
+    private static final String BASE_NAME = "base";
+    private static final String HELLO_WORLD_STRING = "你好世界! ";
+    private File mBaseFile;
+
+    @Before
+    public void setUp() throws Exception {
+        File mDirectory = Files.createTempDirectory("AtomicFile").toFile();
+        mBaseFile = new File(mDirectory, BASE_NAME);
+    }
+
+    @After
+    public void deleteFiles() {
+        mBaseFile.delete();
+    }
+
+    @Test
+    public void testAtomicFileBufferedWrite() {
+        AtomicFile atomicFile = new AtomicFile(mBaseFile);
+        try (var pw = new AtomicFileBufferedPrintWriter(atomicFile, StandardCharsets.UTF_8)) {
+            pw.write(HELLO_WORLD_STRING);
+            pw.write(HELLO_WORLD_STRING, /*off=*/ 0, /*len=*/ HELLO_WORLD_STRING.length() - 1);
+            pw.markSuccess();
+        } catch (IOException e) {
+            // Should never happen
+            throw new RuntimeException(e);
+        }
+
+        try {
+            assertThat(new String(atomicFile.readFully(), StandardCharsets.UTF_8))
+                    .isEqualTo("你好世界! 你好世界!");
+        } catch (IOException e) {
+            // Should never happen
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Test(expected = FileNotFoundException.class)
+    public void testAtomicFileBufferedWriteNotFinished() throws FileNotFoundException {
+        AtomicFile atomicFile = new AtomicFile(mBaseFile);
+        try (var pw = new AtomicFileBufferedPrintWriter(atomicFile, StandardCharsets.UTF_8)) {
+            pw.write(HELLO_WORLD_STRING);
+            pw.write(HELLO_WORLD_STRING, /*off=*/ 0, /*len=*/ HELLO_WORLD_STRING.length() - 1);
+        } catch (IOException e) {
+            // Should never happen
+            throw new RuntimeException(e);
+        }
+
+        try {
+            // openRead should throw FileNotFoundException.
+            // close should never be called, but added here just in case openRead never throws.
+            atomicFile.openRead().close();
+        } catch (FileNotFoundException e) {
+            throw e;
+        } catch (IOException e) {
+            // Should never happen
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/core/tests/utiltests/src/android/util/AtomicFileOutputStreamTest.java b/core/tests/utiltests/src/android/util/AtomicFileOutputStreamTest.java
new file mode 100644
index 0000000..5823168
--- /dev/null
+++ b/core/tests/utiltests/src/android/util/AtomicFileOutputStreamTest.java
@@ -0,0 +1,94 @@
+/*
+ * 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.util;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.nio.file.Files;
+
+@RunWith(AndroidJUnit4.class)
+public class AtomicFileOutputStreamTest {
+    private static final String BASE_NAME = "base";
+    private File mBaseFile;
+
+    @Before
+    public void setUp() throws Exception {
+        File mDirectory = Files.createTempDirectory("AtomicFile").toFile();
+        mBaseFile = new File(mDirectory, BASE_NAME);
+    }
+
+    @After
+    public void deleteFiles() {
+        mBaseFile.delete();
+    }
+
+    @Test
+    public void testAtomicFileWritten() {
+        AtomicFile atomicFile = new AtomicFile(mBaseFile);
+        try (var oStream = new AtomicFileOutputStream(atomicFile)) {
+            oStream.write(0);
+            oStream.write(new byte[]{1, 2});
+            oStream.write(new byte[]{1, 2, 3}, /*off=*/ 2, /*len=*/ 1);
+            oStream.markSuccess();
+        } catch (IOException e) {
+            // Should never happen
+            throw new RuntimeException(e);
+        }
+
+        try {
+            assertThat(atomicFile.readFully()).isEqualTo(new byte[]{0, 1, 2, 3});
+        } catch (IOException e) {
+            // Should never happen
+            throw new RuntimeException(e);
+        }
+    }
+
+
+    @Test(expected = FileNotFoundException.class)
+    public void testAtomicFileNotFinishedWriting() throws FileNotFoundException {
+        AtomicFile atomicFile = new AtomicFile(mBaseFile);
+        try (var oStream = new AtomicFileOutputStream(atomicFile)) {
+            oStream.write(0);
+            oStream.write(new byte[]{1, 2});
+            oStream.write(new byte[]{1, 2, 3}, /*off=*/ 2, /*len=*/ 1);
+        } catch (IOException e) {
+            // Should never happen
+            throw new RuntimeException(e);
+        }
+
+        try {
+            // openRead should throw FileNotFoundException.
+            // close should never be called, but added here just in case openRead never throws.
+            atomicFile.openRead().close();
+        } catch (FileNotFoundException e) {
+            throw e;
+        } catch (IOException e) {
+            // Should never happen
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/core/tests/utiltests/src/android/util/AtomicFilePrintWriterTest.java b/core/tests/utiltests/src/android/util/AtomicFilePrintWriterTest.java
new file mode 100644
index 0000000..467046e
--- /dev/null
+++ b/core/tests/utiltests/src/android/util/AtomicFilePrintWriterTest.java
@@ -0,0 +1,94 @@
+/*
+ * 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.util;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+
+@RunWith(AndroidJUnit4.class)
+public class AtomicFilePrintWriterTest {
+    private static final String BASE_NAME = "base";
+    private static final String HELLO_WORLD_STRING = "你好世界! ";
+    private File mBaseFile;
+
+    @Before
+    public void setUp() throws Exception {
+        File mDirectory = Files.createTempDirectory("AtomicFile").toFile();
+        mBaseFile = new File(mDirectory, BASE_NAME);
+    }
+
+    @After
+    public void deleteFiles() {
+        mBaseFile.delete();
+    }
+
+    @Test
+    public void testAtomicFileWrite() {
+        AtomicFile atomicFile = new AtomicFile(mBaseFile);
+        try (var pw = new AtomicFilePrintWriter(atomicFile, StandardCharsets.UTF_8)) {
+            pw.write(HELLO_WORLD_STRING);
+            pw.write(HELLO_WORLD_STRING, /*off=*/ 0, /*len=*/ HELLO_WORLD_STRING.length() - 1);
+            pw.markSuccess();
+        } catch (IOException e) {
+            // Should never happen
+            throw new RuntimeException(e);
+        }
+
+        try {
+            assertThat(new String(atomicFile.readFully(), StandardCharsets.UTF_8))
+                    .isEqualTo("你好世界! 你好世界!");
+        } catch (IOException e) {
+            // Should never happen
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Test(expected = FileNotFoundException.class)
+    public void testAtomicFileWriteNotFinished() throws FileNotFoundException {
+        AtomicFile atomicFile = new AtomicFile(mBaseFile);
+        try (var pw = new AtomicFilePrintWriter(atomicFile, StandardCharsets.UTF_8)) {
+            pw.write(HELLO_WORLD_STRING);
+            pw.write(HELLO_WORLD_STRING, /*off=*/ 0, /*len=*/ HELLO_WORLD_STRING.length() - 1);
+        } catch (IOException e) {
+            // Should never happen
+            throw new RuntimeException(e);
+        }
+
+        try {
+            // openRead should throw FileNotFoundException.
+            // close should never be called, but added here just in case openRead never throws.
+            atomicFile.openRead().close();
+        } catch (FileNotFoundException e) {
+            throw e;
+        } catch (IOException e) {
+            // Should never happen
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp
index 4c75ea4..957d1b8 100644
--- a/libs/WindowManager/Shell/Android.bp
+++ b/libs/WindowManager/Shell/Android.bp
@@ -26,8 +26,8 @@
 java_library {
     name: "wm_shell_protolog-groups",
     srcs: [
-        "src/com/android/wm/shell/protolog/ShellProtoLogGroup.java",
         ":protolog-common-src",
+        "src/com/android/wm/shell/protolog/ShellProtoLogGroup.java",
     ],
 }
 
@@ -61,8 +61,8 @@
     name: "wm_shell_protolog_src",
     srcs: [
         ":protolog-impl",
-        ":wm_shell_protolog-groups",
         ":wm_shell-sources",
+        ":wm_shell_protolog-groups",
     ],
     tools: ["protologtool"],
     cmd: "$(location protologtool) transform-protolog-calls " +
@@ -80,8 +80,8 @@
 java_genrule {
     name: "generate-wm_shell_protolog.json",
     srcs: [
-        ":wm_shell_protolog-groups",
         ":wm_shell-sources",
+        ":wm_shell_protolog-groups",
     ],
     tools: ["protologtool"],
     cmd: "$(location protologtool) generate-viewer-config " +
@@ -97,8 +97,8 @@
 java_genrule {
     name: "gen-wmshell.protolog.pb",
     srcs: [
-        ":wm_shell_protolog-groups",
         ":wm_shell-sources",
+        ":wm_shell_protolog-groups",
     ],
     tools: ["protologtool"],
     cmd: "$(location protologtool) generate-viewer-config " +
@@ -159,38 +159,39 @@
 android_library {
     name: "WindowManager-Shell",
     srcs: [
-        "src/com/android/wm/shell/EventLogTags.logtags",
         ":wm_shell_protolog_src",
         // TODO(b/168581922) protologtool do not support kotlin(*.kt)
-        ":wm_shell-sources-kt",
+        "src/com/android/wm/shell/EventLogTags.logtags",
         ":wm_shell-aidls",
         ":wm_shell-shared-aidls",
+        ":wm_shell-sources-kt",
     ],
     resource_dirs: [
         "res",
     ],
     static_libs: [
-        "androidx.appcompat_appcompat",
-        "androidx.core_core-ktx",
-        "androidx.arch.core_core-runtime",
-        "androidx.datastore_datastore",
-        "androidx.compose.material3_material3",
-        "androidx-constraintlayout_constraintlayout",
-        "androidx.dynamicanimation_dynamicanimation",
-        "androidx.recyclerview_recyclerview",
-        "kotlinx-coroutines-android",
-        "kotlinx-coroutines-core",
         "//frameworks/libs/systemui:com_android_systemui_shared_flags_lib",
         "//frameworks/libs/systemui:iconloader_base",
+        "//packages/apps/Car/SystemUI/aconfig:com_android_systemui_car_flags_lib",
+        "PlatformAnimationLib",
+        "WindowManager-Shell-lite-proto",
+        "WindowManager-Shell-proto",
+        "WindowManager-Shell-shared",
+        "androidx-constraintlayout_constraintlayout",
+        "androidx.appcompat_appcompat",
+        "androidx.arch.core_core-runtime",
+        "androidx.compose.material3_material3",
+        "androidx.core_core-ktx",
+        "androidx.datastore_datastore",
+        "androidx.dynamicanimation_dynamicanimation",
+        "androidx.recyclerview_recyclerview",
         "com_android_launcher3_flags_lib",
         "com_android_wm_shell_flags_lib",
-        "PlatformAnimationLib",
-        "WindowManager-Shell-proto",
-        "WindowManager-Shell-lite-proto",
-        "WindowManager-Shell-shared",
-        "perfetto_trace_java_protos",
         "dagger2",
         "jsr330",
+        "kotlinx-coroutines-android",
+        "kotlinx-coroutines-core",
+        "perfetto_trace_java_protos",
     ],
     libs: [
         // Soong fails to automatically add this dependency because all the
diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
index bbdcbc9..688bf83 100644
--- a/libs/WindowManager/Shell/aconfig/multitasking.aconfig
+++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
@@ -1,4 +1,6 @@
 # proto-file: build/make/tools/aconfig/aconfig_protos/protos/aconfig.proto
+# proto-message: flag_declarations
+# Project link: http://gantry/projects/android_platform_multitasking
 
 package: "com.android.wm.shell"
 container: "system"
diff --git a/libs/WindowManager/Shell/multivalentTests/Android.bp b/libs/WindowManager/Shell/multivalentTests/Android.bp
index eecf199..03076c0 100644
--- a/libs/WindowManager/Shell/multivalentTests/Android.bp
+++ b/libs/WindowManager/Shell/multivalentTests/Android.bp
@@ -35,7 +35,6 @@
 android_robolectric_test {
     name: "WMShellRobolectricTests",
     instrumentation_for: "WindowManagerShellRobolectric",
-    upstream: true,
     java_resource_dirs: [
         "robolectric/config",
     ],
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 239acd3..ab2e552 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
@@ -49,7 +49,9 @@
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.Mockito
+import org.mockito.kotlin.any
 import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
 import org.mockito.kotlin.verify
 import java.util.concurrent.Semaphore
 import java.util.concurrent.TimeUnit
@@ -175,6 +177,137 @@
     }
 
     @Test
+    fun expandStack_imeHidden() {
+        val bubble = createAndInflateBubble()
+
+        InstrumentationRegistry.getInstrumentation().runOnMainSync {
+            bubbleStackView.addBubble(bubble)
+        }
+
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync()
+        assertThat(bubbleStackView.bubbleCount).isEqualTo(1)
+
+        positioner.setImeVisible(false, 0)
+
+        InstrumentationRegistry.getInstrumentation().runOnMainSync {
+            // simulate a request from the bubble data listener to expand the stack
+            bubbleStackView.isExpanded = true
+            verify(sysuiProxy).onStackExpandChanged(true)
+            shellExecutor.flushAll()
+        }
+
+        assertThat(bubbleStackViewManager.onImeHidden).isNull()
+    }
+
+    @Test
+    fun collapseStack_imeHidden() {
+        val bubble = createAndInflateBubble()
+
+        InstrumentationRegistry.getInstrumentation().runOnMainSync {
+            bubbleStackView.addBubble(bubble)
+        }
+
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync()
+        assertThat(bubbleStackView.bubbleCount).isEqualTo(1)
+
+        positioner.setImeVisible(false, 0)
+
+        InstrumentationRegistry.getInstrumentation().runOnMainSync {
+            // simulate a request from the bubble data listener to expand the stack
+            bubbleStackView.isExpanded = true
+            verify(sysuiProxy).onStackExpandChanged(true)
+            shellExecutor.flushAll()
+        }
+
+        assertThat(bubbleStackViewManager.onImeHidden).isNull()
+
+        InstrumentationRegistry.getInstrumentation().runOnMainSync {
+            // simulate a request from the bubble data listener to collapse the stack
+            bubbleStackView.isExpanded = false
+            verify(sysuiProxy).onStackExpandChanged(false)
+            shellExecutor.flushAll()
+        }
+
+        assertThat(bubbleStackViewManager.onImeHidden).isNull()
+    }
+
+    @Test
+    fun expandStack_waitsForIme() {
+        val bubble = createAndInflateBubble()
+
+        InstrumentationRegistry.getInstrumentation().runOnMainSync {
+            bubbleStackView.addBubble(bubble)
+        }
+
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync()
+        assertThat(bubbleStackView.bubbleCount).isEqualTo(1)
+
+        positioner.setImeVisible(true, 100)
+
+        InstrumentationRegistry.getInstrumentation().runOnMainSync {
+            // simulate a request from the bubble data listener to expand the stack
+            bubbleStackView.isExpanded = true
+        }
+
+        val onImeHidden = bubbleStackViewManager.onImeHidden
+        assertThat(onImeHidden).isNotNull()
+        verify(sysuiProxy, never()).onStackExpandChanged(any())
+        positioner.setImeVisible(false, 0)
+        InstrumentationRegistry.getInstrumentation().runOnMainSync {
+            onImeHidden!!.run()
+            verify(sysuiProxy).onStackExpandChanged(true)
+            shellExecutor.flushAll()
+        }
+    }
+
+    @Test
+    fun collapseStack_waitsForIme() {
+        val bubble = createAndInflateBubble()
+
+        InstrumentationRegistry.getInstrumentation().runOnMainSync {
+            bubbleStackView.addBubble(bubble)
+        }
+
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync()
+        assertThat(bubbleStackView.bubbleCount).isEqualTo(1)
+
+        positioner.setImeVisible(true, 100)
+
+        InstrumentationRegistry.getInstrumentation().runOnMainSync {
+            // simulate a request from the bubble data listener to expand the stack
+            bubbleStackView.isExpanded = true
+        }
+
+        var onImeHidden = bubbleStackViewManager.onImeHidden
+        assertThat(onImeHidden).isNotNull()
+        verify(sysuiProxy, never()).onStackExpandChanged(any())
+        positioner.setImeVisible(false, 0)
+        InstrumentationRegistry.getInstrumentation().runOnMainSync {
+            onImeHidden!!.run()
+            verify(sysuiProxy).onStackExpandChanged(true)
+            shellExecutor.flushAll()
+        }
+
+        bubbleStackViewManager.onImeHidden = null
+        positioner.setImeVisible(true, 100)
+
+        InstrumentationRegistry.getInstrumentation().runOnMainSync {
+            // simulate a request from the bubble data listener to collapse the stack
+            bubbleStackView.isExpanded = false
+        }
+
+        onImeHidden = bubbleStackViewManager.onImeHidden
+        assertThat(onImeHidden).isNotNull()
+        verify(sysuiProxy, never()).onStackExpandChanged(false)
+        positioner.setImeVisible(false, 0)
+        InstrumentationRegistry.getInstrumentation().runOnMainSync {
+            onImeHidden!!.run()
+            verify(sysuiProxy).onStackExpandChanged(false)
+            shellExecutor.flushAll()
+        }
+    }
+
+    @Test
     fun tapDifferentBubble_shouldReorder() {
         val bubble1 = createAndInflateChatBubble(key = "bubble1")
         val bubble2 = createAndInflateChatBubble(key = "bubble2")
@@ -418,6 +551,7 @@
     }
 
     private class FakeBubbleStackViewManager : BubbleStackViewManager {
+        var onImeHidden: Runnable? = null
 
         override fun onAllBubblesAnimatedOut() {}
 
@@ -425,6 +559,8 @@
 
         override fun checkNotificationPanelExpandedState(callback: Consumer<Boolean>) {}
 
-        override fun hideCurrentInputMethod() {}
+        override fun hideCurrentInputMethod(onImeHidden: Runnable?) {
+            this.onImeHidden = onImeHidden
+        }
     }
 }
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelperTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelperTest.kt
index 957f0ca..c45f690 100644
--- a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelperTest.kt
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelperTest.kt
@@ -232,11 +232,16 @@
         val taskController = mock<TaskViewTaskController>()
         val bubble = createBubble("key", taskController).initialize(container)
 
+        val semaphore = Semaphore(0)
+        val after = Runnable { semaphore.release() }
+
         activityScenario.onActivity {
-            animationHelper.animateExpansion(bubble) {}
+            animationHelper.animateExpansion(bubble, after)
             animatorTestRule.advanceTimeBy(1000)
         }
         getInstrumentation().waitForIdleSync()
+        assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue()
+
         getInstrumentation().runOnMainSync {
             animationHelper.animateToRestPosition()
             animatorTestRule.advanceTimeBy(100)
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 fcf74e3..87c520c 100644
--- a/libs/WindowManager/Shell/res/layout/desktop_mode_app_header.xml
+++ b/libs/WindowManager/Shell/res/layout/desktop_mode_app_header.xml
@@ -48,7 +48,6 @@
             android:layout_width="0dp"
             android:layout_height="wrap_content"
             android:maxWidth="130dp"
-            android:textAppearance="@android:style/TextAppearance.Material.Title"
             android:textSize="14sp"
             android:textFontWeight="500"
             android:lineHeight="20sp"
@@ -56,9 +55,7 @@
             android:layout_weight="1"
             android:layout_marginStart="8dp"
             android:singleLine="true"
-            android:ellipsize="none"
-            android:requiresFadingEdge="horizontal"
-            android:fadingEdgeLength="28dp"
+            android:ellipsize="end"
             android:clickable="false"
             android:focusable="false"
             tools:text="Gmail"/>
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedTaskInfo.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedTaskInfo.java
index 4300e84..2ca011b 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedTaskInfo.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedTaskInfo.java
@@ -16,10 +16,12 @@
 
 package com.android.wm.shell.shared;
 
+import static android.app.WindowConfiguration.windowingModeToString;
+import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
+
 import android.annotation.IntDef;
 import android.app.ActivityManager.RecentTaskInfo;
 import android.app.TaskInfo;
-import android.app.WindowConfiguration;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -28,11 +30,14 @@
 
 import com.android.wm.shell.shared.split.SplitBounds;
 
+import kotlin.collections.CollectionsKt;
+
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
 import java.util.Set;
+import java.util.stream.Collectors;
 
 /**
  * Simple container for recent tasks which should be presented as a single task within the
@@ -43,11 +48,13 @@
     public static final int TYPE_FULLSCREEN = 1;
     public static final int TYPE_SPLIT = 2;
     public static final int TYPE_FREEFORM = 3;
+    public static final int TYPE_MIXED = 4;
 
     @IntDef(prefix = {"TYPE_"}, value = {
             TYPE_FULLSCREEN,
             TYPE_SPLIT,
-            TYPE_FREEFORM
+            TYPE_FREEFORM,
+            TYPE_MIXED
     })
     public @interface GroupType {}
 
@@ -64,7 +71,7 @@
      * TYPE_SPLIT: Contains the two split roots of each side
      * TYPE_FREEFORM: Contains the set of tasks currently in freeform mode
      */
-    @NonNull
+    @Nullable
     protected final List<TaskInfo> mTasks;
 
     /**
@@ -85,6 +92,14 @@
     protected final int[] mMinimizedTaskIds;
 
     /**
+     * Only set for TYPE_MIXED.
+     *
+     * The mixed set of task infos in this group.
+     */
+    @Nullable
+    protected final List<GroupedTaskInfo> mGroupedTasks;
+
+    /**
      * Create new for a stack of fullscreen tasks
      */
     public static GroupedTaskInfo forFullscreenTasks(@NonNull TaskInfo task) {
@@ -111,18 +126,41 @@
                 minimizedFreeformTasks.stream().mapToInt(i -> i).toArray());
     }
 
+    /**
+     * Create new for a group of grouped task infos, those grouped task infos may not be mixed
+     * themselves (ie. multiple depths of mixed grouped task infos are not allowed).
+     */
+    public static GroupedTaskInfo forMixed(@NonNull List<GroupedTaskInfo> groupedTasks) {
+        if (groupedTasks.isEmpty()) {
+            throw new IllegalArgumentException("Expected non-empty grouped task list");
+        }
+        if (groupedTasks.stream().anyMatch(task -> task.mType == TYPE_MIXED)) {
+            throw new IllegalArgumentException("Unexpected grouped task list");
+        }
+        return new GroupedTaskInfo(groupedTasks);
+    }
+
     private GroupedTaskInfo(
             @NonNull List<TaskInfo> tasks,
             @Nullable SplitBounds splitBounds,
             @GroupType int type,
             @Nullable int[] minimizedFreeformTaskIds) {
         mTasks = tasks;
+        mGroupedTasks = null;
         mSplitBounds = splitBounds;
         mType = type;
         mMinimizedTaskIds = minimizedFreeformTaskIds;
         ensureAllMinimizedIdsPresent(tasks, minimizedFreeformTaskIds);
     }
 
+    private GroupedTaskInfo(@NonNull List<GroupedTaskInfo> groupedTasks) {
+        mTasks = null;
+        mGroupedTasks = groupedTasks;
+        mSplitBounds = null;
+        mType = TYPE_MIXED;
+        mMinimizedTaskIds = null;
+    }
+
     private void ensureAllMinimizedIdsPresent(
             @NonNull List<TaskInfo> tasks,
             @Nullable int[] minimizedFreeformTaskIds) {
@@ -141,26 +179,47 @@
         for (int i = 0; i < numTasks; i++) {
             mTasks.add(new TaskInfo(parcel));
         }
+        mGroupedTasks = parcel.createTypedArrayList(GroupedTaskInfo.CREATOR);
         mSplitBounds = parcel.readTypedObject(SplitBounds.CREATOR);
         mType = parcel.readInt();
         mMinimizedTaskIds = parcel.createIntArray();
     }
 
     /**
-     * Get primary {@link RecentTaskInfo}
+     * If TYPE_MIXED, returns the root of the grouped tasks
+     * For all other types, returns this task itself
+     */
+    @NonNull
+    public GroupedTaskInfo getBaseGroupedTask() {
+        if (mType == TYPE_MIXED) {
+            return mGroupedTasks.getFirst();
+        }
+        return this;
+    }
+
+    /**
+     * Get primary {@link TaskInfo}.
+     *
+     * @throws IllegalStateException if the group is TYPE_MIXED.
      */
     @NonNull
     public TaskInfo getTaskInfo1() {
+        if (mType == TYPE_MIXED) {
+            throw new IllegalStateException("No indexed tasks for a mixed task");
+        }
         return mTasks.getFirst();
     }
 
     /**
-     * Get secondary {@link RecentTaskInfo}.
+     * Get secondary {@link TaskInfo}, used primarily for TYPE_SPLIT.
      *
-     * Used in split screen.
+     * @throws IllegalStateException if the group is TYPE_MIXED.
      */
     @Nullable
     public TaskInfo getTaskInfo2() {
+        if (mType == TYPE_MIXED) {
+            throw new IllegalStateException("No indexed tasks for a mixed task");
+        }
         if (mTasks.size() > 1) {
             return mTasks.get(1);
         }
@@ -172,9 +231,7 @@
      */
     @Nullable
     public TaskInfo getTaskById(int taskId) {
-        return mTasks.stream()
-                .filter(task -> task.taskId == taskId)
-                .findFirst().orElse(null);
+        return CollectionsKt.firstOrNull(getTaskInfoList(), taskInfo -> taskInfo.taskId == taskId);
     }
 
     /**
@@ -182,35 +239,59 @@
      */
     @NonNull
     public List<TaskInfo> getTaskInfoList() {
-        return mTasks;
+        if (mType == TYPE_MIXED) {
+            return CollectionsKt.flatMap(mGroupedTasks, groupedTaskInfo -> groupedTaskInfo.mTasks);
+        } else {
+            return mTasks;
+        }
     }
 
     /**
      * @return Whether this grouped task contains a task with the given {@code taskId}.
      */
     public boolean containsTask(int taskId) {
-        return mTasks.stream()
-                .anyMatch((task -> task.taskId == taskId));
+        return getTaskById(taskId) != null;
     }
 
     /**
-     * Return {@link SplitBounds} if this is a split screen entry or {@code null}
+     * Returns whether the group is of the given type, if this is a TYPE_MIXED group, then returns
+     * whether the root task info is of the given type.
+     */
+    public boolean isBaseType(@GroupType int type) {
+        return getBaseGroupedTask().mType == type;
+    }
+
+    /**
+     * Return {@link SplitBounds} if this is a split screen entry or {@code null}. Only valid for
+     * TYPE_SPLIT.
      */
     @Nullable
     public SplitBounds getSplitBounds() {
+        if (mType == TYPE_MIXED) {
+            throw new IllegalStateException("No split bounds for a mixed task");
+        }
         return mSplitBounds;
     }
 
     /**
-     * Get type of this recents entry. One of {@link GroupType}
+     * Get type of this recents entry. One of {@link GroupType}.
+     * Note: This is deprecated, callers should use `isBaseType()` and not make assumptions about
+     *       specific group types
      */
+    @Deprecated
     @GroupType
     public int getType() {
         return mType;
     }
 
+    /**
+     * Returns the set of minimized task ids, only valid for TYPE_FREEFORM.
+     */
     @Nullable
     public int[] getMinimizedTaskIds() {
+        if (mType == TYPE_MIXED) {
+            throw new IllegalStateException("No minimized task ids for a mixed task");
+        }
         return mMinimizedTaskIds;
     }
 
@@ -222,67 +303,64 @@
         GroupedTaskInfo other = (GroupedTaskInfo) obj;
         return mType == other.mType
                 && Objects.equals(mTasks, other.mTasks)
+                && Objects.equals(mGroupedTasks, other.mGroupedTasks)
                 && Objects.equals(mSplitBounds, other.mSplitBounds)
                 && Arrays.equals(mMinimizedTaskIds, other.mMinimizedTaskIds);
     }
 
     @Override
     public int hashCode() {
-        return Objects.hash(mType, mTasks, mSplitBounds, Arrays.hashCode(mMinimizedTaskIds));
+        return Objects.hash(mType, mTasks, mGroupedTasks, mSplitBounds,
+                Arrays.hashCode(mMinimizedTaskIds));
     }
 
     @Override
     public String toString() {
         StringBuilder taskString = new StringBuilder();
-        for (int i = 0; i < mTasks.size(); i++) {
-            if (i == 0) {
-                taskString.append("Task");
-            } else {
-                taskString.append(", Task");
+        if (mType == TYPE_MIXED) {
+            taskString.append("GroupedTasks=" + mGroupedTasks.stream()
+                    .map(GroupedTaskInfo::toString)
+                    .collect(Collectors.joining(",\n\t", "[\n\t", "\n]")));
+        } else {
+            taskString.append("Tasks=" + mTasks.stream()
+                    .map(taskInfo -> getTaskInfoDumpString(taskInfo))
+                    .collect(Collectors.joining(", ", "[", "]")));
+            if (mSplitBounds != null) {
+                taskString.append(", SplitBounds=").append(mSplitBounds);
             }
-            taskString.append(i + 1).append(": ").append(getTaskInfo(mTasks.get(i)));
+            taskString.append(", Type=" + typeToString(mType));
+            taskString.append(", Minimized Task IDs=" + Arrays.toString(mMinimizedTaskIds));
         }
-        if (mSplitBounds != null) {
-            taskString.append(", SplitBounds: ").append(mSplitBounds);
-        }
-        taskString.append(", Type=");
-        switch (mType) {
-            case TYPE_FULLSCREEN:
-                taskString.append("TYPE_FULLSCREEN");
-                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(TaskInfo taskInfo) {
+    private String getTaskInfoDumpString(TaskInfo taskInfo) {
         if (taskInfo == null) {
             return null;
         }
+        final boolean isExcluded = (taskInfo.baseIntent.getFlags()
+                & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) != 0;
         return "id=" + taskInfo.taskId
-                + " baseIntent=" +
-                        (taskInfo.baseIntent != null && taskInfo.baseIntent.getComponent() != null
-                                ? taskInfo.baseIntent.getComponent().flattenToString()
-                                : "null")
-                + " winMode=" + WindowConfiguration.windowingModeToString(
-                        taskInfo.getWindowingMode());
+                + " winMode=" + windowingModeToString(taskInfo.getWindowingMode())
+                + " visReq=" + taskInfo.isVisibleRequested
+                + " vis=" + taskInfo.isVisible
+                + " excluded=" + isExcluded
+                + " baseIntent="
+                + (taskInfo.baseIntent != null && taskInfo.baseIntent.getComponent() != null
+                        ? taskInfo.baseIntent.getComponent().flattenToShortString()
+                        : "null");
     }
 
     @Override
     public void writeToParcel(Parcel parcel, int flags) {
         // We don't use the parcel list methods because we want to only write the TaskInfo state
         // and not the subclasses (Recents/RunningTaskInfo) whose fields are all deprecated
-        parcel.writeInt(mTasks.size());
-        for (int i = 0; i < mTasks.size(); i++) {
+        final int tasksSize = mTasks != null ? mTasks.size() : 0;
+        parcel.writeInt(tasksSize);
+        for (int i = 0; i < tasksSize; i++) {
             mTasks.get(i).writeTaskToParcel(parcel, flags);
         }
+        parcel.writeTypedList(mGroupedTasks);
         parcel.writeTypedObject(mSplitBounds, flags);
         parcel.writeInt(mType);
         parcel.writeIntArray(mMinimizedTaskIds);
@@ -293,6 +371,16 @@
         return 0;
     }
 
+    private String typeToString(@GroupType int type) {
+        return switch (type) {
+            case TYPE_FULLSCREEN -> "FULLSCREEN";
+            case TYPE_SPLIT -> "SPLIT";
+            case TYPE_FREEFORM -> "FREEFORM";
+            case TYPE_MIXED -> "MIXED";
+            default -> "UNKNOWN";
+        };
+    }
+
     public static final Creator<GroupedTaskInfo> CREATOR = new Creator() {
         @Override
         public GroupedTaskInfo createFromParcel(Parcel in) {
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 755f472..2fed138 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
@@ -233,6 +233,16 @@
     }
 
     /**
+     * Returns whether the multiple desktops feature is enabled for this device (both backend and
+     * frontend implementations).
+     */
+    public static boolean enableMultipleDesktops(@NonNull Context context) {
+        return Flags.enableMultipleDesktopsBackend()
+                && Flags.enableMultipleDesktopsFrontend()
+                && canEnterDesktopMode(context);
+    }
+
+    /**
      * @return {@code true} if this device is requesting to show the app handle despite non
      * necessarily enabling desktop mode
      */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java
index 9f01316..b098620 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java
@@ -230,6 +230,11 @@
         return mDisplayAreasInfo.get(displayId);
     }
 
+    @Nullable
+    public SurfaceControl getDisplayAreaLeash(int displayId) {
+        return mLeashes.get(displayId);
+    }
+
     /**
      * Applies the {@link DisplayAreaInfo} to the {@link DisplayAreaContext} specified by
      * {@link DisplayAreaInfo#displayId}.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/SizeChangeAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/SizeChangeAnimation.java
new file mode 100644
index 0000000..5018fdb
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/SizeChangeAnimation.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.wm.shell.animation;
+
+import static com.android.wm.shell.transition.DefaultSurfaceAnimator.setupValueAnimator;
+
+import android.animation.Animator;
+import android.animation.ValueAnimator;
+import android.annotation.Nullable;
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.view.Choreographer;
+import android.view.SurfaceControl;
+import android.view.View;
+import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
+import android.view.animation.AnimationSet;
+import android.view.animation.ClipRectAnimation;
+import android.view.animation.ScaleAnimation;
+import android.view.animation.Transformation;
+import android.view.animation.TranslateAnimation;
+
+import java.util.function.Consumer;
+
+/**
+ * Animation implementation for size-changing window container animations. Ported from
+ * {@link com.android.server.wm.WindowChangeAnimationSpec}.
+ * <p>
+ * This animation behaves slightly differently depending on whether the window is growing
+ * or shrinking:
+ * <ul>
+ * <li>If growing, it will do a clip-reveal after quicker fade-out/scale of the smaller (old)
+ * snapshot.
+ * <li>If shrinking, it will do an opposite clip-reveal on the old snapshot followed by a quicker
+ * fade-out of the bigger (old) snapshot while simultaneously shrinking the new window into
+ * place.
+ * </ul>
+ */
+public class SizeChangeAnimation {
+    private final Rect mTmpRect = new Rect();
+    final Transformation mTmpTransform = new Transformation();
+    final Matrix mTmpMatrix = new Matrix();
+    final float[] mTmpFloats = new float[9];
+    final float[] mTmpVecs = new float[4];
+
+    private final Animation mAnimation;
+    private final Animation mSnapshotAnim;
+
+    private final ValueAnimator mAnimator = ValueAnimator.ofFloat(0f, 1f);
+
+    /**
+     * The maximum of stretching applied to any surface during interpolation (since the animation
+     * is a combination of stretching/cropping/fading).
+     */
+    private static final float SCALE_FACTOR = 0.7f;
+
+    /**
+     * Since this animation is made of several sub-animations, we want to pre-arrange the
+     * sub-animations on a "virtual timeline" and then drive the overall progress in lock-step.
+     *
+     * To do this, we have a single value-animator which animates progress from 0-1 with an
+     * arbitrary duration and interpolator. Then we convert the progress to a frame in our virtual
+     * timeline to get the interpolated transforms.
+     *
+     * The APIs for arranging the sub-animations use integral frame numbers, so we need to pick
+     * an integral "duration" for our virtual timeline. That's what this constant specifies. It
+     * is effectively an animation "resolution" since it divides-up the 0-1 interpolation-space.
+     */
+    private static final int ANIMATION_RESOLUTION = 1000;
+
+    public SizeChangeAnimation(Rect startBounds, Rect endBounds) {
+        mAnimation = buildContainerAnimation(startBounds, endBounds);
+        mSnapshotAnim = buildSnapshotAnimation(startBounds, endBounds);
+    }
+
+    /**
+     * Initialize a size-change animation for a container leash.
+     */
+    public void initialize(SurfaceControl leash, SurfaceControl snapshot,
+            SurfaceControl.Transaction startT) {
+        startT.reparent(snapshot, leash);
+        startT.setPosition(snapshot, 0, 0);
+        startT.show(snapshot);
+        startT.show(leash);
+        apply(startT, leash, snapshot, 0.f);
+    }
+
+    /**
+     * Initialize a size-change animation for a view containing the leash surface(s).
+     *
+     * Note that this **will** apply {@param startToApply}!
+     */
+    public void initialize(View view, SurfaceControl leash, SurfaceControl snapshot,
+            SurfaceControl.Transaction startToApply) {
+        startToApply.reparent(snapshot, leash);
+        startToApply.setPosition(snapshot, 0, 0);
+        startToApply.show(snapshot);
+        startToApply.show(leash);
+        apply(view, startToApply, leash, snapshot, 0.f);
+    }
+
+    private ValueAnimator buildAnimatorInner(ValueAnimator.AnimatorUpdateListener updater,
+            SurfaceControl leash, SurfaceControl snapshot, Consumer<Animator> onFinish,
+            SurfaceControl.Transaction transaction, @Nullable View view) {
+        return setupValueAnimator(mAnimator, updater, (anim) -> {
+            transaction.reparent(snapshot, null);
+            if (view != null) {
+                view.setClipBounds(null);
+                view.setAnimationMatrix(null);
+                transaction.setCrop(leash, null);
+            }
+            transaction.apply();
+            transaction.close();
+            onFinish.accept(anim);
+        });
+    }
+
+    /**
+     * Build an animator which works on a pair of surface controls (where the snapshot is assumed
+     * to be a child of the main leash).
+     *
+     * @param onFinish Called when animation finishes. This is called on the anim thread!
+     */
+    public ValueAnimator buildAnimator(SurfaceControl leash, SurfaceControl snapshot,
+            Consumer<Animator> onFinish) {
+        final SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
+        Choreographer choreographer = Choreographer.getInstance();
+        return buildAnimatorInner(animator -> {
+            // The finish callback in buildSurfaceAnimation will ensure that the animation ends
+            // with fraction 1.
+            final float progress = Math.clamp(animator.getAnimatedFraction(), 0.f, 1.f);
+            apply(transaction, leash, snapshot, progress);
+            transaction.setFrameTimelineVsync(choreographer.getVsyncId());
+            transaction.apply();
+        }, leash, snapshot, onFinish, transaction, null /* view */);
+    }
+
+    /**
+     * Build an animator which works on a view that contains a pair of surface controls (where
+     * the snapshot is assumed to be a child of the main leash).
+     *
+     * @param onFinish Called when animation finishes. This is called on the anim thread!
+     */
+    public ValueAnimator buildViewAnimator(View view, SurfaceControl leash,
+            SurfaceControl snapshot, Consumer<Animator> onFinish) {
+        final SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
+        return buildAnimatorInner(animator -> {
+            // The finish callback in buildSurfaceAnimation will ensure that the animation ends
+            // with fraction 1.
+            final float progress = Math.clamp(animator.getAnimatedFraction(), 0.f, 1.f);
+            apply(view, transaction, leash, snapshot, progress);
+        }, leash, snapshot, onFinish, transaction, view);
+    }
+
+    /** Animation for the whole container (snapshot is inside this container). */
+    private static AnimationSet buildContainerAnimation(Rect startBounds, Rect endBounds) {
+        final long duration = ANIMATION_RESOLUTION;
+        boolean growing = endBounds.width() - startBounds.width()
+                + endBounds.height() - startBounds.height() >= 0;
+        long scalePeriod = (long) (duration * SCALE_FACTOR);
+        float startScaleX = SCALE_FACTOR * ((float) startBounds.width()) / endBounds.width()
+                + (1.f - SCALE_FACTOR);
+        float startScaleY = SCALE_FACTOR * ((float) startBounds.height()) / endBounds.height()
+                + (1.f - SCALE_FACTOR);
+        final AnimationSet animSet = new AnimationSet(true);
+
+        final Animation scaleAnim = new ScaleAnimation(startScaleX, 1, startScaleY, 1);
+        scaleAnim.setDuration(scalePeriod);
+        if (!growing) {
+            scaleAnim.setStartOffset(duration - scalePeriod);
+        }
+        animSet.addAnimation(scaleAnim);
+        final Animation translateAnim = new TranslateAnimation(startBounds.left,
+                endBounds.left, startBounds.top, endBounds.top);
+        translateAnim.setDuration(duration);
+        animSet.addAnimation(translateAnim);
+        Rect startClip = new Rect(startBounds);
+        Rect endClip = new Rect(endBounds);
+        startClip.offsetTo(0, 0);
+        endClip.offsetTo(0, 0);
+        final Animation clipAnim = new ClipRectAnimation(startClip, endClip);
+        clipAnim.setDuration(duration);
+        animSet.addAnimation(clipAnim);
+        animSet.initialize(startBounds.width(), startBounds.height(),
+                endBounds.width(), endBounds.height());
+        return animSet;
+    }
+
+    /** The snapshot surface is assumed to be a child of the container surface. */
+    private static AnimationSet buildSnapshotAnimation(Rect startBounds, Rect endBounds) {
+        final long duration = ANIMATION_RESOLUTION;
+        boolean growing = endBounds.width() - startBounds.width()
+                + endBounds.height() - startBounds.height() >= 0;
+        long scalePeriod = (long) (duration * SCALE_FACTOR);
+        float endScaleX = 1.f / (SCALE_FACTOR * ((float) startBounds.width()) / endBounds.width()
+                + (1.f - SCALE_FACTOR));
+        float endScaleY = 1.f / (SCALE_FACTOR * ((float) startBounds.height()) / endBounds.height()
+                + (1.f - SCALE_FACTOR));
+
+        AnimationSet snapAnimSet = new AnimationSet(true);
+        // Animation for the "old-state" snapshot that is atop the task.
+        final Animation snapAlphaAnim = new AlphaAnimation(1.f, 0.f);
+        snapAlphaAnim.setDuration(scalePeriod);
+        if (!growing) {
+            snapAlphaAnim.setStartOffset(duration - scalePeriod);
+        }
+        snapAnimSet.addAnimation(snapAlphaAnim);
+        final Animation snapScaleAnim =
+                new ScaleAnimation(endScaleX, endScaleX, endScaleY, endScaleY);
+        snapScaleAnim.setDuration(duration);
+        snapAnimSet.addAnimation(snapScaleAnim);
+        snapAnimSet.initialize(startBounds.width(), startBounds.height(),
+                endBounds.width(), endBounds.height());
+        return snapAnimSet;
+    }
+
+    private void calcCurrentClipBounds(Rect outClip, Transformation fromTransform) {
+        // The following applies an inverse scale to the clip-rect so that it crops "after" the
+        // scale instead of before.
+        mTmpVecs[1] = mTmpVecs[2] = 0;
+        mTmpVecs[0] = mTmpVecs[3] = 1;
+        fromTransform.getMatrix().mapVectors(mTmpVecs);
+
+        mTmpVecs[0] = 1.f / mTmpVecs[0];
+        mTmpVecs[3] = 1.f / mTmpVecs[3];
+        final Rect clipRect = fromTransform.getClipRect();
+        outClip.left = (int) (clipRect.left * mTmpVecs[0] + 0.5f);
+        outClip.right = (int) (clipRect.right * mTmpVecs[0] + 0.5f);
+        outClip.top = (int) (clipRect.top * mTmpVecs[3] + 0.5f);
+        outClip.bottom = (int) (clipRect.bottom * mTmpVecs[3] + 0.5f);
+    }
+
+    private void apply(SurfaceControl.Transaction t, SurfaceControl leash, SurfaceControl snapshot,
+            float progress) {
+        long currentPlayTime = (long) (((float) ANIMATION_RESOLUTION) * progress);
+        // update thumbnail surface
+        mSnapshotAnim.getTransformation(currentPlayTime, mTmpTransform);
+        t.setMatrix(snapshot, mTmpTransform.getMatrix(), mTmpFloats);
+        t.setAlpha(snapshot, mTmpTransform.getAlpha());
+
+        // update container surface
+        mAnimation.getTransformation(currentPlayTime, mTmpTransform);
+        final Matrix matrix = mTmpTransform.getMatrix();
+        t.setMatrix(leash, matrix, mTmpFloats);
+
+        calcCurrentClipBounds(mTmpRect, mTmpTransform);
+        t.setCrop(leash, mTmpRect);
+    }
+
+    private void apply(View view, SurfaceControl.Transaction tmpT, SurfaceControl leash,
+            SurfaceControl snapshot, float progress) {
+        long currentPlayTime = (long) (((float) ANIMATION_RESOLUTION) * progress);
+        // update thumbnail surface
+        mSnapshotAnim.getTransformation(currentPlayTime, mTmpTransform);
+        tmpT.setMatrix(snapshot, mTmpTransform.getMatrix(), mTmpFloats);
+        tmpT.setAlpha(snapshot, mTmpTransform.getAlpha());
+
+        // update container surface
+        mAnimation.getTransformation(currentPlayTime, mTmpTransform);
+        final Matrix matrix = mTmpTransform.getMatrix();
+        mTmpMatrix.set(matrix);
+        // animationMatrix is applied after getTranslation, so "move" the translate to the end.
+        mTmpMatrix.preTranslate(-view.getTranslationX(), -view.getTranslationY());
+        mTmpMatrix.postTranslate(view.getTranslationX(), view.getTranslationY());
+        view.setAnimationMatrix(mTmpMatrix);
+
+        calcCurrentClipBounds(mTmpRect, mTmpTransform);
+        tmpT.setCrop(leash, mTmpRect);
+        view.setClipBounds(mTmpRect);
+
+        // this takes stuff out of mTmpT so mTmpT can be re-used immediately
+        view.getViewRootImpl().applyTransactionOnDraw(tmpT);
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AppToWebUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AppToWebUtils.kt
index 06a55d3..08079d9 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AppToWebUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AppToWebUtils.kt
@@ -19,7 +19,6 @@
 package com.android.wm.shell.apptoweb
 
 import android.app.assist.AssistContent
-import android.app.assist.AssistContent.EXTRA_SESSION_TRANSFER_WEB_URI
 import android.content.Context
 import android.content.Intent
 import android.content.Intent.ACTION_VIEW
@@ -113,5 +112,5 @@
  * Returns the web uri from the given [AssistContent].
  */
 fun AssistContent.getSessionWebUri(): Uri? {
-    return extras.getParcelable(EXTRA_SESSION_TRANSFER_WEB_URI) ?: webUri
+    return sessionTransferUri ?: webUri
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/OpenByDefaultDialog.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/OpenByDefaultDialog.kt
index 4cc81a9..ec3637a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/OpenByDefaultDialog.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/OpenByDefaultDialog.kt
@@ -18,9 +18,11 @@
 
 import android.app.ActivityManager.RunningTaskInfo
 import android.content.Context
+import android.content.pm.PackageManager.NameNotFoundException
 import android.content.pm.verify.domain.DomainVerificationManager
 import android.graphics.Bitmap
 import android.graphics.PixelFormat
+import android.util.Slog
 import android.view.LayoutInflater
 import android.view.SurfaceControl
 import android.view.SurfaceControlViewHost
@@ -160,8 +162,15 @@
     }
 
     private fun setDefaultLinkHandlingSetting() {
-        domainVerificationManager.setDomainVerificationLinkHandlingAllowed(
-            packageName, openInAppButton.isChecked)
+        try {
+            domainVerificationManager.setDomainVerificationLinkHandlingAllowed(
+                packageName, openInAppButton.isChecked)
+        } catch (e: NameNotFoundException) {
+            Slog.e(
+                TAG,
+                "Failed to change link handling policy due to the package name is not found: " + e
+            )
+        }
     }
 
     private fun closeMenu() {
@@ -203,4 +212,8 @@
         /** Called when open by default dialog view has been released. */
         fun onDialogDismissed()
     }
+
+    companion object {
+        private const val TAG = "OpenByDefaultDialog"
+    }
 }
diff --git a/media/java/android/media/quality/ParamCapability.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/automotive/AutoShellModule.java
similarity index 65%
copy from media/java/android/media/quality/ParamCapability.aidl
copy to libs/WindowManager/Shell/src/com/android/wm/shell/automotive/AutoShellModule.java
index b43409d..fc51c75 100644
--- a/media/java/android/media/quality/ParamCapability.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/automotive/AutoShellModule.java
@@ -14,6 +14,17 @@
  * limitations under the License.
  */
 
-package android.media.quality;
+package com.android.wm.shell.automotive;
 
-parcelable ParamCapability;
+import com.android.wm.shell.dagger.WMSingleton;
+
+import dagger.Binds;
+import dagger.Module;
+
+
+@Module
+public abstract class AutoShellModule {
+    @WMSingleton
+    @Binds
+    abstract AutoTaskStackController provideTaskStackController(AutoTaskStackControllerImpl impl);
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/automotive/AutoTaskStack.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/automotive/AutoTaskStack.kt
new file mode 100644
index 0000000..caacdd3
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/automotive/AutoTaskStack.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.automotive
+
+import android.app.ActivityManager
+import android.graphics.Rect
+import android.view.SurfaceControl
+
+/**
+ * Represents an auto task stack, which is always in multi-window mode.
+ *
+ * @property id The ID of the task stack.
+ * @property displayId The ID of the display the task stack is on.
+ * @property leash The surface control leash of the task stack.
+ */
+interface AutoTaskStack {
+    val id: Int
+    val displayId: Int
+    var leash: SurfaceControl
+}
+
+/**
+ * Data class representing the state of an auto task stack.
+ *
+ * @property bounds The bounds of the task stack.
+ * @property childrenTasksVisible Whether the child tasks of the stack are visible.
+ * @property layer The layer of the task stack.
+ */
+data class AutoTaskStackState(
+    val bounds: Rect = Rect(),
+    val childrenTasksVisible: Boolean,
+    val layer: Int
+)
+
+/**
+ * Data class representing a root task stack.
+ *
+ * @property id The ID of the root task stack
+ * @property displayId The ID of the display the root task stack is on.
+ * @property leash The surface control leash of the root task stack.
+ * @property rootTaskInfo The running task info of the root task.
+ */
+data class RootTaskStack(
+    override val id: Int,
+    override val displayId: Int,
+    override var leash: SurfaceControl,
+    var rootTaskInfo: ActivityManager.RunningTaskInfo
+) : AutoTaskStack
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/automotive/AutoTaskStackController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/automotive/AutoTaskStackController.kt
new file mode 100644
index 0000000..15fedac
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/automotive/AutoTaskStackController.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.automotive
+
+import android.app.PendingIntent
+import android.content.Intent
+import android.os.Bundle
+import android.os.IBinder
+import android.view.SurfaceControl
+import android.window.TransitionInfo
+import android.window.TransitionRequestInfo
+import com.android.wm.shell.shared.annotations.ShellMainThread
+import com.android.wm.shell.transition.Transitions
+import com.android.wm.shell.transition.Transitions.TransitionFinishCallback
+
+/**
+ * Delegate interface for handling auto task stack transitions.
+ */
+interface AutoTaskStackTransitionHandlerDelegate {
+    /**
+     * Handles a transition request.
+     *
+     * @param transition The transition identifier.
+     * @param request The transition request information.
+     * @return An [AutoTaskStackTransaction] to be applied for the transition, or null if the
+     *         animation is not handled by this delegate.
+     */
+    fun handleRequest(
+        transition: IBinder, request: TransitionRequestInfo
+    ): AutoTaskStackTransaction?
+
+    /**
+     * See [Transitions.TransitionHandler.startAnimation] for more details.
+     *
+     * @param changedTaskStacks Contains the states of the task stacks that were changed as a
+     * result of this transition. The key is the [AutoTaskStack.id] and the value is the
+     * corresponding [AutoTaskStackState].
+     */
+    fun startAnimation(
+        transition: IBinder,
+        changedTaskStacks: Map<Int, AutoTaskStackState>,
+        info: TransitionInfo,
+        startTransaction: SurfaceControl.Transaction,
+        finishTransaction: SurfaceControl.Transaction,
+        finishCallback: TransitionFinishCallback
+    ): Boolean
+
+    /**
+     * See [Transitions.TransitionHandler.onTransitionConsumed] for more details.
+     *
+     * @param requestedTaskStacks contains the states of the task stacks that were requested in
+     * the transition. The key is the [AutoTaskStack.id] and the value is the corresponding
+     * [AutoTaskStackState].
+     */
+    fun onTransitionConsumed(
+        transition: IBinder,
+        requestedTaskStacks: Map<Int, AutoTaskStackState>,
+        aborted: Boolean, finishTransaction: SurfaceControl.Transaction?
+    )
+
+    /**
+     * See [Transitions.TransitionHandler.mergeAnimation] for more details.
+     *
+     * @param changedTaskStacks Contains the states of the task stacks that were changed as a
+     * result of this transition. The key is the [AutoTaskStack.id] and the value is the
+     * corresponding [AutoTaskStackState].
+     */
+    fun mergeAnimation(
+        transition: IBinder,
+        changedTaskStacks: Map<Int, AutoTaskStackState>,
+        info: TransitionInfo,
+        surfaceTransaction: SurfaceControl.Transaction,
+        mergeTarget: IBinder,
+        finishCallback: TransitionFinishCallback
+    )
+}
+
+
+/**
+ * Controller for managing auto task stacks.
+ */
+interface AutoTaskStackController {
+
+    var autoTransitionHandlerDelegate: AutoTaskStackTransitionHandlerDelegate?
+        set
+
+    /**
+     * Map of task stack IDs to their states.
+     *
+     * This gets updated right before [AutoTaskStackTransitionHandlerDelegate.startAnimation] or
+     * [AutoTaskStackTransitionHandlerDelegate.onTransitionConsumed] is called.
+     */
+    val taskStackStateMap: Map<Int, AutoTaskStackState>
+        get
+
+    /**
+     * Creates a new multi-window root task.
+     *
+     * A root task stack is placed in the default TDA of the specified display by default.
+     * Once the root task is removed, the [AutoTaskStackController] no longer holds a reference to
+     * it.
+     *
+     * @param displayId The ID of the display to create the root task stack on.
+     * @param listener The listener for root task stack events.
+     */
+    @ShellMainThread
+    fun createRootTaskStack(displayId: Int, listener: RootTaskStackListener)
+
+
+    /**
+     * Sets the default root task stack (launch root) on a display. Calling it again with a
+     * different [rootTaskStackId] will simply replace the default root task stack on the display.
+     *
+     * Note: This is helpful for passively routing tasks to a specified container. If a display
+     * doesn't have a default root task stack set, all tasks will open in fullscreen and cover
+     * the entire default TDA by default.
+     *
+     * @param displayId The ID of the display.
+     * @param rootTaskStackId The ID of the root task stack, or null to clear the default.
+     */
+    @ShellMainThread
+    fun setDefaultRootTaskStackOnDisplay(displayId: Int, rootTaskStackId: Int?)
+
+    /**
+     * Starts a transaction with the specified [transaction].
+     * Returns the transition identifier.
+     */
+    @ShellMainThread
+    fun startTransition(transaction: AutoTaskStackTransaction): IBinder?
+}
+
+internal sealed class TaskStackOperation {
+    data class ReparentTask(
+        val taskId: Int,
+        val parentTaskStackId: Int,
+        val onTop: Boolean
+    ) : TaskStackOperation()
+
+    data class SendPendingIntent(
+        val sender: PendingIntent,
+        val intent: Intent,
+        val options: Bundle?
+    ) : TaskStackOperation()
+
+    data class SetTaskStackState(
+        val taskStackId: Int,
+        val state: AutoTaskStackState
+    ) : TaskStackOperation()
+}
+
+data class AutoTaskStackTransaction internal constructor(
+    internal val operations: MutableList<TaskStackOperation> = mutableListOf()
+) {
+    constructor() : this(
+        mutableListOf()
+    )
+
+    /** See [WindowContainerTransaction.reparent] for more details. */
+    fun reparentTask(
+        taskId: Int,
+        parentTaskStackId: Int,
+        onTop: Boolean
+    ): AutoTaskStackTransaction {
+        operations.add(TaskStackOperation.ReparentTask(taskId, parentTaskStackId, onTop))
+        return this
+    }
+
+    /** See [WindowContainerTransaction.sendPendingIntent] for more details. */
+    fun sendPendingIntent(
+        sender: PendingIntent,
+        intent: Intent,
+        options: Bundle?
+    ): AutoTaskStackTransaction {
+        operations.add(TaskStackOperation.SendPendingIntent(sender, intent, options))
+        return this
+    }
+
+    /**
+     * Adds a set task stack state operation to the transaction.
+     *
+     * If an operation with the same task stack ID already exists, it is replaced with the new one.
+     *
+     * @param taskStackId The ID of the task stack.
+     * @param state The new state of the task stack.
+     * @return The transaction with the added operation.
+     */
+    fun setTaskStackState(taskStackId: Int, state: AutoTaskStackState): AutoTaskStackTransaction {
+        val existingOperation = operations.find {
+            it is TaskStackOperation.SetTaskStackState && it.taskStackId == taskStackId
+        }
+        if (existingOperation != null) {
+            val index = operations.indexOf(existingOperation)
+            operations[index] = TaskStackOperation.SetTaskStackState(taskStackId, state)
+        } else {
+            operations.add(TaskStackOperation.SetTaskStackState(taskStackId, state))
+        }
+        return this
+    }
+
+    /**
+     * Returns a map of task stack IDs to their states from the set task stack state operations.
+     *
+     * @return The map of task stack IDs to states.
+     */
+    fun getTaskStackStates(): Map<Int, AutoTaskStackState> {
+        val states = mutableMapOf<Int, AutoTaskStackState>()
+        operations.forEach { operation ->
+            if (operation is TaskStackOperation.SetTaskStackState) {
+                states[operation.taskStackId] = operation.state
+            }
+        }
+        return states
+    }
+}
+
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/automotive/AutoTaskStackControllerImpl.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/automotive/AutoTaskStackControllerImpl.kt
new file mode 100644
index 0000000..f8f2842
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/automotive/AutoTaskStackControllerImpl.kt
@@ -0,0 +1,534 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.automotive
+
+import android.app.ActivityManager
+import android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT
+import android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS
+import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
+import android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED
+import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW
+import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
+import android.os.IBinder
+import android.util.Log
+import android.util.Slog
+import android.view.SurfaceControl
+import android.view.SurfaceControl.Transaction
+import android.view.WindowManager
+import android.view.WindowManager.TRANSIT_CHANGE
+import android.window.TransitionInfo
+import android.window.TransitionRequestInfo
+import android.window.WindowContainerTransaction
+import com.android.systemui.car.Flags.autoTaskStackWindowing
+import com.android.wm.shell.RootTaskDisplayAreaOrganizer
+import com.android.wm.shell.ShellTaskOrganizer
+import com.android.wm.shell.common.ShellExecutor
+import com.android.wm.shell.dagger.WMSingleton
+import com.android.wm.shell.shared.TransitionUtil
+import com.android.wm.shell.shared.annotations.ShellMainThread
+import com.android.wm.shell.sysui.ShellInit
+import com.android.wm.shell.transition.Transitions
+import com.android.wm.shell.transition.Transitions.TransitionFinishCallback
+import javax.inject.Inject
+
+const val TAG = "AutoTaskStackController"
+
+@WMSingleton
+class AutoTaskStackControllerImpl @Inject constructor(
+    val taskOrganizer: ShellTaskOrganizer,
+    @ShellMainThread private val shellMainThread: ShellExecutor,
+    val transitions: Transitions,
+    val shellInit: ShellInit,
+    val rootTdaOrganizer: RootTaskDisplayAreaOrganizer
+) : AutoTaskStackController, Transitions.TransitionHandler {
+    override var autoTransitionHandlerDelegate: AutoTaskStackTransitionHandlerDelegate? = null
+    override val taskStackStateMap = mutableMapOf<Int, AutoTaskStackState>()
+
+    private val DBG = Log.isLoggable(TAG, Log.DEBUG)
+    private val taskStackMap = mutableMapOf<Int, AutoTaskStack>()
+    private val pendingTransitions = ArrayList<PendingTransition>()
+    private val mTaskStackStateTranslator = TaskStackStateTranslator()
+    private val appTasksMap = mutableMapOf<Int, ActivityManager.RunningTaskInfo>()
+    private val defaultRootTaskPerDisplay = mutableMapOf<Int, Int>()
+
+    init {
+        if (!autoTaskStackWindowing()) {
+            throw IllegalStateException("Failed to initialize" +
+                    "AutoTaskStackController as the auto_task_stack_windowing TS flag is disabled.")
+        } else {
+            shellInit.addInitCallback(this::onInit, this);
+        }
+    }
+
+    fun onInit() {
+        transitions.addHandler(this)
+    }
+
+    /** Translates the [AutoTaskStackState] to relevant WM and surface transactions. */
+    inner class TaskStackStateTranslator {
+        // TODO(b/384946072): Move to an interface with 2 implementations, one for root task and
+        //  other for TDA
+        fun applyVisibilityAndBounds(
+            wct: WindowContainerTransaction,
+            taskStack: AutoTaskStack,
+            state: AutoTaskStackState
+        ) {
+            if (taskStack !is RootTaskStack) {
+                Slog.e(TAG, "Unsupported task stack, unable to convertToWct")
+                return
+            }
+            wct.setBounds(taskStack.rootTaskInfo.token, state.bounds)
+            wct.reorder(taskStack.rootTaskInfo.token, /* onTop= */ state.childrenTasksVisible)
+        }
+
+        fun reorderLeash(
+            taskStack: AutoTaskStack,
+            state: AutoTaskStackState,
+            transaction: Transaction
+        ) {
+            if (taskStack !is RootTaskStack) {
+                Slog.e(TAG, "Unsupported task stack, unable to reorder leash")
+                return
+            }
+            Slog.d(TAG, "Setting the layer ${state.layer}")
+            transaction.setLayer(taskStack.leash, state.layer)
+        }
+
+        fun restoreLeash(taskStack: AutoTaskStack, transaction: Transaction) {
+            if (taskStack !is RootTaskStack) {
+                Slog.e(TAG, "Unsupported task stack, unable to restore leash")
+                return
+            }
+
+            val rootTdaInfo = rootTdaOrganizer.getDisplayAreaInfo(taskStack.displayId)
+            if (rootTdaInfo == null ||
+                rootTdaInfo.featureId != taskStack.rootTaskInfo.displayAreaFeatureId
+            ) {
+                Slog.e(TAG, "Cannot find the rootTDA for the root task stack ${taskStack.id}")
+                return
+            }
+            if (DBG) {
+                Slog.d(TAG, "Reparenting ${taskStack.id} leash to DA ${rootTdaInfo.featureId}")
+            }
+            transaction.reparent(
+                taskStack.leash,
+                rootTdaOrganizer.getDisplayAreaLeash(taskStack.displayId)
+            )
+        }
+    }
+
+    inner class RootTaskStackListenerAdapter(
+        val rootTaskStackListener: RootTaskStackListener,
+    ) : ShellTaskOrganizer.TaskListener {
+        private var rootTaskStack: RootTaskStack? = null
+
+        // TODO(b/384948029): Notify car service for all the children tasks' events
+        override fun onTaskAppeared(
+            taskInfo: ActivityManager.RunningTaskInfo?,
+            leash: SurfaceControl?
+        ) {
+            if (taskInfo == null) {
+                throw IllegalArgumentException("taskInfo can't be null in onTaskAppeared")
+            }
+            if (leash == null) {
+                throw IllegalArgumentException("leash can't be null in onTaskAppeared")
+            }
+            if (DBG) Slog.d(TAG, "onTaskAppeared = ${taskInfo.taskId}")
+
+            if (rootTaskStack == null) {
+                val rootTask =
+                    RootTaskStack(taskInfo.taskId, taskInfo.displayId, leash, taskInfo)
+                taskStackMap[rootTask.id] = rootTask
+
+                rootTaskStack = rootTask;
+                rootTaskStackListener.onRootTaskStackCreated(rootTask);
+                return
+            }
+            appTasksMap[taskInfo.taskId] = taskInfo
+            rootTaskStackListener.onTaskAppeared(taskInfo, leash)
+        }
+
+        override fun onTaskInfoChanged(taskInfo: ActivityManager.RunningTaskInfo?) {
+            if (taskInfo == null) {
+                throw IllegalArgumentException("taskInfo can't be null in onTaskInfoChanged")
+            }
+            if (DBG) Slog.d(TAG, "onTaskInfoChanged = ${taskInfo.taskId}")
+            var previousRootTaskStackInfo = rootTaskStack ?: run {
+                Slog.e(TAG, "Received onTaskInfoChanged, when root task stack is null")
+                return@onTaskInfoChanged
+            }
+            rootTaskStack?.let {
+                if (taskInfo.taskId == previousRootTaskStackInfo.id) {
+                    previousRootTaskStackInfo = previousRootTaskStackInfo.copy(rootTaskInfo = taskInfo)
+                    taskStackMap[previousRootTaskStackInfo.id] = previousRootTaskStackInfo
+                    rootTaskStack = previousRootTaskStackInfo;
+                    rootTaskStackListener.onRootTaskStackInfoChanged(it)
+                    return
+                }
+            }
+
+            appTasksMap[taskInfo.taskId] = taskInfo
+            rootTaskStackListener.onTaskInfoChanged(taskInfo)
+        }
+
+        override fun onTaskVanished(taskInfo: ActivityManager.RunningTaskInfo?) {
+            if (taskInfo == null) {
+                throw IllegalArgumentException("taskInfo can't be null in onTaskVanished")
+            }
+            if (DBG) Slog.d(TAG, "onTaskVanished  = ${taskInfo.taskId}")
+            var rootTask = rootTaskStack ?: run {
+                Slog.e(TAG, "Received onTaskVanished, when root task stack is null")
+                return@onTaskVanished
+            }
+            if (taskInfo.taskId == rootTask.id) {
+                rootTask = rootTask.copy(rootTaskInfo = taskInfo)
+                rootTaskStack = rootTask
+                rootTaskStackListener.onRootTaskStackDestroyed(rootTask)
+                taskStackMap.remove(rootTask.id)
+                taskStackStateMap.remove(rootTask.id)
+                rootTaskStack = null
+                return
+            }
+            appTasksMap.remove(taskInfo.taskId)
+            rootTaskStackListener.onTaskVanished(taskInfo)
+        }
+
+        override fun onBackPressedOnTaskRoot(taskInfo: ActivityManager.RunningTaskInfo?) {
+            if (taskInfo == null) {
+                throw IllegalArgumentException("taskInfo can't be null in onBackPressedOnTaskRoot")
+            }
+            super.onBackPressedOnTaskRoot(taskInfo)
+            rootTaskStackListener.onBackPressedOnTaskRoot(taskInfo)
+        }
+    }
+
+    override fun createRootTaskStack(
+        displayId: Int,
+        listener: RootTaskStackListener
+    ) {
+        if (!autoTaskStackWindowing()) {
+            Slog.e(
+                TAG, "Failed to create root task stack as the " +
+                        "auto_task_stack_windowing TS flag is disabled."
+            )
+            return
+        }
+        taskOrganizer.createRootTask(
+            displayId,
+            WINDOWING_MODE_MULTI_WINDOW,
+            RootTaskStackListenerAdapter(listener),
+            /* removeWithTaskOrganizer= */ true
+        )
+    }
+
+    override fun setDefaultRootTaskStackOnDisplay(displayId: Int, rootTaskStackId: Int?) {
+        if (!autoTaskStackWindowing()) {
+            Slog.e(
+                TAG, "Failed to set default root task stack as the " +
+                        "auto_task_stack_windowing TS flag is disabled."
+            )
+            return
+        }
+        var wct = WindowContainerTransaction()
+
+        // Clear the default root task stack if already set
+        defaultRootTaskPerDisplay[displayId]?.let { existingDefaultRootTaskStackId ->
+            (taskStackMap[existingDefaultRootTaskStackId] as? RootTaskStack)?.let { rootTaskStack ->
+                wct.setLaunchRoot(rootTaskStack.rootTaskInfo.token, null, null)
+            }
+        }
+
+        if (rootTaskStackId != null) {
+            var taskStack =
+                taskStackMap[rootTaskStackId] ?: run { return@setDefaultRootTaskStackOnDisplay }
+            if (DBG) Slog.d(TAG, "setting launch root for  = ${taskStack.id}")
+            if (taskStack !is RootTaskStack) {
+                throw IllegalArgumentException(
+                    "Cannot set a non root task stack as default root task " +
+                            "stack"
+                )
+            }
+            wct.setLaunchRoot(
+                taskStack.rootTaskInfo.token,
+                intArrayOf(WINDOWING_MODE_UNDEFINED),
+                intArrayOf(
+                    ACTIVITY_TYPE_STANDARD, ACTIVITY_TYPE_UNDEFINED, ACTIVITY_TYPE_RECENTS,
+
+                    // TODO(b/386242708): Figure out if this flag will ever be used for automotive
+                    //  assistant. Based on output, remove it from here and fix the
+                    //  AssistantStackTests accordingly.
+                    ACTIVITY_TYPE_ASSISTANT
+                )
+            )
+        }
+
+        taskOrganizer.applyTransaction(wct)
+    }
+
+    override fun startTransition(transaction: AutoTaskStackTransaction): IBinder? {
+        if (!autoTaskStackWindowing()) {
+            Slog.e(
+                TAG, "Failed to start transaction as the " +
+                        "auto_task_stack_windowing TS flag is disabled."
+            )
+            return null
+        }
+        if (transaction.operations.isEmpty()) {
+            Slog.e(TAG, "Operations empty, no transaction started")
+            return null
+        }
+        if (DBG) Slog.d(TAG, "startTransaction ${transaction.operations}")
+
+        var wct = WindowContainerTransaction()
+        convertToWct(transaction, wct)
+        var pending = PendingTransition(
+            TRANSIT_CHANGE,
+            wct,
+            transaction,
+        )
+        return startTransitionNow(pending)
+    }
+
+    override fun handleRequest(
+        transition: IBinder,
+        request: TransitionRequestInfo
+    ): WindowContainerTransaction? {
+        if (DBG) {
+            Slog.d(TAG, "handle request, id=${request.debugId}, type=${request.type}, " +
+                    "triggertask = ${request.triggerTask ?: "null"}")
+        }
+        val ast = autoTransitionHandlerDelegate?.handleRequest(transition, request)
+            ?: run { return@handleRequest null }
+
+        if (ast.operations.isEmpty()) {
+            return null
+        }
+        var wct = WindowContainerTransaction()
+        convertToWct(ast, wct)
+
+        pendingTransitions.add(
+            PendingTransition(request.type, wct, ast).apply { isClaimed = transition }
+        )
+        return wct
+    }
+
+    fun updateTaskStackStates(taskStatStates: Map<Int, AutoTaskStackState>) {
+        taskStackStateMap.putAll(taskStatStates)
+    }
+
+    override fun startAnimation(
+        transition: IBinder,
+        info: TransitionInfo,
+        startTransaction: Transaction,
+        finishTransaction: Transaction,
+        finishCallback: TransitionFinishCallback
+    ): Boolean {
+        if (DBG) Slog.d(TAG, "  startAnimation, id=${info.debugId} = changes=" + info.changes)
+        val pending: PendingTransition? = findPending(transition)
+        if (pending != null) {
+            pendingTransitions.remove(pending)
+            updateTaskStackStates(pending.transaction.getTaskStackStates())
+        }
+
+        reorderLeashes(startTransaction)
+        reorderLeashes(finishTransaction)
+
+        for (chg in info.changes) {
+            // TODO(b/384946072): handle the da stack similarly. The below implementation only
+            // handles the root task stack
+
+            val taskInfo = chg.taskInfo ?: continue
+            val taskStack = taskStackMap[taskInfo.taskId] ?: continue
+
+            // Restore the leashes for the task stacks to ensure correct z-order competition
+            if (taskStackMap.containsKey(taskInfo.taskId)) {
+                mTaskStackStateTranslator.restoreLeash(
+                    taskStack,
+                    startTransaction
+                )
+                if (TransitionUtil.isOpeningMode(chg.mode)) {
+                    // Clients can still manipulate the alpha, but this ensures that the default
+                    // behavior is natural
+                    startTransaction.setAlpha(chg.leash, 1f)
+                }
+                continue
+            }
+        }
+
+        val isPlayedByDelegate = autoTransitionHandlerDelegate?.startAnimation(
+            transition,
+            pending?.transaction?.getTaskStackStates() ?: mapOf(),
+            info,
+            startTransaction,
+            finishTransaction,
+            {
+                shellMainThread.execute {
+                    finishCallback.onTransitionFinished(it)
+                    startNextTransition()
+                }
+            }
+        ) ?: false
+
+        if (isPlayedByDelegate) {
+            if (DBG) Slog.d(TAG, "${info.debugId} played");
+            return true;
+        }
+
+        // If for an animation which is not played by the delegate, contains a change in a known
+        // task stack, it should be leveraged to correct the leashes. So, handle the animation in
+        // this case.
+        if (info.changes.any { taskStackMap.containsKey(it.taskInfo?.taskId) }) {
+            startTransaction.apply()
+            finishCallback.onTransitionFinished(null)
+            startNextTransition()
+            if (DBG) Slog.d(TAG, "${info.debugId} played");
+            return true
+        }
+        return false;
+    }
+
+    fun convertToWct(ast: AutoTaskStackTransaction, wct: WindowContainerTransaction) {
+        ast.operations.forEach { operation ->
+            when (operation) {
+                is TaskStackOperation.ReparentTask -> {
+                    val appTask = appTasksMap[operation.taskId]
+
+                    if (appTask == null) {
+                        Slog.e(
+                            TAG, "task with id=$operation.taskId not found, failed to " +
+                                    "reparent."
+                        )
+                        return@forEach
+                    }
+                    if (!taskStackMap.containsKey(operation.parentTaskStackId)) {
+                        Slog.e(
+                            TAG, "task stack with id=${operation.parentTaskStackId} not " +
+                                    "found, failed to reparent"
+                        )
+                        return@forEach
+                    }
+                    // TODO(b/384946072): Handle a display area stack as well
+                    wct.reparent(
+                        appTask.token,
+                        (taskStackMap[operation.parentTaskStackId] as RootTaskStack)
+                            .rootTaskInfo.token,
+                        operation.onTop
+                    )
+                }
+
+                is TaskStackOperation.SendPendingIntent -> wct.sendPendingIntent(
+                    operation.sender,
+                    operation.intent,
+                    operation.options
+                )
+
+                is TaskStackOperation.SetTaskStackState -> {
+                    taskStackMap[operation.taskStackId]?.let { taskStack ->
+                        mTaskStackStateTranslator.applyVisibilityAndBounds(
+                            wct,
+                            taskStack,
+                            operation.state
+                        )
+                    }
+                        ?: Slog.w(TAG, "AutoTaskStack with id ${operation.taskStackId} " +
+                                "not found.")
+                }
+            }
+        }
+    }
+
+    override fun mergeAnimation(
+        transition: IBinder,
+        info: TransitionInfo,
+        surfaceTransaction: Transaction,
+        mergeTarget: IBinder,
+        finishCallback: TransitionFinishCallback
+    ) {
+        val pending: PendingTransition? = findPending(transition)
+
+        autoTransitionHandlerDelegate?.mergeAnimation(
+            transition,
+            pending?.transaction?.getTaskStackStates() ?: mapOf(),
+            info,
+            surfaceTransaction,
+            mergeTarget,
+            /* finishCallback = */ {
+                shellMainThread.execute {
+                    finishCallback.onTransitionFinished(it)
+                }
+            }
+        )
+    }
+
+    override fun onTransitionConsumed(
+        transition: IBinder,
+        aborted: Boolean,
+        finishTransaction: Transaction?
+    ) {
+        val pending: PendingTransition? = findPending(transition)
+        if (pending != null) {
+            pendingTransitions.remove(pending)
+            updateTaskStackStates(pending.transaction.getTaskStackStates())
+            // Still update the surface order because this means wm didn't lead to any change
+            if (finishTransaction != null) {
+                reorderLeashes(finishTransaction)
+            }
+        }
+        autoTransitionHandlerDelegate?.onTransitionConsumed(
+            transition,
+            pending?.transaction?.getTaskStackStates() ?: mapOf(),
+            aborted,
+            finishTransaction
+        )
+    }
+
+    private fun reorderLeashes(transaction: SurfaceControl.Transaction) {
+        taskStackStateMap.forEach { (taskId, taskStackState) ->
+            taskStackMap[taskId]?.let { taskStack ->
+                mTaskStackStateTranslator.reorderLeash(taskStack, taskStackState, transaction)
+            } ?: Slog.w(TAG, "Warning: AutoTaskStack with id $taskId not found.")
+        }
+    }
+
+    private fun findPending(claimed: IBinder) = pendingTransitions.find { it.isClaimed == claimed }
+
+    private fun startTransitionNow(pending: PendingTransition): IBinder {
+        val claimedTransition = transitions.startTransition(pending.mType, pending.wct, this)
+        pending.isClaimed = claimedTransition
+        pendingTransitions.add(pending)
+        return claimedTransition
+    }
+
+    fun startNextTransition() {
+        if (pendingTransitions.isEmpty()) return
+        val pending: PendingTransition = pendingTransitions[0]
+        if (pending.isClaimed != null) {
+            // Wait for this to start animating.
+            return
+        }
+        pending.isClaimed = transitions.startTransition(pending.mType, pending.wct, this)
+    }
+
+    internal class PendingTransition(
+        @field:WindowManager.TransitionType @param:WindowManager.TransitionType val mType: Int,
+        val wct: WindowContainerTransaction,
+        val transaction: AutoTaskStackTransaction,
+    ) {
+        var isClaimed: IBinder? = null
+    }
+
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/automotive/RootTaskStackListener.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/automotive/RootTaskStackListener.kt
new file mode 100644
index 0000000..9d121b1
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/automotive/RootTaskStackListener.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.automotive
+
+import com.android.wm.shell.ShellTaskOrganizer
+
+/**
+ * A [TaskListener] which simplifies the interface when used for
+ * [ShellTaskOrganizer.createRootTask].
+ *
+ * [onRootTaskStackCreated], [onRootTaskStackInfoChanged], [onRootTaskStackDestroyed] will be called
+ * for the underlying root task.
+ * The [onTaskAppeared], [onTaskInfoChanged], [onTaskVanished] are called for the children tasks.
+ */
+interface RootTaskStackListener : ShellTaskOrganizer.TaskListener {
+    fun onRootTaskStackCreated(rootTaskStack: RootTaskStack)
+    fun onRootTaskStackInfoChanged(rootTaskStack: RootTaskStack)
+    fun onRootTaskStackDestroyed(rootTaskStack: RootTaskStack)
+}
\ No newline at end of file
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 ddc107e..8dabd54 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
@@ -28,8 +28,6 @@
 import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER;
 
 import static com.android.internal.jank.InteractionJankMonitor.CUJ_PREDICTIVE_BACK_HOME;
-import static com.android.window.flags.Flags.migratePredictiveBackTransition;
-import static com.android.window.flags.Flags.predictiveBackSystemAnims;
 import static com.android.window.flags.Flags.unifyBackNavigationTransition;
 import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW;
 
@@ -41,23 +39,17 @@
 import android.app.IActivityTaskManager;
 import android.app.TaskInfo;
 import android.content.ComponentName;
-import android.content.ContentResolver;
 import android.content.Context;
 import android.content.res.Configuration;
-import android.database.ContentObserver;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.hardware.input.InputManager;
-import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.RemoteCallback;
 import android.os.RemoteException;
 import android.os.SystemClock;
-import android.os.SystemProperties;
-import android.os.UserHandle;
-import android.provider.Settings.Global;
 import android.util.Log;
 import android.view.IRemoteAnimationRunner;
 import android.view.InputDevice;
@@ -93,7 +85,6 @@
 import com.android.wm.shell.common.RemoteCallable;
 import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.shared.TransitionUtil;
-import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
 import com.android.wm.shell.shared.annotations.ShellMainThread;
 import com.android.wm.shell.sysui.ConfigurationChangeListener;
 import com.android.wm.shell.sysui.ShellCommandHandler;
@@ -103,7 +94,6 @@
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.Predicate;
 
 /**
@@ -112,14 +102,7 @@
 public class BackAnimationController implements RemoteCallable<BackAnimationController>,
         ConfigurationChangeListener {
     private static final String TAG = "ShellBackPreview";
-    private static final int SETTING_VALUE_OFF = 0;
-    private static final int SETTING_VALUE_ON = 1;
-    public static final boolean IS_ENABLED =
-            SystemProperties.getInt("persist.wm.debug.predictive_back",
-                    SETTING_VALUE_ON) == SETTING_VALUE_ON;
 
-    /** Predictive back animation developer option */
-    private final AtomicBoolean mEnableAnimations = new AtomicBoolean(false);
     /**
      * Max duration to wait for an animation to finish before triggering the real back.
      */
@@ -149,11 +132,9 @@
     private boolean mReceivedNullNavigationInfo = false;
     private final IActivityTaskManager mActivityTaskManager;
     private final Context mContext;
-    private final ContentResolver mContentResolver;
     private final ShellController mShellController;
     private final ShellCommandHandler mShellCommandHandler;
     private final ShellExecutor mShellExecutor;
-    private final Handler mBgHandler;
     private final WindowManager mWindowManager;
     private final Transitions mTransitions;
     @VisibleForTesting
@@ -215,9 +196,7 @@
                         ProtoLog.i(WM_SHELL_BACK_PREVIEW, "Navigation window gone.");
                         setTriggerBack(false);
                         // Trigger close transition if necessary.
-                        if (Flags.migratePredictiveBackTransition()) {
-                            mBackTransitionHandler.onAnimationFinished();
-                        }
+                        mBackTransitionHandler.onAnimationFinished();
                         resetTouchTracker();
                         // Don't wait for animation start
                         mShellExecutor.removeCallbacks(mAnimationTimeoutRunnable);
@@ -248,7 +227,6 @@
             @NonNull ShellInit shellInit,
             @NonNull ShellController shellController,
             @NonNull @ShellMainThread ShellExecutor shellExecutor,
-            @NonNull @ShellBackgroundThread Handler backgroundHandler,
             Context context,
             @NonNull BackAnimationBackground backAnimationBackground,
             ShellBackAnimationRegistry shellBackAnimationRegistry,
@@ -259,10 +237,8 @@
                 shellInit,
                 shellController,
                 shellExecutor,
-                backgroundHandler,
                 ActivityTaskManager.getService(),
                 context,
-                context.getContentResolver(),
                 backAnimationBackground,
                 shellBackAnimationRegistry,
                 shellCommandHandler,
@@ -275,10 +251,8 @@
             @NonNull ShellInit shellInit,
             @NonNull ShellController shellController,
             @NonNull @ShellMainThread ShellExecutor shellExecutor,
-            @NonNull @ShellBackgroundThread Handler bgHandler,
             @NonNull IActivityTaskManager activityTaskManager,
             Context context,
-            ContentResolver contentResolver,
             @NonNull BackAnimationBackground backAnimationBackground,
             ShellBackAnimationRegistry shellBackAnimationRegistry,
             ShellCommandHandler shellCommandHandler,
@@ -288,10 +262,8 @@
         mShellExecutor = shellExecutor;
         mActivityTaskManager = activityTaskManager;
         mContext = context;
-        mContentResolver = contentResolver;
         mRequirePointerPilfer =
                 context.getResources().getBoolean(R.bool.config_backAnimationRequiresPointerPilfer);
-        mBgHandler = bgHandler;
         shellInit.addInitCallback(this::onInit, this);
         mAnimationBackground = backAnimationBackground;
         mShellBackAnimationRegistry = shellBackAnimationRegistry;
@@ -308,8 +280,6 @@
     }
 
     private void onInit() {
-        setupAnimationDeveloperSettingsObserver(mContentResolver, mBgHandler);
-        updateEnableAnimationFromFlags();
         createAdapter();
         mShellController.addExternalInterface(IBackAnimation.DESCRIPTOR,
                 this::createExternalInterface, this);
@@ -317,42 +287,6 @@
         mShellController.addConfigurationChangeListener(this);
     }
 
-    private void setupAnimationDeveloperSettingsObserver(
-            @NonNull ContentResolver contentResolver,
-            @NonNull @ShellBackgroundThread final Handler backgroundHandler) {
-        if (predictiveBackSystemAnims()) {
-            ProtoLog.d(WM_SHELL_BACK_PREVIEW, "Back animation aconfig flag is enabled, therefore "
-                    + "developer settings flag is ignored and no content observer registered");
-            return;
-        }
-        ContentObserver settingsObserver = new ContentObserver(backgroundHandler) {
-            @Override
-            public void onChange(boolean selfChange, Uri uri) {
-                updateEnableAnimationFromFlags();
-            }
-        };
-        contentResolver.registerContentObserver(
-                Global.getUriFor(Global.ENABLE_BACK_ANIMATION),
-                false, settingsObserver, UserHandle.USER_SYSTEM
-        );
-    }
-
-    /**
-     * Updates {@link BackAnimationController#mEnableAnimations} based on the current values of the
-     * aconfig flag and the developer settings flag
-     */
-    @ShellBackgroundThread
-    private void updateEnableAnimationFromFlags() {
-        boolean isEnabled = predictiveBackSystemAnims() || isDeveloperSettingEnabled();
-        mEnableAnimations.set(isEnabled);
-        ProtoLog.d(WM_SHELL_BACK_PREVIEW, "Back animation enabled=%s", isEnabled);
-    }
-
-    private boolean isDeveloperSettingEnabled() {
-        return Global.getInt(mContext.getContentResolver(),
-                Global.ENABLE_BACK_ANIMATION, SETTING_VALUE_OFF) == SETTING_VALUE_ON;
-    }
-
     public BackAnimation getBackAnimationImpl() {
         return mBackAnimation;
     }
@@ -620,14 +554,13 @@
     private void startBackNavigation(@NonNull BackTouchTracker touchTracker) {
         try {
             startLatencyTracking();
-            final BackAnimationAdapter adapter = mEnableAnimations.get()
-                    ? mBackAnimationAdapter : null;
-            if (adapter != null && mShellBackAnimationRegistry.hasSupportedAnimatorsChanged()) {
-                adapter.updateSupportedAnimators(
+            if (mBackAnimationAdapter != null
+                    && mShellBackAnimationRegistry.hasSupportedAnimatorsChanged()) {
+                mBackAnimationAdapter.updateSupportedAnimators(
                         mShellBackAnimationRegistry.getSupportedAnimators());
             }
             mBackNavigationInfo = mActivityTaskManager.startBackNavigation(
-                    mNavigationObserver, adapter);
+                    mNavigationObserver, mBackAnimationAdapter);
             onBackNavigationInfoReceived(mBackNavigationInfo, touchTracker);
         } catch (RemoteException remoteException) {
             Log.e(TAG, "Failed to initAnimation", remoteException);
@@ -699,9 +632,7 @@
     }
 
     private boolean shouldDispatchToAnimator() {
-        return mEnableAnimations.get()
-                && mBackNavigationInfo != null
-                && mBackNavigationInfo.isPrepareRemoteAnimation();
+        return mBackNavigationInfo != null && mBackNavigationInfo.isPrepareRemoteAnimation();
     }
 
     private void tryPilferPointers() {
@@ -918,23 +849,16 @@
         mShellExecutor.executeDelayed(mAnimationTimeoutRunnable, MAX_ANIMATION_DURATION);
 
         // The next callback should be {@link #onBackAnimationFinished}.
-        final boolean migrateBackToTransition = migratePredictiveBackTransition();
         if (mCurrentTracker.getTriggerBack()) {
-            if (migrateBackToTransition) {
-                // notify core gesture is commit
-                if (shouldTriggerCloseTransition()) {
-                    mBackTransitionHandler.mCloseTransitionRequested = true;
-                    final IOnBackInvokedCallback callback =
-                            mBackNavigationInfo.getOnBackInvokedCallback();
-                    // invoked client side onBackInvoked
-                    dispatchOnBackInvoked(callback);
-                    mRealCallbackInvoked = true;
-                }
-            } else {
-                // notify gesture finished
-                mBackNavigationInfo.onBackGestureFinished(true);
+            // notify core gesture is commit
+            if (shouldTriggerCloseTransition()) {
+                mBackTransitionHandler.mCloseTransitionRequested = true;
+                final IOnBackInvokedCallback callback =
+                        mBackNavigationInfo.getOnBackInvokedCallback();
+                // invoked client side onBackInvoked
+                dispatchOnBackInvoked(callback);
+                mRealCallbackInvoked = true;
             }
-
             // start post animation
             dispatchOnBackInvoked(mActiveCallback);
         } else {
@@ -1103,7 +1027,6 @@
                 () -> mShellExecutor.execute(this::onBackAnimationFinished));
 
         if (mApps.length >= 1) {
-            mCurrentTracker.updateStartLocation();
             BackMotionEvent startEvent = mCurrentTracker.createStartEvent(mApps[0]);
             dispatchOnBackStarted(mActiveCallback, startEvent);
             if (startEvent.getSwipeEdge() == EDGE_NONE) {
@@ -1204,7 +1127,6 @@
      */
     private void dump(PrintWriter pw, String prefix) {
         pw.println(prefix + "BackAnimationController state:");
-        pw.println(prefix + "  mEnableAnimations=" + mEnableAnimations.get());
         pw.println(prefix + "  mBackGestureStarted=" + mBackGestureStarted);
         pw.println(prefix + "  mPostCommitAnimationInProgress=" + mPostCommitAnimationInProgress);
         pw.println(prefix + "  mShouldStartOnNextMoveEvent=" + mShouldStartOnNextMoveEvent);
@@ -1469,8 +1391,7 @@
                         }
                         moveToTop = change.hasFlags(FLAG_MOVED_TO_TOP);
                         info.getChanges().remove(j);
-                    } else if ((openShowWallpaper && change.hasFlags(FLAG_IS_WALLPAPER))
-                            || !change.hasFlags(FLAG_BACK_GESTURE_ANIMATED)) {
+                    } else if ((openShowWallpaper && change.hasFlags(FLAG_IS_WALLPAPER))) {
                         info.getChanges().remove(j);
                     } else if (!mergePredictive && TransitionUtil.isClosingMode(change.getMode())) {
                         mergePredictive = true;
@@ -1493,6 +1414,13 @@
                             if (moveToTop) {
                                 change.setFlags(change.getFlags() | FLAG_MOVED_TO_TOP);
                             }
+                        } else if (Flags.unifyBackNavigationTransition()
+                                && change.hasFlags(FLAG_BACK_GESTURE_ANIMATED)
+                                && change.getMode() == TRANSIT_CHANGE
+                                && isCloseChangeExist(info, change)) {
+                            // This is the original top target, don't add it into current transition
+                            // if it is closing.
+                            continue;
                         }
                         info.getChanges().add(i, change);
                     }
@@ -1824,6 +1752,17 @@
         return findComponentName(change) != null || findTaskId(change) != INVALID_TASK_ID;
     }
 
+    private static boolean isCloseChangeExist(TransitionInfo info, TransitionInfo.Change change) {
+        for (int j = info.getChanges().size() - 1; j >= 0; --j) {
+            final TransitionInfo.Change current = info.getChanges().get(j);
+            if (TransitionUtil.isClosingMode(current.getMode())
+                    && change.getLeash().isSameSurface(current.getLeash())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     // Record the latest back gesture happen on which task.
     static class BackTransitionObserver implements Transitions.TransitionObserver {
         int mFocusedTaskId = INVALID_TASK_ID;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationRunner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationRunner.java
index 4569cf3..b9fccc1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationRunner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationRunner.java
@@ -106,11 +106,12 @@
 
     private Runnable mFinishedCallback;
     private RemoteAnimationTarget[] mApps;
-    private IRemoteAnimationFinishedCallback mRemoteCallback;
+    private RemoteAnimationFinishedStub mRemoteCallback;
 
     private static class RemoteAnimationFinishedStub extends IRemoteAnimationFinishedCallback.Stub {
         //the binder callback should not hold strong reference to it to avoid memory leak.
-        private WeakReference<BackAnimationRunner> mRunnerRef;
+        private final WeakReference<BackAnimationRunner> mRunnerRef;
+        private boolean mAbandoned;
 
         private RemoteAnimationFinishedStub(BackAnimationRunner runner) {
             mRunnerRef = new WeakReference<>(runner);
@@ -118,23 +119,29 @@
 
         @Override
         public void onAnimationFinished() {
-            BackAnimationRunner runner = mRunnerRef.get();
+            synchronized (this) {
+                if (mAbandoned) {
+                    return;
+                }
+            }
+            final BackAnimationRunner runner = mRunnerRef.get();
             if (runner == null) {
                 return;
             }
-            if (runner.shouldMonitorCUJ(runner.mApps)) {
-                InteractionJankMonitor.getInstance().end(runner.mCujType);
-            }
+            runner.onAnimationFinish(this);
+        }
 
-            runner.mFinishedCallback.run();
-            for (int i = runner.mApps.length - 1; i >= 0; --i) {
-                 SurfaceControl sc = runner.mApps[i].leash;
-                 if (sc != null && sc.isValid()) {
-                     sc.release();
-                 }
+        void abandon() {
+            synchronized (this) {
+                mAbandoned = true;
+                final BackAnimationRunner runner = mRunnerRef.get();
+                if (runner == null) {
+                    return;
+                }
+                if (runner.shouldMonitorCUJ(runner.mApps)) {
+                    InteractionJankMonitor.getInstance().end(runner.mCujType);
+                }
             }
-            runner.mApps = null;
-            runner.mFinishedCallback = null;
         }
     }
 
@@ -144,13 +151,16 @@
      */
     void startAnimation(RemoteAnimationTarget[] apps, RemoteAnimationTarget[] wallpapers,
             RemoteAnimationTarget[] nonApps, Runnable finishedCallback) {
-        InteractionJankMonitor interactionJankMonitor = InteractionJankMonitor.getInstance();
+        if (mRemoteCallback != null) {
+            mRemoteCallback.abandon();
+            mRemoteCallback = null;
+        }
+        mRemoteCallback = new RemoteAnimationFinishedStub(this);
         mFinishedCallback = finishedCallback;
         mApps = apps;
-        if (mRemoteCallback == null) mRemoteCallback = new RemoteAnimationFinishedStub(this);
         mWaitingAnimation = false;
         if (shouldMonitorCUJ(apps)) {
-            interactionJankMonitor.begin(
+            InteractionJankMonitor.getInstance().begin(
                     apps[0].leash, mContext, mHandler, mCujType);
         }
         try {
@@ -161,6 +171,28 @@
         }
     }
 
+    void onAnimationFinish(RemoteAnimationFinishedStub finished) {
+        mHandler.post(() -> {
+            if (mRemoteCallback != null && finished != mRemoteCallback) {
+                return;
+            }
+            if (shouldMonitorCUJ(mApps)) {
+                InteractionJankMonitor.getInstance().end(mCujType);
+            }
+
+            mFinishedCallback.run();
+            for (int i = mApps.length - 1; i >= 0; --i) {
+                final SurfaceControl sc = mApps[i].leash;
+                if (sc != null && sc.isValid()) {
+                    sc.release();
+                }
+            }
+            mApps = null;
+            mFinishedCallback = null;
+            mRemoteCallback = null;
+        });
+    }
+
     @VisibleForTesting
     boolean shouldMonitorCUJ(RemoteAnimationTarget[] apps) {
         return apps.length > 0 && mCujType != NO_CUJ;
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 8efeecb..08e3692 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
@@ -215,6 +215,8 @@
     private final BubblePositioner mBubblePositioner;
     private Bubbles.SysuiProxy mSysuiProxy;
 
+    @Nullable private Runnable mOnImeHidden;
+
     // Tracks the id of the current (foreground) user.
     private int mCurrentUserId;
     // Current profiles of the user (e.g. user with a workprofile)
@@ -615,7 +617,8 @@
     /**
      * Hides the current input method, wherever it may be focused, via InputMethodManagerInternal.
      */
-    void hideCurrentInputMethod() {
+    void hideCurrentInputMethod(@Nullable Runnable onImeHidden) {
+        mOnImeHidden = onImeHidden;
         mBubblePositioner.setImeVisible(false /* visible */, 0 /* height */);
         int displayId = mWindowManager.getDefaultDisplay().getDisplayId();
         try {
@@ -2267,7 +2270,7 @@
         if (mLayerView != null && mLayerView.isExpanded()) {
             if (mBubblePositioner.isImeVisible()) {
                 // If we're collapsing, hide the IME
-                hideCurrentInputMethod();
+                hideCurrentInputMethod(null);
             }
             mLayerView.collapse();
         }
@@ -2552,6 +2555,10 @@
             mBubblePositioner.setImeVisible(imeVisible, totalImeHeight);
             if (mStackView != null) {
                 mStackView.setImeVisible(imeVisible);
+                if (!imeVisible && mOnImeHidden != null) {
+                    mOnImeHidden.run();
+                    mOnImeHidden = null;
+                }
             }
         }
 
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 47032fd..13f8e9e 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
@@ -222,15 +222,9 @@
                     mTaskView.getBoundsOnScreen(launchBounds);
 
                     options.setTaskAlwaysOnTop(true);
-                    options.setLaunchedFromBubble(true);
                     options.setPendingIntentBackgroundActivityStartMode(
                             MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS);
 
-                    Intent fillInIntent = new Intent();
-                    // Apply flags to make behaviour match documentLaunchMode=always.
-                    fillInIntent.addFlags(FLAG_ACTIVITY_NEW_DOCUMENT);
-                    fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
-
                     final boolean isShortcutBubble = (mBubble.hasMetadataShortcutId()
                             || (mBubble.getShortcutInfo() != null && Flags.enableBubbleAnything()));
 
@@ -242,7 +236,6 @@
                                 context,
                                 /* requestCode= */ 0,
                                 mBubble.getAppBubbleIntent()
-                                        .addFlags(FLAG_ACTIVITY_NEW_DOCUMENT)
                                         .addFlags(FLAG_ACTIVITY_MULTIPLE_TASK),
                                 PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT,
                                 /* options= */ null);
@@ -250,13 +243,19 @@
                                 launchBounds);
                     } else if (!mIsOverflow && isShortcutBubble) {
                         ProtoLog.v(WM_SHELL_BUBBLES, "startingShortcutBubble=%s", getBubbleKey());
+                        options.setLaunchedFromBubble(true);
                         options.setApplyActivityFlagsForBubbles(true);
                         mTaskView.startShortcutActivity(mBubble.getShortcutInfo(),
                                 options, launchBounds);
                     } else {
+                        options.setLaunchedFromBubble(true);
                         if (mBubble != null) {
                             mBubble.setIntentActive();
                         }
+                        final Intent fillInIntent = new Intent();
+                        // Apply flags to make behaviour match documentLaunchMode=always.
+                        fillInIntent.addFlags(FLAG_ACTIVITY_NEW_DOCUMENT);
+                        fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
                         mTaskView.startActivity(mPendingIntent, fillInIntent, options,
                                 launchBounds);
                     }
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 6423eed..a026231 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
@@ -82,7 +82,7 @@
                 override fun isShowingAsBubbleBar(): Boolean = controller.isShowingAsBubbleBar
 
                 override fun hideCurrentInputMethod() {
-                    controller.hideCurrentInputMethod()
+                    controller.hideCurrentInputMethod(null)
                 }
 
                 override fun updateBubbleBarLocation(
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 249a218..979d958 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
@@ -280,6 +280,7 @@
     private int mCornerRadius;
     @Nullable private BubbleViewProvider mExpandedBubble;
     private boolean mIsExpanded;
+    private boolean mIsImeVisible = false;
 
     /** Whether the stack is currently on the left side of the screen, or animating there. */
     private boolean mStackOnLeftOrWillBe = true;
@@ -2233,29 +2234,40 @@
 
         boolean wasExpanded = mIsExpanded;
 
-        hideCurrentInputMethod();
+        // Do the actual expansion/collapse after the IME is hidden if it's currently visible in
+        // order to avoid flickers
+        Runnable onImeHidden = () -> {
+            mSysuiProxyProvider.getSysuiProxy().onStackExpandChanged(shouldExpand);
 
-        mSysuiProxyProvider.getSysuiProxy().onStackExpandChanged(shouldExpand);
+            if (wasExpanded) {
+                stopMonitoringSwipeUpGesture();
+                animateCollapse();
+                showManageMenu(false);
+                logBubbleEvent(mExpandedBubble,
+                        FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED);
+            } else {
+                animateExpansion();
+                // TODO: move next line to BubbleData
+                logBubbleEvent(mExpandedBubble,
+                        FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__EXPANDED);
+                logBubbleEvent(mExpandedBubble,
+                        FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__STACK_EXPANDED);
+                mManager.checkNotificationPanelExpandedState(notifPanelExpanded -> {
+                    if (!notifPanelExpanded && mIsExpanded) {
+                        startMonitoringSwipeUpGesture();
+                    }
+                });
+            }
+            notifyExpansionChanged(mExpandedBubble, mIsExpanded);
+            announceExpandForAccessibility(mExpandedBubble, mIsExpanded);
+        };
 
-        if (wasExpanded) {
-            stopMonitoringSwipeUpGesture();
-            animateCollapse();
-            showManageMenu(false);
-            logBubbleEvent(mExpandedBubble, FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__COLLAPSED);
+        if (mPositioner.isImeVisible()) {
+            hideCurrentInputMethod(onImeHidden);
         } else {
-            animateExpansion();
-            // TODO: move next line to BubbleData
-            logBubbleEvent(mExpandedBubble, FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__EXPANDED);
-            logBubbleEvent(mExpandedBubble,
-                    FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__STACK_EXPANDED);
-            mManager.checkNotificationPanelExpandedState(notifPanelExpanded -> {
-                if (!notifPanelExpanded && mIsExpanded) {
-                    startMonitoringSwipeUpGesture();
-                }
-            });
+            // the IME is already hidden, so run the runnable immediately
+            onImeHidden.run();
         }
-        notifyExpansionChanged(mExpandedBubble, mIsExpanded);
-        announceExpandForAccessibility(mExpandedBubble, mIsExpanded);
     }
 
     /**
@@ -2373,7 +2385,17 @@
      * not.
      */
     void hideCurrentInputMethod() {
-        mManager.hideCurrentInputMethod();
+        mManager.hideCurrentInputMethod(null);
+    }
+
+    /**
+     * Hides the IME similar to {@link #hideCurrentInputMethod()} but also runs {@code onImeHidden}
+     * after after the IME is hidden.
+     *
+     * @see #hideCurrentInputMethod()
+     */
+    void hideCurrentInputMethod(Runnable onImeHidden) {
+        mManager.hideCurrentInputMethod(onImeHidden);
     }
 
     /** Set the stack position to whatever the positioner says. */
@@ -2865,6 +2887,10 @@
      * and clip the expanded view.
      */
     public void setImeVisible(boolean visible) {
+        if (mIsImeVisible == visible) {
+            return;
+        }
+        mIsImeVisible = visible;
         if ((mIsExpansionAnimating || mIsBubbleSwitchAnimating) && mIsExpanded) {
             // This will update the animation so the bubbles move to position for the IME
             mExpandedAnimationController.expandFromStack(() -> {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackViewManager.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackViewManager.kt
index fb597a0..8b901a1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackViewManager.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackViewManager.kt
@@ -34,7 +34,7 @@
     fun checkNotificationPanelExpandedState(callback: Consumer<Boolean>)
 
     /** Requests to hide the current input method. */
-    fun hideCurrentInputMethod()
+    fun hideCurrentInputMethod(onImeHidden: Runnable?)
 
     companion object {
 
@@ -52,8 +52,8 @@
                 controller.isNotificationPanelExpanded(callback)
             }
 
-            override fun hideCurrentInputMethod() {
-                controller.hideCurrentInputMethod()
+            override fun hideCurrentInputMethod(onImeHidden: Runnable?) {
+                controller.hideCurrentInputMethod(onImeHidden)
             }
         }
     }
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 0c0fd7b..89c038b 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
@@ -105,15 +105,8 @@
                         getBubbleKey());
                 try {
                     options.setTaskAlwaysOnTop(true);
-                    options.setLaunchedFromBubble(true);
                     options.setPendingIntentBackgroundActivityStartMode(
                             MODE_BACKGROUND_ACTIVITY_START_ALLOW_ALWAYS);
-
-                    Intent fillInIntent = new Intent();
-                    // Apply flags to make behaviour match documentLaunchMode=always.
-                    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()) {
@@ -124,20 +117,25 @@
                                 context,
                                 /* requestCode= */ 0,
                                 mBubble.getAppBubbleIntent()
-                                        .addFlags(FLAG_ACTIVITY_NEW_DOCUMENT)
                                         .addFlags(FLAG_ACTIVITY_MULTIPLE_TASK),
                                 PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT,
                                 /* options= */ null);
                         mTaskView.startActivity(pi, /* fillInIntent= */ null, options,
                                 launchBounds);
                     } else if (isShortcutBubble) {
+                        options.setLaunchedFromBubble(true);
                         options.setApplyActivityFlagsForBubbles(true);
                         mTaskView.startShortcutActivity(mBubble.getShortcutInfo(),
                                 options, launchBounds);
                     } else {
+                        options.setLaunchedFromBubble(true);
                         if (mBubble != null) {
                             mBubble.setIntentActive();
                         }
+                        final Intent fillInIntent = new Intent();
+                        // Apply flags to make behaviour match documentLaunchMode=always.
+                        fillInIntent.addFlags(FLAG_ACTIVITY_NEW_DOCUMENT);
+                        fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
                         mTaskView.startActivity(mPendingIntent, fillInIntent, options,
                                 launchBounds);
                     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/ResizingEffectPolicy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/ResizingEffectPolicy.java
new file mode 100644
index 0000000..3f76fd0
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/ResizingEffectPolicy.java
@@ -0,0 +1,324 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.view.WindowManager.DOCKED_BOTTOM;
+import static android.view.WindowManager.DOCKED_INVALID;
+import static android.view.WindowManager.DOCKED_LEFT;
+import static android.view.WindowManager.DOCKED_RIGHT;
+import static android.view.WindowManager.DOCKED_TOP;
+
+import static com.android.wm.shell.common.split.SplitLayout.PARALLAX_ALIGN_CENTER;
+import static com.android.wm.shell.common.split.SplitLayout.PARALLAX_DISMISSING;
+import static com.android.wm.shell.common.split.SplitLayout.PARALLAX_FLEX;
+import static com.android.wm.shell.common.split.SplitLayout.PARALLAX_NONE;
+import static com.android.wm.shell.shared.animation.Interpolators.DIM_INTERPOLATOR;
+import static com.android.wm.shell.shared.animation.Interpolators.SLOWDOWN_INTERPOLATOR;
+
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.view.SurfaceControl;
+import android.view.WindowManager;
+
+/**
+ * This class governs how and when parallax and dimming effects are applied to task surfaces,
+ * usually when the divider is being moved around by the user (or during an animation).
+ */
+class ResizingEffectPolicy {
+    private final SplitLayout mSplitLayout;
+    /** The parallax algorithm we are currently using. */
+    private final int mParallaxType;
+
+    int mShrinkSide = DOCKED_INVALID;
+
+    // The current dismissing side.
+    int mDismissingSide = DOCKED_INVALID;
+
+    /**
+     * A {@link Point} that stores a single x and y value, representing the parallax translation
+     * we use on the app that the divider is moving toward. The app is either shrinking in size or
+     * getting pushed off the screen.
+     */
+    final Point mRetreatingSideParallax = new Point();
+    /**
+     * A {@link Point} that stores a single x and y value, representing the parallax translation
+     * we use on the app that the divider is moving away from. The app is either growing in size or
+     * getting pulled onto the screen.
+     */
+    final Point mAdvancingSideParallax = new Point();
+
+    // The dimming value to hint the dismissing side and progress.
+    float mDismissingDimValue = 0.0f;
+
+    /**
+     * Content bounds for the app that the divider is moving toward. This is the content that is
+     * currently drawn at the start of the divider movement. It stays unchanged throughout the
+     * divider's movement.
+     */
+    final Rect mRetreatingContent = new Rect();
+    /**
+     * Surface bounds for the app that the divider is moving toward. This is the "canvas" on
+     * which an app could potentially be drawn. It changes on every frame as the divider moves
+     * around.
+     */
+    final Rect mRetreatingSurface = new Rect();
+    /**
+     * Content bounds for the app that the divider is moving away from. This is the content that
+     * is currently drawn at the start of the divider movement. It stays unchanged throughout
+     * the divider's movement.
+     */
+    final Rect mAdvancingContent = new Rect();
+    /**
+     * Surface bounds for the app that the divider is moving away from. This is the "canvas" on
+     * which an app could potentially be drawn. It changes on every frame as the divider moves
+     * around.
+     */
+    final Rect mAdvancingSurface = new Rect();
+
+    final Rect mTempRect = new Rect();
+    final Rect mTempRect2 = new Rect();
+
+    ResizingEffectPolicy(int parallaxType, SplitLayout splitLayout) {
+        mParallaxType = parallaxType;
+        mSplitLayout = splitLayout;
+    }
+
+    /**
+     * Calculates the desired parallax values and stores them in {@link #mRetreatingSideParallax}
+     * and {@link #mAdvancingSideParallax}. These values will be then be applied in
+     * {@link #adjustRootSurface}.
+     *
+     * @param position    The divider's position on the screen (x-coordinate in left-right split,
+     *                    y-coordinate in top-bottom split).
+     */
+    void applyDividerPosition(
+            int position, boolean isLeftRightSplit, DividerSnapAlgorithm snapAlgorithm) {
+        mDismissingSide = DOCKED_INVALID;
+        mRetreatingSideParallax.set(0, 0);
+        mAdvancingSideParallax.set(0, 0);
+        mDismissingDimValue = 0;
+        Rect displayBounds = mSplitLayout.getRootBounds();
+
+        int totalDismissingDistance = 0;
+        if (position < snapAlgorithm.getFirstSplitTarget().position) {
+            mDismissingSide = isLeftRightSplit ? DOCKED_LEFT : DOCKED_TOP;
+            totalDismissingDistance = snapAlgorithm.getDismissStartTarget().position
+                    - snapAlgorithm.getFirstSplitTarget().position;
+        } else if (position > snapAlgorithm.getLastSplitTarget().position) {
+            mDismissingSide = isLeftRightSplit ? DOCKED_RIGHT : DOCKED_BOTTOM;
+            totalDismissingDistance = snapAlgorithm.getLastSplitTarget().position
+                    - snapAlgorithm.getDismissEndTarget().position;
+        }
+
+        final boolean topLeftShrink = isLeftRightSplit
+                ? position < mSplitLayout.getTopLeftContentBounds().right
+                : position < mSplitLayout.getTopLeftContentBounds().bottom;
+        if (topLeftShrink) {
+            mShrinkSide = isLeftRightSplit ? DOCKED_LEFT : DOCKED_TOP;
+            mRetreatingContent.set(mSplitLayout.getTopLeftContentBounds());
+            mRetreatingSurface.set(mSplitLayout.getTopLeftBounds());
+            mAdvancingContent.set(mSplitLayout.getBottomRightContentBounds());
+            mAdvancingSurface.set(mSplitLayout.getBottomRightBounds());
+        } else {
+            mShrinkSide = isLeftRightSplit ? DOCKED_RIGHT : DOCKED_BOTTOM;
+            mRetreatingContent.set(mSplitLayout.getBottomRightContentBounds());
+            mRetreatingSurface.set(mSplitLayout.getBottomRightBounds());
+            mAdvancingContent.set(mSplitLayout.getTopLeftContentBounds());
+            mAdvancingSurface.set(mSplitLayout.getTopLeftBounds());
+        }
+
+        if (mDismissingSide != DOCKED_INVALID) {
+            float fraction =
+                    Math.max(0, Math.min(snapAlgorithm.calculateDismissingFraction(position), 1f));
+            mDismissingDimValue = DIM_INTERPOLATOR.getInterpolation(fraction);
+            if (mParallaxType == PARALLAX_DISMISSING) {
+                fraction = calculateParallaxDismissingFraction(fraction, mDismissingSide);
+                if (isLeftRightSplit) {
+                    mRetreatingSideParallax.x = (int) (fraction * totalDismissingDistance);
+                } else {
+                    mRetreatingSideParallax.y = (int) (fraction * totalDismissingDistance);
+                }
+            }
+        }
+
+        if (mParallaxType == PARALLAX_ALIGN_CENTER) {
+            if (isLeftRightSplit) {
+                mRetreatingSideParallax.x =
+                        (mRetreatingSurface.width() - mRetreatingContent.width()) / 2;
+            } else {
+                mRetreatingSideParallax.y =
+                        (mRetreatingSurface.height() - mRetreatingContent.height()) / 2;
+            }
+        } else if (mParallaxType == PARALLAX_FLEX) {
+            // Whether an app is getting pushed offscreen by the divider.
+            boolean isRetreatingOffscreen = !displayBounds.contains(mRetreatingSurface);
+            // Whether an app was getting pulled onscreen at the beginning of the drag.
+            boolean advancingSideStartedOffscreen = !displayBounds.contains(mAdvancingContent);
+
+            // The simpler case when an app gets pushed offscreen (e.g. 50:50 -> 90:10)
+            if (isRetreatingOffscreen && !advancingSideStartedOffscreen) {
+                // On the left side, we use parallax to simulate the contents sticking to the
+                // divider. This is because surfaces naturally expand to the bottom and right,
+                // so when a surface's area expands, the contents stick to the left. This is
+                // correct behavior on the right-side surface, but not the left.
+                if (topLeftShrink) {
+                    if (isLeftRightSplit) {
+                        mRetreatingSideParallax.x =
+                                mRetreatingSurface.width() - mRetreatingContent.width();
+                    } else {
+                        mRetreatingSideParallax.y =
+                                mRetreatingSurface.height() - mRetreatingContent.height();
+                    }
+                }
+                // All other cases (e.g. 10:90 -> 50:50, 10:90 -> 90:10, 10:90 -> dismiss)
+            } else {
+                mTempRect.set(mRetreatingSurface);
+                Point rootOffset = new Point();
+                // 10:90 -> 50:50, 10:90, or dismiss right
+                if (advancingSideStartedOffscreen) {
+                    // We have to handle a complicated case here to keep the parallax smooth.
+                    // When the divider crosses the 50% mark, the retreating-side app surface
+                    // will start expanding offscreen. This is expected and unavoidable, but
+                    // makes the parallax look disjointed. In order to preserve the illusion,
+                    // we add another offset (rootOffset) to simulate the surface staying
+                    // onscreen.
+                    mTempRect.intersect(displayBounds);
+                    if (mRetreatingSurface.left < displayBounds.left) {
+                        rootOffset.x = displayBounds.left - mRetreatingSurface.left;
+                    }
+                    if (mRetreatingSurface.top < displayBounds.top) {
+                        rootOffset.y = displayBounds.top - mRetreatingSurface.top;
+                    }
+
+                    // On the left side, we again have to simulate the contents sticking to the
+                    // divider.
+                    if (!topLeftShrink) {
+                        if (isLeftRightSplit) {
+                            mAdvancingSideParallax.x =
+                                    mAdvancingSurface.width() - mAdvancingContent.width();
+                        } else {
+                            mAdvancingSideParallax.y =
+                                    mAdvancingSurface.height() - mAdvancingContent.height();
+                        }
+                    }
+                }
+
+                // In all these cases, the shrinking app also receives a center parallax.
+                if (isLeftRightSplit) {
+                    mRetreatingSideParallax.x = rootOffset.x
+                            + ((mTempRect.width() - mRetreatingContent.width()) / 2);
+                } else {
+                    mRetreatingSideParallax.y = rootOffset.y
+                            + ((mTempRect.height() - mRetreatingContent.height()) / 2);
+                }
+            }
+        }
+    }
+
+    /**
+     * @return for a specified {@code fraction}, this returns an adjusted value that simulates a
+     * slowing down parallax effect
+     */
+    private float calculateParallaxDismissingFraction(float fraction, int dockSide) {
+        float result = SLOWDOWN_INTERPOLATOR.getInterpolation(fraction) / 3.5f;
+
+        // Less parallax at the top, just because.
+        if (dockSide == WindowManager.DOCKED_TOP) {
+            result /= 2f;
+        }
+        return result;
+    }
+
+    /** Applies the calculated parallax and dimming values to task surfaces. */
+    void adjustRootSurface(SurfaceControl.Transaction t,
+            SurfaceControl leash1, SurfaceControl leash2) {
+        SurfaceControl retreatingLeash = null;
+        SurfaceControl advancingLeash = null;
+
+        if (mParallaxType == PARALLAX_DISMISSING) {
+            switch (mDismissingSide) {
+                case DOCKED_TOP:
+                case DOCKED_LEFT:
+                    retreatingLeash = leash1;
+                    mTempRect.set(mSplitLayout.getTopLeftBounds());
+                    advancingLeash = leash2;
+                    mTempRect2.set(mSplitLayout.getBottomRightBounds());
+                    break;
+                case DOCKED_BOTTOM:
+                case DOCKED_RIGHT:
+                    retreatingLeash = leash2;
+                    mTempRect.set(mSplitLayout.getBottomRightBounds());
+                    advancingLeash = leash1;
+                    mTempRect2.set(mSplitLayout.getTopLeftBounds());
+                    break;
+            }
+        } else if (mParallaxType == PARALLAX_ALIGN_CENTER || mParallaxType == PARALLAX_FLEX) {
+            switch (mShrinkSide) {
+                case DOCKED_TOP:
+                case DOCKED_LEFT:
+                    retreatingLeash = leash1;
+                    mTempRect.set(mSplitLayout.getTopLeftBounds());
+                    advancingLeash = leash2;
+                    mTempRect2.set(mSplitLayout.getBottomRightBounds());
+                    break;
+                case DOCKED_BOTTOM:
+                case DOCKED_RIGHT:
+                    retreatingLeash = leash2;
+                    mTempRect.set(mSplitLayout.getBottomRightBounds());
+                    advancingLeash = leash1;
+                    mTempRect2.set(mSplitLayout.getTopLeftBounds());
+                    break;
+            }
+        }
+        if (mParallaxType != PARALLAX_NONE
+                && retreatingLeash != null && advancingLeash != null) {
+            t.setPosition(retreatingLeash, mTempRect.left + mRetreatingSideParallax.x,
+                    mTempRect.top + mRetreatingSideParallax.y);
+            // Transform the screen-based split bounds to surface-based crop bounds.
+            mTempRect.offsetTo(-mRetreatingSideParallax.x, -mRetreatingSideParallax.y);
+            t.setWindowCrop(retreatingLeash, mTempRect);
+
+            t.setPosition(advancingLeash, mTempRect2.left + mAdvancingSideParallax.x,
+                    mTempRect2.top + mAdvancingSideParallax.y);
+            // Transform the screen-based split bounds to surface-based crop bounds.
+            mTempRect2.offsetTo(-mAdvancingSideParallax.x, -mAdvancingSideParallax.y);
+            t.setWindowCrop(advancingLeash, mTempRect2);
+        }
+    }
+
+    void adjustDimSurface(SurfaceControl.Transaction t,
+            SurfaceControl dimLayer1, SurfaceControl dimLayer2) {
+        SurfaceControl targetDimLayer;
+        switch (mDismissingSide) {
+            case DOCKED_TOP:
+            case DOCKED_LEFT:
+                targetDimLayer = dimLayer1;
+                break;
+            case DOCKED_BOTTOM:
+            case DOCKED_RIGHT:
+                targetDimLayer = dimLayer2;
+                break;
+            case DOCKED_INVALID:
+            default:
+                t.setAlpha(dimLayer1, 0).hide(dimLayer1);
+                t.setAlpha(dimLayer2, 0).hide(dimLayer2);
+                return;
+        }
+        t.setAlpha(targetDimLayer, mDismissingDimValue)
+                .setVisibility(targetDimLayer, mDismissingDimValue > 0.001f);
+    }
+}
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 9fb36b3..28da8bf 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
@@ -33,14 +33,12 @@
 import android.animation.ValueAnimator;
 import android.app.ActivityManager;
 import android.content.Context;
-import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
 import android.graphics.Color;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.os.Binder;
-import android.os.Trace;
 import android.view.IWindow;
 import android.view.LayoutInflater;
 import android.view.SurfaceControl;
@@ -52,12 +50,10 @@
 import android.widget.ImageView;
 
 import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
 
 import com.android.launcher3.icons.IconProvider;
 import com.android.wm.shell.R;
 import com.android.wm.shell.common.ScreenshotUtils;
-import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SurfaceUtils;
 
 import java.util.function.Consumer;
@@ -83,19 +79,9 @@
     private static final String RESIZING_BACKGROUND_SURFACE_NAME = "ResizingBackground";
     private static final String GAP_BACKGROUND_SURFACE_NAME = "GapBackground";
 
-    // Indicates the loading state of mIcon
-    enum IconLoadState {
-        NOT_LOADED,
-        LOADING,
-        LOADED
-    }
-
     private final IconProvider mIconProvider;
-    private final ShellExecutor mMainExecutor;
-    private final ShellExecutor mBgExecutor;
 
     private Drawable mIcon;
-    private IconLoadState mIconLoadState = IconLoadState.NOT_LOADED;
     private ImageView mVeilIconView;
     private SurfaceControlViewHost mViewHost;
     /** The parent surface that this is attached to. Should be the stage root. */
@@ -123,14 +109,9 @@
     private int mOffsetY;
     private int mRunningAnimationCount = 0;
 
-    public SplitDecorManager(Configuration configuration,
-            IconProvider iconProvider,
-            ShellExecutor mainExecutor,
-            ShellExecutor bgExecutor) {
+    public SplitDecorManager(Configuration configuration, IconProvider iconProvider) {
         super(configuration, null /* rootSurface */, null /* hostInputToken */);
         mIconProvider = iconProvider;
-        mMainExecutor = mainExecutor;
-        mBgExecutor = bgExecutor;
     }
 
     @Override
@@ -218,7 +199,6 @@
         }
         mHostLeash = null;
         mIcon = null;
-        mIconLoadState = IconLoadState.NOT_LOADED;
         mVeilIconView = null;
         mIsCurrentlyChanging = false;
         mShown = false;
@@ -227,10 +207,37 @@
         mInstantaneousBounds.setEmpty();
     }
 
-    /** Showing resizing hint. */
+    /**
+     * Called on every frame when an app is getting resized, and controls the showing & hiding of
+     * the app veil. IMPORTANT: There is one SplitDecorManager for each task, so if two tasks are
+     * getting resized simultaneously, this method is called in parallel on the other
+     * SplitDecorManager too. In general, we want to hide the app behind a veil when:
+     *   a) the app is stretching past its original bounds (because app content layout doesn't
+     *      update mid-stretch).
+     *   b) the app is resizing down from fullscreen (because there is no parallax effect that
+     *      makes every app look good in this scenario).
+     * In the world of flexible split, where apps can go offscreen, there is an exception to this:
+     *   - We do NOT hide the app when it is going offscreen, even though it is technically
+     *     getting larger and would qualify for condition (a). Instead, we use parallax to give
+     *     the illusion that the app is getting pushed offscreen by the divider.
+     *
+     * @param resizingTask The task that is getting resized.
+     * @param newBounds The bounds that that we are updating this surface to. This can be an
+     *                  instantaneous bounds, just for a frame, during a drag or animation.
+     * @param sideBounds The bounds of the OPPOSITE task in the split layout. This is used just for
+     *                   reference/calculation, the surface of the other app won't be set here.
+     * @param displayBounds The bounds of the entire display.
+     * @param t The transaction on which these changes will be bundled.
+     * @param offsetX The x-translation applied to the task surface for parallax. Will be used to
+     *                position the task screenshot and/or icon veil.
+     * @param offsetY The x-translation applied to the task surface for parallax. Will be used to
+     *                position the task screenshot and/or icon veil.
+     * @param immediately {@code true} if the veil should transition in/out instantly, with no
+     *                                animation.
+     */
     public void onResizing(ActivityManager.RunningTaskInfo resizingTask, Rect newBounds,
-            Rect sideBounds, SurfaceControl.Transaction t, int offsetX, int offsetY,
-            boolean immediately) {
+            Rect sideBounds, Rect displayBounds, SurfaceControl.Transaction t, int offsetX,
+            int offsetY, boolean immediately) {
         if (mVeilIconView == null) {
             return;
         }
@@ -252,7 +259,10 @@
         final boolean isStretchingPastOriginalBounds =
                 newBounds.width() > mOldMainBounds.width()
                         || newBounds.height() > mOldMainBounds.height();
-        final boolean showVeil = isResizingDownFromFullscreen || isStretchingPastOriginalBounds;
+        final boolean isFullyOnscreen = displayBounds.contains(newBounds);
+        boolean showVeil = isFullyOnscreen
+                && (isResizingDownFromFullscreen || isStretchingPastOriginalBounds);
+
         final boolean update = showVeil != mShown;
         if (update && mFadeAnimator != null && mFadeAnimator.isRunning()) {
             // If we need to animate and animator still running, cancel it before we ensure both
@@ -280,11 +290,10 @@
                     .setWindowCrop(mGapBackgroundLeash, sideBounds.width(), sideBounds.height());
         }
 
-        if (mIconLoadState == IconLoadState.NOT_LOADED && resizingTask.topActivityInfo != null) {
-            loadIconInBackground(resizingTask.topActivityInfo, () -> {
-                mVeilIconView.setImageDrawable(mIcon);
-                mVeilIconView.setVisibility(View.VISIBLE);
-            });
+        if (mIcon == null && resizingTask.topActivityInfo != null) {
+            mIcon = mIconProvider.getIcon(resizingTask.topActivityInfo);
+            mVeilIconView.setImageDrawable(mIcon);
+            mVeilIconView.setVisibility(View.VISIBLE);
 
             WindowManager.LayoutParams lp =
                     (WindowManager.LayoutParams) mViewHost.getView().getLayoutParams();
@@ -438,10 +447,10 @@
         }
 
         if (mIcon == null && resizingTask.topActivityInfo != null) {
-            loadIconInBackground(resizingTask.topActivityInfo, () -> {
-                mVeilIconView.setImageDrawable(mIcon);
-                mVeilIconView.setVisibility(View.VISIBLE);
-            });
+            // Initialize icon
+            mIcon = mIconProvider.getIcon(resizingTask.topActivityInfo);
+            mVeilIconView.setImageDrawable(mIcon);
+            mVeilIconView.setVisibility(View.VISIBLE);
 
             WindowManager.LayoutParams lp =
                     (WindowManager.LayoutParams) mViewHost.getView().getLayoutParams();
@@ -474,7 +483,7 @@
             return;
         }
 
-        // Re-center icon
+        // Recenter icon
         t.setPosition(mIconLeash,
                 mInstantaneousBounds.width() / 2f - mIconSize / 2f,
                 mInstantaneousBounds.height() / 2f - mIconSize / 2f);
@@ -617,38 +626,9 @@
             mVeilIconView.setImageDrawable(null);
             t.hide(mIconLeash);
             mIcon = null;
-            mIconLoadState = IconLoadState.NOT_LOADED;
         }
     }
 
-    /**
-     * Loads the icon for the given {@param info}, calling {@param postLoadCb} on the main thread
-     * if provided.
-     */
-    private void loadIconInBackground(@NonNull ActivityInfo info, @Nullable Runnable postLoadCb) {
-        mIconLoadState = IconLoadState.LOADING;
-        mBgExecutor.setBoost();
-        mBgExecutor.execute(() -> {
-            Trace.beginSection("SplitDecorManager.loadIconInBackground("
-                    + info.applicationInfo.packageName + ")");
-            final Drawable icon = mIconProvider.getIcon(info);
-            Trace.endSection();
-            mMainExecutor.execute(() -> {
-                if (mIconLoadState != IconLoadState.LOADING) {
-                    // The request was canceled while loading in the background, just drop the
-                    // result
-                    return;
-                }
-                mIcon = icon;
-                mIconLoadState = IconLoadState.LOADED;
-                if (postLoadCb != null) {
-                    postLoadCb.run();
-                }
-            });
-            mBgExecutor.resetBoost();
-        });
-    }
-
     private static float[] getResizingBackgroundColor(ActivityManager.RunningTaskInfo taskInfo) {
         final int taskBgColor = taskInfo.taskDescription.getBackgroundColor();
         return Color.valueOf(taskBgColor == -1 ? Color.WHITE : taskBgColor).getComponents();
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 4bcec70..cd5c135 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
@@ -18,19 +18,14 @@
 
 import static android.content.res.Configuration.SCREEN_HEIGHT_DP_UNDEFINED;
 import static android.content.res.Configuration.SCREEN_WIDTH_DP_UNDEFINED;
-import static android.view.WindowManager.DOCKED_BOTTOM;
-import static android.view.WindowManager.DOCKED_INVALID;
 import static android.view.WindowManager.DOCKED_LEFT;
-import static android.view.WindowManager.DOCKED_RIGHT;
 import static android.view.WindowManager.DOCKED_TOP;
 
 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.shared.animation.Interpolators.DIM_INTERPOLATOR;
 import static com.android.wm.shell.shared.animation.Interpolators.EMPHASIZED;
 import static com.android.wm.shell.shared.animation.Interpolators.FAST_OUT_SLOW_IN;
 import static com.android.wm.shell.shared.animation.Interpolators.LINEAR;
-import static com.android.wm.shell.shared.animation.Interpolators.SLOWDOWN_INTERPOLATOR;
 import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_10_90;
 import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_90_10;
 import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_3_10_45_45;
@@ -52,7 +47,6 @@
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Insets;
-import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.Handler;
 import android.util.Log;
@@ -103,9 +97,17 @@
  */
 public final class SplitLayout implements DisplayInsetsController.OnInsetsChangedListener {
     private static final String TAG = "SplitLayout";
+    /** No parallax effect when the user is dragging the divider */
     public static final int PARALLAX_NONE = 0;
     public static final int PARALLAX_DISMISSING = 1;
+    /** Parallax effect (center-aligned) when the user is dragging the divider */
     public static final int PARALLAX_ALIGN_CENTER = 2;
+    /**
+     * A custom parallax effect for flexible split. When an app is being pushed/pulled offscreen,
+     * we use a specific parallax to give the impression that it is stuck to the divider.
+     * Otherwise, we fall back to PARALLAX_ALIGN_CENTER behavior.
+     */
+    public static final int PARALLAX_FLEX = 3;
 
     public static final int FLING_RESIZE_DURATION = 250;
     private static final int FLING_ENTER_DURATION = 450;
@@ -146,6 +148,7 @@
     private int mDividerSize;
 
     private final Rect mTempRect = new Rect();
+    private final Rect mTempRect2 = new Rect();
     private final Rect mRootBounds = new Rect();
     private final Rect mDividerBounds = new Rect();
     /**
@@ -219,7 +222,7 @@
                 parentContainerCallbacks);
         mTaskOrganizer = taskOrganizer;
         mImePositionProcessor = new ImePositionProcessor(mContext.getDisplayId());
-        mSurfaceEffectPolicy = new ResizingEffectPolicy(parallaxType);
+        mSurfaceEffectPolicy = new ResizingEffectPolicy(parallaxType, this);
         mSplitState = splitState;
 
         final Resources res = mContext.getResources();
@@ -580,7 +583,8 @@
         DockedDividerUtils.sanitizeStackBounds(bounds1, true /** topLeft */);
         DockedDividerUtils.sanitizeStackBounds(bounds2, false /** topLeft */);
         if (setEffectBounds) {
-            mSurfaceEffectPolicy.applyDividerPosition(position, mIsLeftRightSplit);
+            mSurfaceEffectPolicy.applyDividerPosition(
+                    position, mIsLeftRightSplit, mDividerSnapAlgorithm);
         }
     }
 
@@ -710,8 +714,9 @@
      */
     void updateDividerBounds(int position, boolean shouldUseParallaxEffect) {
         updateBounds(position);
-        mSplitLayoutHandler.onLayoutSizeChanging(this, mSurfaceEffectPolicy.mParallaxOffset.x,
-                mSurfaceEffectPolicy.mParallaxOffset.y, shouldUseParallaxEffect);
+        mSplitLayoutHandler.onLayoutSizeChanging(this,
+                mSurfaceEffectPolicy.mRetreatingSideParallax.x,
+                mSurfaceEffectPolicy.mRetreatingSideParallax.y, shouldUseParallaxEffect);
     }
 
     void setDividerPosition(int position, boolean applyLayoutChange) {
@@ -794,10 +799,6 @@
     }
 
     void onStartDragging() {
-        // This triggers initialization of things like the resize veil in preparation for
-        // showing it when the user moves the divider past the slop
-        updateDividerBounds(getDividerPosition(), false /* shouldUseParallaxEffect */);
-
         mInteractionJankMonitor.begin(getDividerLeash(), mContext, mHandler,
                 CUJ_SPLIT_SCREEN_RESIZE);
     }
@@ -1361,169 +1362,6 @@
         int getSplitItemPosition(WindowContainerToken token);
     }
 
-    /**
-     * Calculates and applies proper dismissing parallax offset and dimming value to hint users
-     * dismissing gesture.
-     */
-    private class ResizingEffectPolicy {
-        /** Indicates whether to offset splitting bounds to hint dismissing progress or not. */
-        private final int mParallaxType;
-
-        int mShrinkSide = DOCKED_INVALID;
-
-        // The current dismissing side.
-        int mDismissingSide = DOCKED_INVALID;
-
-        // The parallax offset to hint the dismissing side and progress.
-        final Point mParallaxOffset = new Point();
-
-        // The dimming value to hint the dismissing side and progress.
-        float mDismissingDimValue = 0.0f;
-        final Rect mContentBounds = new Rect();
-        final Rect mSurfaceBounds = new Rect();
-
-        ResizingEffectPolicy(int parallaxType) {
-            mParallaxType = parallaxType;
-        }
-
-        /**
-         * Applies a parallax to the task to hint dismissing progress.
-         *
-         * @param position    the split position to apply dismissing parallax effect
-         * @param isLeftRightSplit indicates whether it's splitting horizontally or vertically
-         */
-        void applyDividerPosition(int position, boolean isLeftRightSplit) {
-            mDismissingSide = DOCKED_INVALID;
-            mParallaxOffset.set(0, 0);
-            mDismissingDimValue = 0;
-
-            int totalDismissingDistance = 0;
-            if (position < mDividerSnapAlgorithm.getFirstSplitTarget().position) {
-                mDismissingSide = isLeftRightSplit ? DOCKED_LEFT : DOCKED_TOP;
-                totalDismissingDistance = mDividerSnapAlgorithm.getDismissStartTarget().position
-                        - mDividerSnapAlgorithm.getFirstSplitTarget().position;
-            } else if (position > mDividerSnapAlgorithm.getLastSplitTarget().position) {
-                mDismissingSide = isLeftRightSplit ? DOCKED_RIGHT : DOCKED_BOTTOM;
-                totalDismissingDistance = mDividerSnapAlgorithm.getLastSplitTarget().position
-                        - mDividerSnapAlgorithm.getDismissEndTarget().position;
-            }
-
-            final boolean topLeftShrink = isLeftRightSplit
-                    ? position < getTopLeftContentBounds().right
-                    : position < getTopLeftContentBounds().bottom;
-            if (topLeftShrink) {
-                mShrinkSide = isLeftRightSplit ? DOCKED_LEFT : DOCKED_TOP;
-                mContentBounds.set(getTopLeftContentBounds());
-                mSurfaceBounds.set(getTopLeftBounds());
-            } else {
-                mShrinkSide = isLeftRightSplit ? DOCKED_RIGHT : DOCKED_BOTTOM;
-                mContentBounds.set(getBottomRightContentBounds());
-                mSurfaceBounds.set(getBottomRightBounds());
-            }
-
-            if (mDismissingSide != DOCKED_INVALID) {
-                float fraction = Math.max(0,
-                        Math.min(mDividerSnapAlgorithm.calculateDismissingFraction(position), 1f));
-                mDismissingDimValue = DIM_INTERPOLATOR.getInterpolation(fraction);
-                if (mParallaxType == PARALLAX_DISMISSING) {
-                    fraction = calculateParallaxDismissingFraction(fraction, mDismissingSide);
-                    if (isLeftRightSplit) {
-                        mParallaxOffset.x = (int) (fraction * totalDismissingDistance);
-                    } else {
-                        mParallaxOffset.y = (int) (fraction * totalDismissingDistance);
-                    }
-                }
-            }
-
-            if (mParallaxType == PARALLAX_ALIGN_CENTER) {
-                if (isLeftRightSplit) {
-                    mParallaxOffset.x =
-                            (mSurfaceBounds.width() - mContentBounds.width()) / 2;
-                } else {
-                    mParallaxOffset.y =
-                            (mSurfaceBounds.height() - mContentBounds.height()) / 2;
-                }
-            }
-        }
-
-        /**
-         * @return for a specified {@code fraction}, this returns an adjusted value that simulates a
-         * slowing down parallax effect
-         */
-        private float calculateParallaxDismissingFraction(float fraction, int dockSide) {
-            float result = SLOWDOWN_INTERPOLATOR.getInterpolation(fraction) / 3.5f;
-
-            // Less parallax at the top, just because.
-            if (dockSide == WindowManager.DOCKED_TOP) {
-                result /= 2f;
-            }
-            return result;
-        }
-
-        /** Applies parallax offset and dimming value to the root surface at the dismissing side. */
-        void adjustRootSurface(SurfaceControl.Transaction t,
-                SurfaceControl leash1, SurfaceControl leash2) {
-            SurfaceControl targetLeash = null;
-
-            if (mParallaxType == PARALLAX_DISMISSING) {
-                switch (mDismissingSide) {
-                    case DOCKED_TOP:
-                    case DOCKED_LEFT:
-                        targetLeash = leash1;
-                        mTempRect.set(getTopLeftBounds());
-                        break;
-                    case DOCKED_BOTTOM:
-                    case DOCKED_RIGHT:
-                        targetLeash = leash2;
-                        mTempRect.set(getBottomRightBounds());
-                        break;
-                }
-            } else if (mParallaxType == PARALLAX_ALIGN_CENTER) {
-                switch (mShrinkSide) {
-                    case DOCKED_TOP:
-                    case DOCKED_LEFT:
-                        targetLeash = leash1;
-                        mTempRect.set(getTopLeftBounds());
-                        break;
-                    case DOCKED_BOTTOM:
-                    case DOCKED_RIGHT:
-                        targetLeash = leash2;
-                        mTempRect.set(getBottomRightBounds());
-                        break;
-                }
-            }
-            if (mParallaxType != PARALLAX_NONE && targetLeash != null) {
-                t.setPosition(targetLeash,
-                        mTempRect.left + mParallaxOffset.x, mTempRect.top + mParallaxOffset.y);
-                // Transform the screen-based split bounds to surface-based crop bounds.
-                mTempRect.offsetTo(-mParallaxOffset.x, -mParallaxOffset.y);
-                t.setWindowCrop(targetLeash, mTempRect);
-            }
-        }
-
-        void adjustDimSurface(SurfaceControl.Transaction t,
-                SurfaceControl dimLayer1, SurfaceControl dimLayer2) {
-            SurfaceControl targetDimLayer;
-            switch (mDismissingSide) {
-                case DOCKED_TOP:
-                case DOCKED_LEFT:
-                    targetDimLayer = dimLayer1;
-                    break;
-                case DOCKED_BOTTOM:
-                case DOCKED_RIGHT:
-                    targetDimLayer = dimLayer2;
-                    break;
-                case DOCKED_INVALID:
-                default:
-                    t.setAlpha(dimLayer1, 0).hide(dimLayer1);
-                    t.setAlpha(dimLayer2, 0).hide(dimLayer2);
-                    return;
-            }
-            t.setAlpha(targetDimLayer, mDismissingDimValue)
-                    .setVisibility(targetDimLayer, mDismissingDimValue > 0.001f);
-        }
-    }
-
     /** Records IME top offset changes and updates SplitLayout correspondingly. */
     private class ImePositionProcessor implements DisplayImeController.ImePositionProcessor {
         /**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitSpec.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitSpec.java
index 9c951bd..6f1dc56 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitSpec.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitSpec.java
@@ -43,7 +43,7 @@
  */
 public class SplitSpec {
     private static final String TAG = "SplitSpec";
-    private static final boolean DEBUG = true;
+    private static final boolean DEBUG = false;
 
     /** A split ratio used on larger screens, where we can fit both apps onscreen. */
     public static final float ONSCREEN_ONLY_ASYMMETRIC_RATIO = 0.33f;
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 b796b41..1323fe0 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
@@ -20,7 +20,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.app.ActivityManager.RunningTaskInfo;
 import android.app.TaskInfo;
 import android.content.ComponentName;
 import android.content.Context;
@@ -60,7 +59,6 @@
 import com.android.wm.shell.sysui.KeyguardChangeListener;
 import com.android.wm.shell.sysui.ShellController;
 import com.android.wm.shell.sysui.ShellInit;
-import com.android.wm.shell.transition.FocusTransitionObserver;
 import com.android.wm.shell.transition.Transitions;
 
 import dagger.Lazy;
@@ -73,6 +71,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;
 
 /**
@@ -198,9 +197,6 @@
     private final CompatUIStatusManager mCompatUIStatusManager;
 
     @NonNull
-    private final FocusTransitionObserver mFocusTransitionObserver;
-
-    @NonNull
     private final Optional<DesktopUserRepositories> mDesktopUserRepositories;
 
     public CompatUIController(@NonNull Context context,
@@ -217,8 +213,7 @@
             @NonNull CompatUIShellCommandHandler compatUIShellCommandHandler,
             @NonNull AccessibilityManager accessibilityManager,
             @NonNull CompatUIStatusManager compatUIStatusManager,
-            @NonNull Optional<DesktopUserRepositories> desktopUserRepositories,
-            @NonNull FocusTransitionObserver focusTransitionObserver) {
+            @NonNull Optional<DesktopUserRepositories> desktopUserRepositories) {
         mContext = context;
         mShellController = shellController;
         mDisplayController = displayController;
@@ -235,7 +230,6 @@
                 DISAPPEAR_DELAY_MS, flags);
         mCompatUIStatusManager = compatUIStatusManager;
         mDesktopUserRepositories = desktopUserRepositories;
-        mFocusTransitionObserver = focusTransitionObserver;
         shellInit.addInitCallback(this::onInit, this);
     }
 
@@ -412,8 +406,7 @@
         // start tracking the buttons visibility for this task.
         if (mTopActivityTaskId != taskInfo.taskId
                 && !taskInfo.isTopActivityTransparent
-                && taskInfo.isVisible
-                && mFocusTransitionObserver.hasGlobalFocus((RunningTaskInfo) taskInfo)) {
+                && taskInfo.isVisible && taskInfo.isFocused) {
             mTopActivityTaskId = taskInfo.taskId;
             setHasShownUserAspectRatioSettingsButton(false);
         }
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 5b6b897..aebd94f 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
@@ -34,7 +34,6 @@
 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.ShellBackgroundThread;
 import com.android.wm.shell.shared.annotations.ShellMainThread;
 import com.android.wm.shell.splitscreen.SplitScreenController;
 import com.android.wm.shell.splitscreen.tv.TvSplitScreenController;
@@ -94,12 +93,11 @@
             SplitState splitState,
             @ShellMainThread ShellExecutor mainExecutor,
             Handler mainHandler,
-            @ShellBackgroundThread ShellExecutor bgExecutor,
             SystemWindows systemWindows) {
         return new TvSplitScreenController(context, shellInit, shellCommandHandler, shellController,
                 shellTaskOrganizer, syncQueue, rootTDAOrganizer, displayController,
                 displayImeController, displayInsetsController, transitions, transactionPool,
                 iconProvider, recentTasks, launchAdjacentController, multiInstanceHelper,
-                splitState, mainExecutor, mainHandler, bgExecutor, systemWindows);
+                splitState, mainExecutor, mainHandler, systemWindows);
     }
 }
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 1408b6e..23a0f4a 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
@@ -108,7 +108,6 @@
 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;
 import com.android.wm.shell.shared.annotations.ShellSplashscreenThread;
 import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
@@ -273,8 +272,7 @@
             @NonNull CompatUIState compatUIState,
             @NonNull CompatUIComponentIdGenerator componentIdGenerator,
             @NonNull CompatUIComponentFactory compatUIComponentFactory,
-            CompatUIStatusManager compatUIStatusManager,
-            @NonNull FocusTransitionObserver focusTransitionObserver) {
+            CompatUIStatusManager compatUIStatusManager) {
         if (!context.getResources().getBoolean(R.bool.config_enableCompatUIController)) {
             return Optional.empty();
         }
@@ -299,8 +297,7 @@
                         compatUIShellCommandHandler.get(),
                         accessibilityManager.get(),
                         compatUIStatusManager,
-                        desktopUserRepositories,
-                        focusTransitionObserver));
+                        desktopUserRepositories));
     }
 
     @WMSingleton
@@ -438,29 +435,24 @@
             ShellInit shellInit,
             ShellController shellController,
             @ShellMainThread ShellExecutor shellExecutor,
-            @ShellBackgroundThread Handler backgroundHandler,
             BackAnimationBackground backAnimationBackground,
             Optional<ShellBackAnimationRegistry> shellBackAnimationRegistry,
             ShellCommandHandler shellCommandHandler,
             Transitions transitions,
             @ShellMainThread Handler handler
     ) {
-        if (BackAnimationController.IS_ENABLED) {
             return shellBackAnimationRegistry.map(
                     (animations) ->
                             new BackAnimationController(
                                     shellInit,
                                     shellController,
                                     shellExecutor,
-                                    backgroundHandler,
                                     context,
                                     backAnimationBackground,
                                     animations,
                                     shellCommandHandler,
                                     transitions,
                                     handler));
-        }
-        return Optional.empty();
     }
 
     @BindsOptionalOf
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 48b0a6c..1916215 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
@@ -520,8 +520,7 @@
             MultiInstanceHelper multiInstanceHelper,
             SplitState splitState,
             @ShellMainThread ShellExecutor mainExecutor,
-            @ShellMainThread Handler mainHandler,
-            @ShellBackgroundThread ShellExecutor bgExecutor) {
+            @ShellMainThread Handler mainHandler) {
         return new SplitScreenController(
                 context,
                 shellInit,
@@ -545,8 +544,7 @@
                 multiInstanceHelper,
                 splitState,
                 mainExecutor,
-                mainHandler,
-                bgExecutor);
+                mainHandler);
     }
 
     //
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 94a6e58..9e2b9b2 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
@@ -81,11 +81,12 @@
             @NonNull PipScheduler pipScheduler,
             @NonNull PipTransitionState pipStackListenerController,
             @NonNull PipDisplayLayoutState pipDisplayLayoutState,
-            @NonNull PipUiStateChangeController pipUiStateChangeController) {
+            @NonNull PipUiStateChangeController pipUiStateChangeController,
+            Optional<DesktopUserRepositories> desktopUserRepositoriesOptional) {
         return new PipTransition(context, shellInit, shellTaskOrganizer, transitions,
                 pipBoundsState, null, pipBoundsAlgorithm, pipTaskListener,
                 pipScheduler, pipStackListenerController, pipDisplayLayoutState,
-                pipUiStateChangeController);
+                pipUiStateChangeController, desktopUserRepositoriesOptional);
     }
 
     @WMSingleton
@@ -116,6 +117,7 @@
             PipTouchHandler pipTouchHandler,
             PipAppOpsListener pipAppOpsListener,
             PhonePipMenuController pipMenuController,
+            PipUiEventLogger pipUiEventLogger,
             @ShellMainThread ShellExecutor mainExecutor) {
         if (!PipUtils.isPip2ExperimentEnabled()) {
             return Optional.empty();
@@ -125,7 +127,7 @@
                     displayInsetsController, pipBoundsState, pipBoundsAlgorithm,
                     pipDisplayLayoutState, pipScheduler, taskStackListener, shellTaskOrganizer,
                     pipTransitionState, pipTouchHandler, pipAppOpsListener, pipMenuController,
-                    mainExecutor));
+                    pipUiEventLogger, mainExecutor));
         }
     }
 
@@ -187,11 +189,11 @@
             FloatingContentCoordinator floatingContentCoordinator,
             PipScheduler pipScheduler,
             Optional<PipPerfHintController> pipPerfHintControllerOptional,
-            PipBoundsAlgorithm pipBoundsAlgorithm,
-            PipTransitionState pipTransitionState) {
+            PipTransitionState pipTransitionState,
+            PipUiEventLogger pipUiEventLogger) {
         return new PipMotionHelper(context, pipBoundsState, menuController, pipSnapAlgorithm,
                 floatingContentCoordinator, pipScheduler, pipPerfHintControllerOptional,
-                pipBoundsAlgorithm, pipTransitionState);
+                pipTransitionState, pipUiEventLogger);
     }
 
     @WMSingleton
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImmersiveController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImmersiveController.kt
index 536dc2a..a4620d5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImmersiveController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopImmersiveController.kt
@@ -339,7 +339,7 @@
                         .setWindowCrop(leash, endBounds.width(), endBounds.height())
                         .apply()
                     onTaskResizeAnimationListener?.onAnimationEnd(taskId)
-                    finishCallback.onTransitionFinished(null /* wct */)
+                    finishCallback.onTransitionFinished(/* wct= */ null)
                 }
             )
             addUpdateListener { animation ->
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 ceef699..e8f9a78 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
@@ -306,7 +306,7 @@
     fun logTaskInfoStateInit() {
         logTaskUpdate(
             FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__TASK_EVENT__TASK_INIT_STATSD,
-            /* session_id */ 0,
+            sessionId = 0,
             TaskUpdate(
                 visibleTaskCount = 0,
                 instanceId = 0,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLogger.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLogger.kt
index 301ba9e..b96b9d2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLogger.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUiEventLogger.kt
@@ -145,7 +145,11 @@
         @UiEvent(doc = "Moving the desktop window by dragging the header")
         DESKTOP_WINDOW_MOVE_BY_HEADER_DRAG(2021),
         @UiEvent(doc = "Double tap on the window header to refocus a desktop window")
-        DESKTOP_WINDOW_HEADER_TAP_TO_REFOCUS(2022);
+        DESKTOP_WINDOW_HEADER_TAP_TO_REFOCUS(2022),
+        @UiEvent(doc = "Enter multi-instance by using the New Window button")
+        DESKTOP_WINDOW_MULTI_INSTANCE_NEW_WINDOW_CLICK(2069),
+        @UiEvent(doc = "Enter multi-instance by clicking an icon in the Manage Windows menu")
+        DESKTOP_WINDOW_MULTI_INSTANCE_MANAGE_WINDOWS_ICON_CLICK(2070);
 
         override fun getId(): Int = mId
     }
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 cd37113..32ee319 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
@@ -274,7 +274,7 @@
         lp.inputFeatures |= INPUT_FEATURE_NO_INPUT_CHANNEL;
         final WindowlessWindowManager windowManager = new WindowlessWindowManager(
                 mTaskInfo.configuration, mLeash,
-                null /* hostInputToken */);
+                /* hostInputToken= */ null);
         mViewHost = new SurfaceControlViewHost(mContext,
                 mDisplayController.getDisplay(mTaskInfo.displayId), windowManager,
                 "DesktopModeVisualIndicator");
@@ -338,7 +338,7 @@
         if (mCurrentType == NO_INDICATOR) {
             fadeInIndicator(newType);
         } else if (newType == NO_INDICATOR) {
-            fadeOutIndicator(null /* finishCallback */);
+            fadeOutIndicator(/* finishCallback= */ null);
         } else {
             final VisualIndicatorAnimator animator = VisualIndicatorAnimator.animateIndicatorType(
                     mView, mDisplayController.getDisplayLayout(mTaskInfo.displayId), mCurrentType,
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 d180ea7..050dfb6 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
@@ -310,6 +310,11 @@
         transitions.startTransition(transitionType, wct, handler).also { t ->
             handler?.setTransition(t)
         }
+
+        // launch from recent DesktopTaskView
+        desktopModeEnterExitTransitionListener?.onEnterDesktopModeTransitionStarted(
+            FREEFORM_ANIMATION_DURATION
+        )
     }
 
     /** Gets number of visible freeform tasks in [displayId]. */
@@ -762,7 +767,7 @@
             return
         }
         val wct = WindowContainerTransaction()
-        wct.reorder(taskInfo.token, true /* onTop */, true /* includingParents */)
+        wct.reorder(taskInfo.token, /* onTop= */ true, /* includingParents= */ true)
         startLaunchTransition(
             transitionType = TRANSIT_TO_FRONT,
             wct = wct,
@@ -884,7 +889,7 @@
         } else if (Flags.enableMoveToNextDisplayShortcut()) {
             applyFreeformDisplayChange(wct, task, displayId)
         }
-        wct.reparent(task.token, displayAreaInfo.token, true /* onTop */)
+        wct.reparent(task.token, displayAreaInfo.token, /* onTop= */ true)
         if (Flags.enableDisplayFocusInShellTransitions()) {
             // Bring the destination display to top with includingParents=true, so that the
             // destination display gains the display focus, which makes the top task in the display
@@ -896,7 +901,7 @@
             performDesktopExitCleanupIfNeeded(task.taskId, task.displayId, wct)
         }
 
-        transitions.startTransition(TRANSIT_CHANGE, wct, null /* handler */)
+        transitions.startTransition(TRANSIT_CHANGE, wct, /* handler= */ null)
     }
 
     /**
@@ -1344,7 +1349,7 @@
 
     private fun addWallpaperActivity(displayId: Int, wct: WindowContainerTransaction) {
         logV("addWallpaperActivity")
-        if (Flags.enableDesktopWallpaperActivityOnSystemUser()) {
+        if (Flags.enableDesktopWallpaperActivityForSystemUser()) {
             val intent = Intent(context, DesktopWallpaperActivity::class.java)
             val options =
                 ActivityOptions.makeBasic().apply {
@@ -1393,7 +1398,7 @@
     private fun removeWallpaperActivity(wct: WindowContainerTransaction) {
         desktopWallpaperActivityTokenProvider.getToken()?.let { token ->
             logV("removeWallpaperActivity")
-            if (Flags.enableDesktopWallpaperActivityOnSystemUser()) {
+            if (Flags.enableDesktopWallpaperActivityForSystemUser()) {
                 wct.reorder(token, /* onTop= */ false)
             } else {
                 wct.removeTask(token)
@@ -1672,7 +1677,7 @@
                 requestedTaskId,
                 splitPosition,
                 options.toBundle(),
-                null, /* hideTaskToken */
+                /* hideTaskToken= */ null,
             )
         }
     }
@@ -1709,8 +1714,8 @@
                     fillIn,
                     splitPosition,
                     options.toBundle(),
-                    null /* hideTaskToken */,
-                    true /* forceLaunchNewTask */,
+                    /* hideTaskToken= */ null,
+                    /* forceLaunchNewTask= */ true,
                     splitIndex,
                 )
             }
@@ -1961,7 +1966,7 @@
             wct.setBounds(taskInfo.token, initialBounds)
         }
         wct.setWindowingMode(taskInfo.token, targetWindowingMode)
-        wct.reorder(taskInfo.token, true /* onTop */)
+        wct.reorder(taskInfo.token, /* onTop= */ true)
         if (useDesktopOverrideDensity()) {
             wct.setDensityDpi(taskInfo.token, DESKTOP_DENSITY_OVERRIDE)
         }
@@ -2796,7 +2801,7 @@
                 controller,
                 "visibleTaskCount",
                 { controller -> result[0] = controller.visibleTaskCount(displayId) },
-                true, /* blocking */
+                /* blocking= */ true,
             )
             return result[0]
         }
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 0330a5f..c2dd4d28 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
@@ -234,7 +234,7 @@
         // If it's a running task, reorder it to back.
         taskIdToMinimize
             ?.let { shellTaskOrganizer.getRunningTaskInfo(it) }
-            ?.let { wct.reorder(it.token, false /* onTop */) }
+            ?.let { wct.reorder(it.token, /* onTop= */ false) }
         return taskIdToMinimize
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
index e7a0077..9bf5555 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt
@@ -236,7 +236,7 @@
         if (transitionToCloseWallpaper == transition) {
             // TODO: b/362469671 - Handle merging the animation when desktop is also closing.
             desktopWallpaperActivityTokenProvider.getToken()?.let { wallpaperActivityToken ->
-                if (Flags.enableDesktopWallpaperActivityOnSystemUser()) {
+                if (Flags.enableDesktopWallpaperActivityForSystemUser()) {
                     transitions.startTransition(
                         TRANSIT_TO_BACK,
                         WindowContainerTransaction()
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 72c0642..1380a9c 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
@@ -131,7 +131,7 @@
         val pendingIntent =
             PendingIntent.getActivityAsUser(
                 context.createContextAsUser(taskUser, /* flags= */ 0),
-                0 /* requestCode */,
+                /* requestCode= */ 0,
                 launchHomeIntent,
                 FLAG_MUTABLE or FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT or FILL_IN_COMPONENT,
                 options.toBundle(),
@@ -234,7 +234,7 @@
             val wct = WindowContainerTransaction()
             restoreWindowOrder(wct, state)
             state.startTransitionFinishTransaction?.apply()
-            state.startTransitionFinishCb?.onTransitionFinished(null /* wct */)
+            state.startTransitionFinishCb?.onTransitionFinished(/* wct= */ null)
             requestSplitFromScaledTask(splitPosition, wct)
             clearState()
         } else {
@@ -440,7 +440,7 @@
             val wct = WindowContainerTransaction()
             restoreWindowOrder(wct)
             state.startTransitionFinishTransaction?.apply()
-            state.startTransitionFinishCb?.onTransitionFinished(null /* wct */)
+            state.startTransitionFinishCb?.onTransitionFinished(/* wct= */ null)
             requestSplitSelect(wct, taskInfo, splitPosition)
         }
         return true
@@ -492,7 +492,7 @@
                 finishTransaction = startTransactionFinishT,
             )
             // Call finishCallback to merge animation before startTransitionFinishCb is called
-            finishCallback.onTransitionFinished(null /* wct */)
+            finishCallback.onTransitionFinished(/* wct= */ null)
             animateEndDragToDesktop(startTransaction = t, startTransitionFinishCb)
         } else if (isCancelTransition) {
             info.changes.forEach { change ->
@@ -500,8 +500,8 @@
                 startTransactionFinishT.show(change.leash)
             }
             t.apply()
-            finishCallback.onTransitionFinished(null /* wct */)
-            startTransitionFinishCb.onTransitionFinished(null /* wct */)
+            finishCallback.onTransitionFinished(/* wct= */ null)
+            startTransitionFinishCb.onTransitionFinished(/* wct= */ null)
             clearState()
         }
     }
@@ -653,7 +653,7 @@
             interactionJankMonitor.cancel(CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_HOLD)
         } else if (state.cancelTransitionToken == transition) {
             state.draggedTaskChange?.leash?.let { state.startTransitionFinishTransaction?.show(it) }
-            state.startTransitionFinishCb?.onTransitionFinished(null /* wct */)
+            state.startTransitionFinishCb?.onTransitionFinished(/* wct= */ null)
             clearState()
         } else {
             // This transition being aborted is neither the start, nor the cancel transition, so
@@ -741,19 +741,19 @@
                         // TODO(b/322852244): investigate why even though these "other" tasks are
                         //  reordered in front of home and behind the translucent dragged task, its
                         //  surface is not visible on screen.
-                        wct.reorder(wc, true /* toTop */)
+                        wct.reorder(wc, /* onTop= */ true)
                     }
                 val wc =
                     state.draggedTaskChange?.container
                         ?: error("Dragged task should be non-null before cancelling")
                 // Then the dragged task a the very top.
-                wct.reorder(wc, true /* toTop */)
+                wct.reorder(wc, /* onTop= */ true)
             }
             is TransitionState.FromSplit -> {
                 val wc =
                     state.splitRootChange?.container
                         ?: error("Split root should be non-null before cancelling")
-                wct.reorder(wc, true /* toTop */)
+                wct.reorder(wc, /* onTop= */ true)
             }
         }
         val homeWc =
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 491b577..e24b2c5 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
@@ -332,7 +332,9 @@
             dragSession = new DragSession(ActivityTaskManager.getInstance(),
                     mDisplayController.getDisplayLayout(displayId), event.getClipData(),
                     event.getDragFlags());
-            dragSession.initialize();
+            // Only update the running task for now to determine if we should defer to desktop to
+            // handle the drag
+            dragSession.updateRunningTask();
             final ActivityManager.RunningTaskInfo taskInfo = dragSession.runningTaskInfo;
             // Desktop tasks will have their own drag handling.
             final boolean isDesktopDrag = taskInfo != null && taskInfo.isFreeform()
@@ -340,7 +342,8 @@
             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(),
+                    pd.isHandlingDrag,
+                    event.getClipData() != null ? event.getClipData().getItemCount() : -1,
                     DragUtils.getMimeTypesConcatenated(description),
                     DragUtils.dragFlagsToString(event.getDragFlags()));
         }
@@ -355,6 +358,8 @@
                     Slog.w(TAG, "Unexpected drag start during an active drag");
                     return false;
                 }
+                // Only initialize the session after we've checked that we're handling the drag
+                dragSession.initialize(true /* skipUpdateRunningTask */);
                 pd.dragSession = dragSession;
                 pd.activeDragCount++;
                 pd.dragLayout.prepare(pd.dragSession, mLogger.logStart(pd.dragSession));
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragSession.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragSession.java
index c4ff87d..279452e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragSession.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragSession.java
@@ -29,7 +29,6 @@
 import android.content.ClipDescription;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
-import android.os.PersistableBundle;
 
 import androidx.annotation.Nullable;
 
@@ -44,6 +43,7 @@
  */
 public class DragSession {
     private final ActivityTaskManager mActivityTaskManager;
+    @Nullable
     private final ClipData mInitialDragData;
     private final int mInitialDragFlags;
 
@@ -66,7 +66,7 @@
     @WindowConfiguration.ActivityType
     int runningTaskActType = ACTIVITY_TYPE_STANDARD;
     boolean dragItemSupportsSplitscreen;
-    int hideDragSourceTaskId = -1;
+    final int hideDragSourceTaskId;
 
     DragSession(ActivityTaskManager activityTaskManager,
             DisplayLayout dispLayout, ClipData data, int dragFlags) {
@@ -83,7 +83,6 @@
 
     /**
      * Returns the clip description associated with the drag.
-     * @return
      */
     ClipDescription getClipDescription() {
         return mInitialDragData.getDescription();
@@ -125,8 +124,10 @@
     /**
      * Updates the session data based on the current state of the system at the start of the drag.
      */
-    void initialize() {
-        updateRunningTask();
+    void initialize(boolean skipUpdateRunningTask) {
+        if (!skipUpdateRunningTask) {
+            updateRunningTask();
+        }
 
         activityInfo = mInitialDragData.getItemAt(0).getActivityInfo();
         // TODO: This should technically check & respect config_supportsNonResizableMultiWindow
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragUtils.java
index 248a112..a62dd1c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/draganddrop/DragUtils.java
@@ -49,7 +49,7 @@
      * Returns whether we can handle this particular drag.
      */
     public static boolean canHandleDrag(DragEvent event) {
-        if (event.getClipData().getItemCount() <= 0) {
+        if (event.getClipData() == null || event.getClipData().getItemCount() <= 0) {
             // No clip data, ignore this drag
             return false;
         }
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 55e90e7..bec75b3 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
@@ -664,10 +664,12 @@
                 // TODO(b/375977163): polish the animation to restoring the PIP task back from
                 //  swipe-pip-to-home. Ideally we should send the transitionInfo after reparenting
                 //  the PIP activity back to the original task.
-                if (shouldUseMainWindowFrame) {
+                if (shouldUseMainWindowFrame && isOutPipDirection) {
                     // If we should animate the main window frame, set it to the rotatedRect
                     // instead. The end bounds reported by transitionInfo is the bounds before
                     // rotation, while main window frame is calculated after the rotation.
+                    // Note that we only override main window frame for leaving pip animation as
+                    // the pip activity should match parent.
                     rotatedEndRect.set(mainWindowFrame);
                 } else {
                     // Rotate the end bounds according to the rotation delta because the display
@@ -810,11 +812,19 @@
                         }
                     }
                     final Rect sourceBounds = new Rect(initialContainerRect);
+                    Rect relativeEndWindowFrame = null;
+                    if (isOutPipDirection) {
+                        relativeEndWindowFrame = rotatedEndRect;
+                    }
+                    if (relativeEndWindowFrame != null) {
+                        relativeEndWindowFrame.offset(leashOffset.x, leashOffset.y);
+                    }
                     sourceBounds.inset(insets);
                     getSurfaceTransactionHelper()
                             .rotateAndScaleWithCrop(tx, leash, initialContainerRect, bounds,
                                     insets, degree, x, y, isOutPipDirection,
-                                    rotationDelta == ROTATION_270 /* clockwise */)
+                                    rotationDelta == ROTATION_270 /* clockwise */,
+                                    relativeEndWindowFrame)
                             .round(tx, leash, sourceBounds, bounds)
                             .shadow(tx, leash, shouldApplyShadowRadius());
                     if (!handlePipTransaction(leash, tx, bounds, 1f /* alpha */)) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
index b02bd0f..955a981 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipSurfaceTransactionHelper.java
@@ -25,6 +25,8 @@
 import android.view.Choreographer;
 import android.view.SurfaceControl;
 
+import androidx.annotation.Nullable;
+
 import com.android.wm.shell.R;
 import com.android.wm.shell.transition.Transitions;
 
@@ -224,12 +226,17 @@
     /**
      * Operates the rotation according to the given degrees and scale (setMatrix) according to the
      * source bounds and rotated destination bounds. The crop will be the unscaled source bounds.
+     *
+     * @param relativeEndWindowFrame specified if
+     *   {@link android.app.TaskInfo#topActivityMainWindowFrame} is provided. It's only applied for
+     *   the animation that {@code isExpanding} PIP to original task.
      * @return same {@link PipSurfaceTransactionHelper} instance for method chaining
      */
-    public PipSurfaceTransactionHelper rotateAndScaleWithCrop(SurfaceControl.Transaction tx,
-            SurfaceControl leash, Rect sourceBounds, Rect destinationBounds, Rect insets,
+    public PipSurfaceTransactionHelper rotateAndScaleWithCrop(
+            @NonNull SurfaceControl.Transaction tx, @NonNull SurfaceControl leash,
+            @NonNull Rect sourceBounds, @NonNull Rect destinationBounds, @NonNull Rect insets,
             float degrees, float positionX, float positionY, boolean isExpanding,
-            boolean clockwise) {
+            boolean clockwise, @Nullable Rect relativeEndWindowFrame) {
         mTmpDestinationRect.set(sourceBounds);
         mTmpDestinationRect.inset(insets);
         final int srcW = mTmpDestinationRect.width();
@@ -240,23 +247,31 @@
         // destination are different.
         final float scale = srcW <= srcH ? (float) destW / srcW : (float) destH / srcH;
         final Rect crop = mTmpDestinationRect;
-        crop.set(0, 0, Transitions.SHELL_TRANSITIONS_ROTATION ? destH
-                : destW, Transitions.SHELL_TRANSITIONS_ROTATION ? destW : destH);
-        // Inverse scale for crop to fit in screen coordinates.
-        crop.scale(1 / scale);
-        crop.offset(insets.left, insets.top);
-        if (isExpanding) {
-            // Expand bounds (shrink insets) in source orientation.
-            positionX -= insets.left * scale;
-            positionY -= insets.top * scale;
+        if (isExpanding && relativeEndWindowFrame != null) {
+            // If relative end window frame is provided, it usually means the top activity chooses
+            // a customized layout which may not match parent. In this case, we should crop the
+            // task surface with the window frame. Note that we don't need to consider the insets
+            // because the main window frame excludes the insets.
+            crop.set(relativeEndWindowFrame);
         } else {
-            // Shrink bounds (expand insets) in destination orientation.
-            if (clockwise) {
-                positionX -= insets.top * scale;
-                positionY += insets.left * scale;
+            crop.set(0, 0, Transitions.SHELL_TRANSITIONS_ROTATION ? destH
+                    : destW, Transitions.SHELL_TRANSITIONS_ROTATION ? destW : destH);
+            // Inverse scale for crop to fit in screen coordinates.
+            crop.scale(1 / scale);
+            crop.offset(insets.left, insets.top);
+            if (isExpanding) {
+                // Expand bounds (shrink insets) in source orientation.
+                positionX -= insets.left * scale;
+                positionY -= insets.top * scale;
             } else {
-                positionX += insets.top * scale;
-                positionY -= insets.left * scale;
+                // Shrink bounds (expand insets) in destination orientation.
+                if (clockwise) {
+                    positionX -= insets.top * scale;
+                    positionY += insets.left * scale;
+                } else {
+                    positionX += insets.top * scale;
+                    positionY -= insets.left * scale;
+                }
             }
         }
         mTmpTransform.setScale(scale, scale);
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 0042ec9..bd676ce 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
@@ -844,7 +844,8 @@
             }
             mSurfaceTransactionHelper.rotateAndScaleWithCrop(finishTransaction,
                     pipLeash, endBounds, endBounds, new Rect(), degree, x, y,
-                    true /* isExpanding */, rotationDelta == ROTATION_270 /* clockwise */);
+                    true /* isExpanding */, rotationDelta == ROTATION_270 /* clockwise */,
+                    null /* relativeEndWindowFrame */);
         } else {
             rotationDelta = Surface.ROTATION_0;
         }
@@ -868,7 +869,9 @@
         // Get the start bounds in new orientation.
         final Rect startBounds = new Rect(pipChange.getStartAbsBounds());
         rotateBounds(startBounds, displayRotationChange.getStartAbsBounds(), rotateDelta);
-        final Rect endBounds = new Rect(pipChange.getEndAbsBounds());
+        final Rect windowFrame = taskInfo.topActivityMainWindowFrame;
+        final Rect endBounds = new Rect(windowFrame != null
+                ? windowFrame : pipChange.getEndAbsBounds());
         startBounds.offset(-offset.x, -offset.y);
         endBounds.offset(-offset.x, -offset.y);
 
@@ -888,7 +891,7 @@
         }
         mSurfaceTransactionHelper.rotateAndScaleWithCrop(startTransaction, pipChange.getLeash(),
                 endBounds, startBounds, new Rect(), degree, x, y, true /* isExpanding */,
-                pipRotateDelta == ROTATION_270 /* clockwise */);
+                pipRotateDelta == ROTATION_270 /* clockwise */, null /* relativeEndWindowFrame */);
         startTransaction.apply();
         rotator.cleanUp(finishTransaction);
 
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 8c6d5f5..562b260 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
@@ -59,6 +59,7 @@
 import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
 import com.android.wm.shell.common.pip.PipBoundsState;
 import com.android.wm.shell.common.pip.PipDisplayLayoutState;
+import com.android.wm.shell.common.pip.PipUiEventLogger;
 import com.android.wm.shell.common.pip.PipUtils;
 import com.android.wm.shell.pip.Pip;
 import com.android.wm.shell.protolog.ShellProtoLogGroup;
@@ -98,6 +99,7 @@
     private final PipTouchHandler mPipTouchHandler;
     private final PipAppOpsListener mPipAppOpsListener;
     private final PhonePipMenuController mPipMenuController;
+    private final PipUiEventLogger mPipUiEventLogger;
     private final ShellExecutor mMainExecutor;
     private final PipImpl mImpl;
     private final List<Consumer<Boolean>> mOnIsInPipStateChangedListeners = new ArrayList<>();
@@ -143,6 +145,7 @@
             PipTouchHandler pipTouchHandler,
             PipAppOpsListener pipAppOpsListener,
             PhonePipMenuController pipMenuController,
+            PipUiEventLogger pipUiEventLogger,
             ShellExecutor mainExecutor) {
         mContext = context;
         mShellCommandHandler = shellCommandHandler;
@@ -160,6 +163,7 @@
         mPipTouchHandler = pipTouchHandler;
         mPipAppOpsListener = pipAppOpsListener;
         mPipMenuController = pipMenuController;
+        mPipUiEventLogger = pipUiEventLogger;
         mMainExecutor = mainExecutor;
         mImpl = new PipImpl();
 
@@ -187,6 +191,7 @@
             PipTouchHandler pipTouchHandler,
             PipAppOpsListener pipAppOpsListener,
             PhonePipMenuController pipMenuController,
+            PipUiEventLogger pipUiEventLogger,
             ShellExecutor mainExecutor) {
         if (!context.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) {
             ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
@@ -197,7 +202,7 @@
                 displayController, displayInsetsController, pipBoundsState, pipBoundsAlgorithm,
                 pipDisplayLayoutState, pipScheduler, taskStackListener, shellTaskOrganizer,
                 pipTransitionState, pipTouchHandler, pipAppOpsListener, pipMenuController,
-                mainExecutor);
+                pipUiEventLogger, mainExecutor);
     }
 
     public PipImpl getPipImpl() {
@@ -238,18 +243,6 @@
         });
 
         mPipAppOpsListener.setCallback(mPipTouchHandler.getMotionHelper());
-        mPipTransitionState.addPipTransitionStateChangedListener(
-                (oldState, newState, extra) -> {
-                    if (newState == PipTransitionState.ENTERED_PIP) {
-                        final TaskInfo taskInfo = mPipTransitionState.getPipTaskInfo();
-                        if (taskInfo != null && taskInfo.topActivity != null) {
-                            mPipAppOpsListener.onActivityPinned(
-                                    taskInfo.topActivity.getPackageName());
-                        }
-                    } else if (newState == PipTransitionState.EXITED_PIP) {
-                        mPipAppOpsListener.onActivityUnpinned();
-                    }
-                });
     }
 
     private ExternalInterfaceBinder createExternalInterface() {
@@ -446,14 +439,25 @@
                 mPipTransitionState.setSwipePipToHomeState(overlay, appBounds);
                 break;
             case PipTransitionState.ENTERED_PIP:
+                final TaskInfo taskInfo = mPipTransitionState.getPipTaskInfo();
+                if (taskInfo != null && taskInfo.topActivity != null) {
+                    mPipAppOpsListener.onActivityPinned(taskInfo.topActivity.getPackageName());
+                    mPipUiEventLogger.setTaskInfo(taskInfo);
+                }
                 if (mPipTransitionState.isInSwipePipToHomeTransition()) {
+                    mPipUiEventLogger.log(
+                            PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_AUTO_ENTER);
                     mPipTransitionState.resetSwipePipToHomeState();
+                } else {
+                    mPipUiEventLogger.log(PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_ENTER);
                 }
                 for (Consumer<Boolean> listener : mOnIsInPipStateChangedListeners) {
                     listener.accept(true /* inPip */);
                 }
                 break;
             case PipTransitionState.EXITED_PIP:
+                mPipAppOpsListener.onActivityUnpinned();
+                mPipUiEventLogger.setTaskInfo(null);
                 for (Consumer<Boolean> listener : mOnIsInPipStateChangedListeners) {
                     listener.accept(false /* inPip */);
                 }
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 3729653..9babe9e 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
@@ -43,20 +43,20 @@
 import com.android.wm.shell.animation.FloatProperties;
 import com.android.wm.shell.common.FloatingContentCoordinator;
 import com.android.wm.shell.common.pip.PipAppOpsListener;
-import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
 import com.android.wm.shell.common.pip.PipBoundsState;
 import com.android.wm.shell.common.pip.PipPerfHintController;
 import com.android.wm.shell.common.pip.PipSnapAlgorithm;
+import com.android.wm.shell.common.pip.PipUiEventLogger;
 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 java.util.Optional;
+
 import kotlin.Unit;
 import kotlin.jvm.functions.Function0;
 
-import java.util.Optional;
-
 /**
  * A helper to animate and manipulate the PiP.
  */
@@ -80,12 +80,12 @@
     private static final float DISMISS_CIRCLE_PERCENT = 0.85f;
 
     private final Context mContext;
-    private @NonNull PipBoundsState mPipBoundsState;
-    private @NonNull PipBoundsAlgorithm mPipBoundsAlgorithm;
-    private @NonNull PipScheduler mPipScheduler;
-    private @NonNull PipTransitionState mPipTransitionState;
-    private PhonePipMenuController mMenuController;
-    private PipSnapAlgorithm mSnapAlgorithm;
+    @NonNull private final PipBoundsState mPipBoundsState;
+    @NonNull private final PipScheduler mPipScheduler;
+    @NonNull private final PipTransitionState mPipTransitionState;
+    @NonNull private final PipUiEventLogger mPipUiEventLogger;
+    private final PhonePipMenuController mMenuController;
+    private final PipSnapAlgorithm mSnapAlgorithm;
 
     /** The region that all of PIP must stay within. */
     private final Rect mFloatingAllowedArea = new Rect();
@@ -168,10 +168,9 @@
             PhonePipMenuController menuController, PipSnapAlgorithm snapAlgorithm,
             FloatingContentCoordinator floatingContentCoordinator, PipScheduler pipScheduler,
             Optional<PipPerfHintController> pipPerfHintControllerOptional,
-            PipBoundsAlgorithm pipBoundsAlgorithm, PipTransitionState pipTransitionState) {
+            PipTransitionState pipTransitionState, PipUiEventLogger pipUiEventLogger) {
         mContext = context;
         mPipBoundsState = pipBoundsState;
-        mPipBoundsAlgorithm = pipBoundsAlgorithm;
         mPipScheduler = pipScheduler;
         mMenuController = menuController;
         mSnapAlgorithm = snapAlgorithm;
@@ -185,6 +184,7 @@
         };
         mPipTransitionState = pipTransitionState;
         mPipTransitionState.addPipTransitionStateChangedListener(this);
+        mPipUiEventLogger = pipUiEventLogger;
     }
 
     void init() {
@@ -850,9 +850,11 @@
         if (mPipBoundsState.getBounds().left < 0
                 && mPipBoundsState.getStashedState() != STASH_TYPE_LEFT) {
             mPipBoundsState.setStashed(STASH_TYPE_LEFT);
+            mPipUiEventLogger.log(PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_STASH_LEFT);
         } else if (mPipBoundsState.getBounds().left >= 0
                 && mPipBoundsState.getStashedState() != STASH_TYPE_RIGHT) {
             mPipBoundsState.setStashed(STASH_TYPE_RIGHT);
+            mPipUiEventLogger.log(PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_STASH_RIGHT);
         }
         mMenuController.hideMenu();
     }
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 2e38449..8061ee9 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
@@ -63,6 +63,7 @@
 import com.android.wm.shell.common.pip.PipMenuController;
 import com.android.wm.shell.common.pip.PipUtils;
 import com.android.wm.shell.common.split.SplitScreenUtils;
+import com.android.wm.shell.desktopmode.DesktopUserRepositories;
 import com.android.wm.shell.pip.PipTransitionController;
 import com.android.wm.shell.pip2.animation.PipAlphaAnimator;
 import com.android.wm.shell.pip2.animation.PipEnterAnimator;
@@ -72,6 +73,8 @@
 import com.android.wm.shell.sysui.ShellInit;
 import com.android.wm.shell.transition.Transitions;
 
+import java.util.Optional;
+
 /**
  * Implementation of transitions for PiP on phone.
  */
@@ -106,6 +109,7 @@
     private final PipScheduler mPipScheduler;
     private final PipTransitionState mPipTransitionState;
     private final PipDisplayLayoutState mPipDisplayLayoutState;
+    private final Optional<DesktopUserRepositories> mDesktopUserRepositoriesOptional;
 
     //
     // Transition caches
@@ -140,7 +144,8 @@
             PipScheduler pipScheduler,
             PipTransitionState pipTransitionState,
             PipDisplayLayoutState pipDisplayLayoutState,
-            PipUiStateChangeController pipUiStateChangeController) {
+            PipUiStateChangeController pipUiStateChangeController,
+            Optional<DesktopUserRepositories> desktopUserRepositoriesOptional) {
         super(shellInit, shellTaskOrganizer, transitions, pipBoundsState, pipMenuController,
                 pipBoundsAlgorithm);
 
@@ -151,6 +156,7 @@
         mPipTransitionState = pipTransitionState;
         mPipTransitionState.addPipTransitionStateChangedListener(this);
         mPipDisplayLayoutState = pipDisplayLayoutState;
+        mDesktopUserRepositoriesOptional = desktopUserRepositoriesOptional;
     }
 
     @Override
@@ -820,6 +826,17 @@
             return false;
         }
 
+
+        // Since opening a new task while in Desktop Mode always first open in Fullscreen
+        // until DesktopMode Shell code resolves it to Freeform, PipTransition will get a
+        // possibility to handle it also. In this case return false to not have it enter PiP.
+        final boolean isInDesktopSession = !mDesktopUserRepositoriesOptional.isEmpty()
+                && mDesktopUserRepositoriesOptional.get().getCurrent().getVisibleTaskCount(
+                pipTask.displayId) > 0;
+        if (isInDesktopSession) {
+            return false;
+        }
+
         // Assuming auto-enter is enabled and pipTask is non-null, the TRANSIT_OPEN request type
         // implies that we are entering PiP in button navigation mode. This is guaranteed by
         // TaskFragment#startPausing()` in Core which wouldn't get called in gesture nav.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentsAnimationController.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentsAnimationController.aidl
index 964e5fd..af1679f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentsAnimationController.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentsAnimationController.aidl
@@ -36,12 +36,6 @@
 interface IRecentsAnimationController {
 
     /**
-     * Takes a screenshot of the task associated with the given {@param taskId}. Only valid for the
-     * current set of task ids provided to the handler.
-     */
-    TaskSnapshot screenshotTask(int taskId);
-
-    /**
      * Sets the final surface transaction on a Task. This is used by Launcher to notify the system
      * that animating Activity to PiP has completed and the associated task surface should be
      * updated accordingly. This should be called before `finish`
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 032dac9..76496b0 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
@@ -1227,19 +1227,6 @@
         }
 
         @Override
-        public TaskSnapshot screenshotTask(int taskId) {
-            try {
-                ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION,
-                        "[%d] RecentsController.screenshotTask: taskId=%d", mInstanceId, taskId);
-                return ActivityTaskManager.getService().takeTaskSnapshot(taskId,
-                        true /* updateCache */);
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Failed to screenshot task", e);
-            }
-            return null;
-        }
-
-        @Override
         public void setInputConsumerEnabled(boolean enabled) {
             mExecutor.execute(() -> {
                 if (mFinishCB == null || !enabled) {
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 39ed206..c724135 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
@@ -185,7 +185,6 @@
     private final LauncherApps mLauncherApps;
     private final RootTaskDisplayAreaOrganizer mRootTDAOrganizer;
     private final ShellExecutor mMainExecutor;
-    private final ShellExecutor mBgExecutor;
     private final Handler mMainHandler;
     private final SplitScreenImpl mImpl = new SplitScreenImpl();
     private final DisplayController mDisplayController;
@@ -232,8 +231,7 @@
             MultiInstanceHelper multiInstanceHelper,
             SplitState splitState,
             ShellExecutor mainExecutor,
-            Handler mainHandler,
-            ShellExecutor bgExecutor) {
+            Handler mainHandler) {
         mShellCommandHandler = shellCommandHandler;
         mShellController = shellController;
         mTaskOrganizer = shellTaskOrganizer;
@@ -243,7 +241,6 @@
         mRootTDAOrganizer = rootTDAOrganizer;
         mMainExecutor = mainExecutor;
         mMainHandler = mainHandler;
-        mBgExecutor = bgExecutor;
         mDisplayController = displayController;
         mDisplayImeController = displayImeController;
         mDisplayInsetsController = displayInsetsController;
@@ -301,9 +298,8 @@
         return new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,
                 mTaskOrganizer, mDisplayController, mDisplayImeController,
                 mDisplayInsetsController, mTransitions, mTransactionPool, mIconProvider,
-                mMainExecutor, mMainHandler, mBgExecutor, mRecentTasksOptional,
-                mLaunchAdjacentController, mWindowDecorViewModel, mSplitState,
-                mDesktopTasksController);
+                mMainExecutor, mMainHandler, mRecentTasksOptional, mLaunchAdjacentController,
+                mWindowDecorViewModel, mSplitState, mDesktopTasksController);
     }
 
     @Override
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 535112f..5aa3291 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -35,7 +35,9 @@
 import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER;
 
 import static com.android.wm.shell.Flags.enableFlexibleSplit;
+import static com.android.wm.shell.Flags.enableFlexibleTwoAppSplit;
 import static com.android.wm.shell.common.split.SplitLayout.PARALLAX_ALIGN_CENTER;
+import static com.android.wm.shell.common.split.SplitLayout.PARALLAX_FLEX;
 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;
@@ -127,7 +129,6 @@
 import com.android.internal.policy.FoldLockSettingsObserver;
 import com.android.internal.protolog.ProtoLog;
 import com.android.launcher3.icons.IconProvider;
-import com.android.wm.shell.Flags;
 import com.android.wm.shell.R;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.DisplayController;
@@ -216,7 +217,6 @@
     private final SplitscreenEventLogger mLogger;
     private final ShellExecutor mMainExecutor;
     private final Handler mMainHandler;
-    private final ShellExecutor mBgExecutor;
     // Cache live tile tasks while entering recents, evict them from stages in finish transaction
     // if user is opening another task(s).
     private final ArrayList<Integer> mPausingTasks = new ArrayList<>();
@@ -345,20 +345,12 @@
                 }
             };
 
-    protected StageCoordinator(Context context,
-            int displayId,
-            SyncTransactionQueue syncQueue,
-            ShellTaskOrganizer taskOrganizer,
-            DisplayController displayController,
+    protected StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
+            ShellTaskOrganizer taskOrganizer, DisplayController displayController,
             DisplayImeController displayImeController,
-            DisplayInsetsController displayInsetsController,
-            Transitions transitions,
-            TransactionPool transactionPool,
-            IconProvider iconProvider,
-            ShellExecutor mainExecutor,
-            Handler mainHandler,
-            ShellExecutor bgExecutor,
-            Optional<RecentTasksController> recentTasks,
+            DisplayInsetsController displayInsetsController, Transitions transitions,
+            TransactionPool transactionPool, IconProvider iconProvider, ShellExecutor mainExecutor,
+            Handler mainHandler, Optional<RecentTasksController> recentTasks,
             LaunchAdjacentController launchAdjacentController,
             Optional<WindowDecorViewModel> windowDecorViewModel, SplitState splitState,
             Optional<DesktopTasksController> desktopTasksController) {
@@ -369,7 +361,6 @@
         mLogger = new SplitscreenEventLogger();
         mMainExecutor = mainExecutor;
         mMainHandler = mainHandler;
-        mBgExecutor = bgExecutor;
         mRecentTasks = recentTasks;
         mLaunchAdjacentController = launchAdjacentController;
         mWindowDecorViewModel = windowDecorViewModel;
@@ -386,8 +377,6 @@
                     this /*stageListenerCallbacks*/,
                     mSyncQueue,
                     iconProvider,
-                    mMainExecutor,
-                    mBgExecutor,
                     mWindowDecorViewModel);
         } else {
             mMainStage = new StageTaskListener(
@@ -397,8 +386,6 @@
                     this /*stageListenerCallbacks*/,
                     mSyncQueue,
                     iconProvider,
-                    mMainExecutor,
-                    mBgExecutor,
                     mWindowDecorViewModel, STAGE_TYPE_MAIN);
             mSideStage = new StageTaskListener(
                     mContext,
@@ -407,8 +394,6 @@
                     this /*stageListenerCallbacks*/,
                     mSyncQueue,
                     iconProvider,
-                    mMainExecutor,
-                    mBgExecutor,
                     mWindowDecorViewModel, STAGE_TYPE_SIDE);
         }
         mDisplayController = displayController;
@@ -430,22 +415,13 @@
     }
 
     @VisibleForTesting
-    StageCoordinator(Context context,
-            int displayId,
-            SyncTransactionQueue syncQueue,
-            ShellTaskOrganizer taskOrganizer,
-            StageTaskListener mainStage,
-            StageTaskListener sideStage,
-            DisplayController displayController,
+    StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
+            ShellTaskOrganizer taskOrganizer, StageTaskListener mainStage,
+            StageTaskListener sideStage, DisplayController displayController,
             DisplayImeController displayImeController,
-            DisplayInsetsController displayInsetsController,
-            SplitLayout splitLayout,
-            Transitions transitions,
-            TransactionPool transactionPool,
-            ShellExecutor mainExecutor,
-            Handler mainHandler,
-            ShellExecutor bgExecutor,
-            Optional<RecentTasksController> recentTasks,
+            DisplayInsetsController displayInsetsController, SplitLayout splitLayout,
+            Transitions transitions, TransactionPool transactionPool, ShellExecutor mainExecutor,
+            Handler mainHandler, Optional<RecentTasksController> recentTasks,
             LaunchAdjacentController launchAdjacentController,
             Optional<WindowDecorViewModel> windowDecorViewModel, SplitState splitState,
             Optional<DesktopTasksController> desktopTasksController) {
@@ -465,7 +441,6 @@
         mLogger = new SplitscreenEventLogger();
         mMainExecutor = mainExecutor;
         mMainHandler = mainHandler;
-        mBgExecutor = bgExecutor;
         mRecentTasks = recentTasks;
         mLaunchAdjacentController = launchAdjacentController;
         mWindowDecorViewModel = windowDecorViewModel;
@@ -2007,7 +1982,7 @@
             // If all stages are filled, create new SplitBounds and update Recents.
             if (mainStageTopTaskId != INVALID_TASK_ID && sideStageTopTaskId != INVALID_TASK_ID) {
                 int currentSnapPosition = mSplitLayout.calculateCurrentSnapPosition();
-                if (Flags.enableFlexibleTwoAppSplit()) {
+                if (enableFlexibleTwoAppSplit()) {
                     // Split screen can be laid out in such a way that some of the apps are
                     // offscreen. For the purposes of passing SplitBounds up to launcher (for use in
                     // thumbnails etc.), we crop the bounds down to the screen size.
@@ -2064,10 +2039,11 @@
         mRootTaskLeash = leash;
 
         if (mSplitLayout == null) {
+            int parallaxType = enableFlexibleTwoAppSplit() ? PARALLAX_FLEX : PARALLAX_ALIGN_CENTER;
             mSplitLayout = new SplitLayout(TAG + "SplitDivider", mContext,
                     mRootTaskInfo.configuration, this, mParentContainerCallbacks,
-                    mDisplayController, mDisplayImeController, mTaskOrganizer,
-                    PARALLAX_ALIGN_CENTER /* parallaxType */, mSplitState, mMainHandler);
+                    mDisplayController, mDisplayImeController, mTaskOrganizer, parallaxType,
+                    mSplitState, mMainHandler);
             mDisplayInsetsController.addInsetsChangedListener(mDisplayId, mSplitLayout);
         }
 
@@ -2190,7 +2166,8 @@
         wct.setForceTranslucent(mRootTaskInfo.token, translucent);
     }
 
-    /** Callback when split roots visiblility changed. */
+    /** Callback when split roots visiblility changed.
+     * NOTICE: This only be called on legacy transition. */
     @Override
     public void onStageVisibilityChanged(StageTaskListener stageListener) {
         // If split didn't active, just ignore this callback because we should already did these
@@ -2407,6 +2384,8 @@
         updateSurfaceBounds(layout, t, shouldUseParallaxEffect);
         getMainStageBounds(mTempRect1);
         getSideStageBounds(mTempRect2);
+        Rect displayBounds = mSplitLayout.getRootBounds();
+
         if (enableFlexibleSplit()) {
             StageTaskListener ltStage =
                     mStageOrderOperator.getStageForLegacyPosition(SPLIT_POSITION_TOP_OR_LEFT,
@@ -2414,12 +2393,14 @@
             StageTaskListener brStage =
                     mStageOrderOperator.getStageForLegacyPosition(SPLIT_POSITION_BOTTOM_OR_RIGHT,
                             false /*checkAllStagesIfNotActive*/);
-            ltStage.onResizing(mTempRect1, mTempRect2, t, offsetX, offsetY, mShowDecorImmediately);
-            brStage.onResizing(mTempRect2, mTempRect1, t, offsetX, offsetY, mShowDecorImmediately);
-        } else {
-            mMainStage.onResizing(mTempRect1, mTempRect2, t, offsetX, offsetY,
+            ltStage.onResizing(mTempRect1, mTempRect2, displayBounds, t, offsetX, offsetY,
                     mShowDecorImmediately);
-            mSideStage.onResizing(mTempRect2, mTempRect1, t, offsetX, offsetY,
+            brStage.onResizing(mTempRect2, mTempRect1, displayBounds, t, offsetX, offsetY,
+                    mShowDecorImmediately);
+        } else {
+            mMainStage.onResizing(mTempRect1, mTempRect2, displayBounds, t, offsetX, offsetY,
+                    mShowDecorImmediately);
+            mSideStage.onResizing(mTempRect2, mTempRect1, displayBounds, t, offsetX, offsetY,
                     mShowDecorImmediately);
         }
         t.apply();
@@ -2466,7 +2447,7 @@
             mSplitLayout.populateTouchZones();
         }, mainDecor, sideDecor, decorManagers);
 
-        if (Flags.enableFlexibleTwoAppSplit()) {
+        if (enableFlexibleTwoAppSplit()) {
             switch (layout.calculateCurrentSnapPosition()) {
                 case SNAP_TO_2_10_90 -> grantFocusToPosition(false /* leftOrTop */);
                 case SNAP_TO_2_90_10 -> grantFocusToPosition(true /* leftOrTop */);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageOrderOperator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageOrderOperator.kt
index 5256e78..a921004 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageOrderOperator.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageOrderOperator.kt
@@ -20,7 +20,6 @@
 import com.android.internal.protolog.ProtoLog
 import com.android.launcher3.icons.IconProvider
 import com.android.wm.shell.ShellTaskOrganizer
-import com.android.wm.shell.common.ShellExecutor
 import com.android.wm.shell.common.SyncTransactionQueue
 import com.android.wm.shell.protolog.ShellProtoLogGroup
 import com.android.wm.shell.shared.split.SplitScreenConstants
@@ -53,8 +52,6 @@
         stageCallbacks: StageTaskListener.StageListenerCallbacks,
         syncQueue: SyncTransactionQueue,
         iconProvider: IconProvider,
-        mainExecutor: ShellExecutor,
-        bgExecutor: ShellExecutor,
         windowDecorViewModel: Optional<WindowDecorViewModel>
     ) {
 
@@ -86,8 +83,6 @@
                 stageCallbacks,
                 syncQueue,
                 iconProvider,
-                mainExecutor,
-                bgExecutor,
                 windowDecorViewModel,
                 stageIds[i])
             )
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 816f51f..021f659 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
@@ -48,7 +48,6 @@
 import com.android.internal.util.ArrayUtils;
 import com.android.launcher3.icons.IconProvider;
 import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SurfaceUtils;
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.common.split.SplitDecorManager;
@@ -96,8 +95,6 @@
     private final StageListenerCallbacks mCallbacks;
     private final SyncTransactionQueue mSyncQueue;
     private final IconProvider mIconProvider;
-    private final ShellExecutor mMainExecutor;
-    private final ShellExecutor mBgExecutor;
     private final Optional<WindowDecorViewModel> mWindowDecorViewModel;
 
     /** Whether or not the root task has been created. */
@@ -114,21 +111,14 @@
     // TODO(b/204308910): Extracts SplitDecorManager related code to common package.
     private SplitDecorManager mSplitDecorManager;
 
-    StageTaskListener(Context context,
-            ShellTaskOrganizer taskOrganizer,
-            int displayId,
-            StageListenerCallbacks callbacks,
-            SyncTransactionQueue syncQueue,
+    StageTaskListener(Context context, ShellTaskOrganizer taskOrganizer, int displayId,
+            StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
             IconProvider iconProvider,
-            ShellExecutor mainExecutor,
-            ShellExecutor bgExecutor,
             Optional<WindowDecorViewModel> windowDecorViewModel, int id) {
         mContext = context;
         mCallbacks = callbacks;
         mSyncQueue = syncQueue;
         mIconProvider = iconProvider;
-        mMainExecutor = mainExecutor;
-        mBgExecutor = bgExecutor;
         mWindowDecorViewModel = windowDecorViewModel;
         taskOrganizer.createRootTask(displayId, WINDOWING_MODE_MULTI_WINDOW, this);
         mId = id;
@@ -224,8 +214,9 @@
         if (mRootTaskInfo == null) {
             mRootLeash = leash;
             mRootTaskInfo = taskInfo;
-            mSplitDecorManager = new SplitDecorManager(mRootTaskInfo.configuration, mIconProvider,
-                    mMainExecutor, mBgExecutor);
+            mSplitDecorManager = new SplitDecorManager(
+                    mRootTaskInfo.configuration,
+                    mIconProvider);
             mHasRootTask = true;
             mCallbacks.onRootTaskAppeared();
             if (mVisible != mRootTaskInfo.isVisible) {
@@ -249,20 +240,12 @@
     @Override
     @CallSuper
     public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
-        ProtoLog.d(WM_SHELL_SPLIT_SCREEN,
-                "onTaskInfoChanged: taskId=%d vis=%b reqVis=%b baseAct=%s stageId=%s",
-                taskInfo.taskId, taskInfo.isVisible, taskInfo.isVisibleRequested,
-                taskInfo.baseActivity, stageTypeToString(mId));
+        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTaskInfoChanged: taskId=%d taskAct=%s "
+                        + "stageId=%s",
+                taskInfo.taskId, taskInfo.baseActivity, stageTypeToString(mId));
         mWindowDecorViewModel.ifPresent(viewModel -> viewModel.onTaskInfoChanged(taskInfo));
         if (mRootTaskInfo.taskId == taskInfo.taskId) {
             mRootTaskInfo = taskInfo;
-            boolean isVisible = taskInfo.isVisible && taskInfo.isVisibleRequested;
-            if (mVisible != isVisible) {
-                ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTaskInfoChanged: currentVis=%b newVis=%b",
-                        mVisible, isVisible);
-                mVisible = isVisible;
-                mCallbacks.onStageVisibilityChanged(this);
-            }
         } else if (taskInfo.parentTaskId == mRootTaskInfo.taskId) {
             if (!taskInfo.supportsMultiWindow
                     || !ArrayUtils.contains(CONTROLLED_ACTIVITY_TYPES, taskInfo.getActivityType())
@@ -339,11 +322,11 @@
         return mRootTaskInfo != null && mRootTaskInfo.taskId == taskId;
     }
 
-    void onResizing(Rect newBounds, Rect sideBounds, SurfaceControl.Transaction t, int offsetX,
-            int offsetY, boolean immediately) {
+    void onResizing(Rect newBounds, Rect sideBounds, Rect displayBounds,
+            SurfaceControl.Transaction t, int offsetX, int offsetY, boolean immediately) {
         if (mSplitDecorManager != null && mRootTaskInfo != null) {
-            mSplitDecorManager.onResizing(mRootTaskInfo, newBounds, sideBounds, t, offsetX,
-                    offsetY, immediately);
+            mSplitDecorManager.onResizing(mRootTaskInfo, newBounds, sideBounds, displayBounds, t,
+                    offsetX, offsetY, immediately);
         }
     }
 
@@ -353,6 +336,12 @@
         }
     }
 
+    void screenshotIfNeeded(SurfaceControl.Transaction t) {
+        if (mSplitDecorManager != null) {
+            mSplitDecorManager.screenshotIfNeeded(t);
+        }
+    }
+
     void fadeOutDecor(Runnable finishedCallback) {
         if (mSplitDecorManager != null) {
             mSplitDecorManager.fadeOutDecor(finishedCallback, false /* addDelay */);
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 ea755306..c5e158c 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
@@ -53,7 +53,6 @@
     private final SyncTransactionQueue mSyncQueue;
     private final Context mContext;
     private final ShellExecutor mMainExecutor;
-    private final ShellExecutor mBgExecutor;
     private final DisplayController mDisplayController;
     private final DisplayImeController mDisplayImeController;
     private final DisplayInsetsController mDisplayInsetsController;
@@ -86,20 +85,18 @@
             SplitState splitState,
             ShellExecutor mainExecutor,
             Handler mainHandler,
-            ShellExecutor bgExecutor,
             SystemWindows systemWindows) {
         super(context, shellInit, shellCommandHandler, shellController, shellTaskOrganizer,
                 syncQueue, rootTDAOrganizer, displayController, displayImeController,
                 displayInsetsController, null, transitions, transactionPool,
                 iconProvider, recentTasks, launchAdjacentController, Optional.empty(),
                 Optional.empty(), null /* stageCoordinator */, multiInstanceHelper, splitState,
-                mainExecutor, mainHandler, bgExecutor);
+                mainExecutor, mainHandler);
+
         mTaskOrganizer = shellTaskOrganizer;
         mSyncQueue = syncQueue;
         mContext = context;
         mMainExecutor = mainExecutor;
-        mMainHandler = mainHandler;
-        mBgExecutor = bgExecutor;
         mDisplayController = displayController;
         mDisplayImeController = displayImeController;
         mDisplayInsetsController = displayInsetsController;
@@ -109,6 +106,8 @@
         mRecentTasksOptional = recentTasks;
         mLaunchAdjacentController = launchAdjacentController;
         mSplitState = splitState;
+
+        mMainHandler = mainHandler;
         mSystemWindows = systemWindows;
     }
 
@@ -121,7 +120,7 @@
         return new TvStageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,
                 mTaskOrganizer, mDisplayController, mDisplayImeController,
                 mDisplayInsetsController, mTransitions, mTransactionPool,
-                mIconProvider, mMainExecutor, mMainHandler, mBgExecutor,
+                mIconProvider, mMainExecutor, mMainHandler,
                 mRecentTasksOptional, mLaunchAdjacentController, mSplitState, mSystemWindows);
     }
 
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 9d85bea..e1bf12f 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
@@ -51,14 +51,14 @@
             DisplayInsetsController displayInsetsController, Transitions transitions,
             TransactionPool transactionPool,
             IconProvider iconProvider, ShellExecutor mainExecutor,
-            Handler mainHandler, ShellExecutor bgExecutor,
+            Handler mainHandler,
             Optional<RecentTasksController> recentTasks,
             LaunchAdjacentController launchAdjacentController,
             SplitState splitState,
             SystemWindows systemWindows) {
         super(context, displayId, syncQueue, taskOrganizer, displayController, displayImeController,
                 displayInsetsController, transitions, transactionPool, iconProvider,
-                mainExecutor, mainHandler, bgExecutor, recentTasks, launchAdjacentController,
+                mainExecutor, mainHandler, recentTasks, launchAdjacentController,
                 Optional.empty(), splitState, Optional.empty());
 
         mTvSplitMenuController = new TvSplitMenuController(context, this,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultSurfaceAnimator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultSurfaceAnimator.java
index d8884f6..f5aaaad 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultSurfaceAnimator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultSurfaceAnimator.java
@@ -33,6 +33,7 @@
 import com.android.wm.shell.shared.TransactionPool;
 
 import java.util.ArrayList;
+import java.util.function.Consumer;
 
 public class DefaultSurfaceAnimator {
 
@@ -58,42 +59,12 @@
         // Animation length is already expected to be scaled.
         va.overrideDurationScale(1.0f);
         va.setDuration(anim.computeDurationHint());
-        va.addUpdateListener(updateListener);
-        va.addListener(new AnimatorListenerAdapter() {
-            // It is possible for the end/cancel to be called more than once, which may cause
-            // issues if the animating surface has already been released. Track the finished
-            // state here to skip duplicate callbacks. See b/252872225.
-            private boolean mFinished;
-
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                onFinish();
-            }
-
-            @Override
-            public void onAnimationCancel(Animator animation) {
-                onFinish();
-            }
-
-            private void onFinish() {
-                if (mFinished) return;
-                mFinished = true;
-                // Apply transformation of end state in case the animation is canceled.
-                if (va.getAnimatedFraction() < 1f) {
-                    va.setCurrentFraction(1f);
-                }
-
-                pool.release(transaction);
-                mainExecutor.execute(() -> {
-                    animations.remove(va);
-                    finishCallback.run();
-                });
-                // The update listener can continue to be called after the animation has ended if
-                // end() is called manually again before the finisher removes the animation.
-                // Remove it manually here to prevent animating a released surface.
-                // See b/252872225.
-                va.removeUpdateListener(updateListener);
-            }
+        setupValueAnimator(va, updateListener, (vanim) -> {
+            pool.release(transaction);
+            mainExecutor.execute(() -> {
+                animations.remove(vanim);
+                finishCallback.run();
+            });
         });
         animations.add(va);
     }
@@ -188,4 +159,50 @@
             }
         }
     }
+
+    /**
+     * Setup some callback logic on a value-animator. This helper ensures that a value animator
+     * finishes at its final fraction (1f) and that relevant callbacks are only called once.
+     */
+    public static ValueAnimator setupValueAnimator(ValueAnimator animator,
+            ValueAnimator.AnimatorUpdateListener updateListener,
+            Consumer<ValueAnimator> afterFinish) {
+        animator.addUpdateListener(updateListener);
+        animator.addListener(new AnimatorListenerAdapter() {
+            // It is possible for the end/cancel to be called more than once, which may cause
+            // issues if the animating surface has already been released. Track the finished
+            // state here to skip duplicate callbacks. See b/252872225.
+            private boolean mFinished;
+
+            @Override
+            public void onAnimationStart(Animator animation) {
+            }
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                onFinish();
+            }
+
+            @Override
+            public void onAnimationCancel(Animator animation) {
+                onFinish();
+            }
+
+            private void onFinish() {
+                if (mFinished) return;
+                mFinished = true;
+                // Apply transformation of end state in case the animation is canceled.
+                if (animator.getAnimatedFraction() < 1f) {
+                    animator.setCurrentFraction(1f);
+                }
+                afterFinish.accept(animator);
+                // The update listener can continue to be called after the animation has ended if
+                // end() is called manually again before the finisher removes the animation.
+                // Remove it manually here to prevent animating a released surface.
+                // See b/252872225.
+                animator.removeUpdateListener(updateListener);
+            }
+        });
+        return animator;
+    }
 }
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 1689bb5..36c3e97 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
@@ -55,6 +55,7 @@
 import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
 import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
 
+import static com.android.internal.policy.TransitionAnimation.DEFAULT_APP_TRANSITION_DURATION;
 import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_CHANGE;
 import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_CLOSE;
 import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_INTRA_CLOSE;
@@ -69,6 +70,7 @@
 import static com.android.wm.shell.transition.TransitionAnimationHelper.loadAttributeAnimation;
 
 import android.animation.Animator;
+import android.animation.ValueAnimator;
 import android.annotation.ColorInt;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -104,6 +106,7 @@
 import com.android.internal.protolog.ProtoLog;
 import com.android.window.flags.Flags;
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
+import com.android.wm.shell.animation.SizeChangeAnimation;
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.DisplayLayout;
 import com.android.wm.shell.common.ShellExecutor;
@@ -422,6 +425,14 @@
                             ROTATION_ANIMATION_ROTATE, 0 /* flags */, animations, onAnimFinish);
                     continue;
                 }
+
+                if (Flags.portWindowSizeAnimation() && isTask
+                        && TransitionInfo.isIndependent(change, info)
+                        && change.getSnapshot() != null) {
+                    startBoundsChangeAnimation(startTransaction, animations, change, onAnimFinish,
+                            mMainExecutor);
+                    continue;
+                }
             }
 
             // Hide the invisible surface directly without animating it if there is a display
@@ -734,6 +745,21 @@
         }
     }
 
+    private void startBoundsChangeAnimation(@NonNull SurfaceControl.Transaction startT,
+            @NonNull ArrayList<Animator> animations, @NonNull TransitionInfo.Change change,
+            @NonNull Runnable finishCb, @NonNull ShellExecutor mainExecutor) {
+        final SizeChangeAnimation sca =
+                new SizeChangeAnimation(change.getStartAbsBounds(), change.getEndAbsBounds());
+        sca.initialize(change.getLeash(), change.getSnapshot(), startT);
+        final ValueAnimator va = sca.buildAnimator(change.getLeash(), change.getSnapshot(),
+                (animator) -> mainExecutor.execute(() -> {
+                    animations.remove(animator);
+                    finishCb.run();
+                }));
+        va.setDuration(DEFAULT_APP_TRANSITION_DURATION);
+        animations.add(va);
+    }
+
     @Nullable
     @Override
     public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
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 b5220c4..1917996 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,7 +30,6 @@
 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;
@@ -73,19 +72,12 @@
             final int mode = change.getMode();
             final boolean isBackGesture = change.hasFlags(FLAG_BACK_GESTURE_ANIMATED);
             if (taskInfo.getActivityType() == ACTIVITY_TYPE_HOME) {
-                if (Flags.migratePredictiveBackTransition()) {
-                    final boolean gestureToHomeTransition = isBackGesture
-                            && TransitionUtil.isClosingType(info.getType());
-                    if (gestureToHomeTransition || TransitionUtil.isClosingMode(mode)
-                            || (!isBackGesture && TransitionUtil.isOpeningMode(mode))) {
-                        notifyHomeVisibilityChanged(gestureToHomeTransition
-                                || TransitionUtil.isOpeningType(mode));
-                    }
-                } else {
-                    if (TransitionUtil.isOpenOrCloseMode(mode) || isBackGesture) {
-                        notifyHomeVisibilityChanged(TransitionUtil.isOpeningType(mode)
-                                || isBackGesture);
-                    }
+                final boolean gestureToHomeTransition = isBackGesture
+                        && TransitionUtil.isClosingType(info.getType());
+                if (gestureToHomeTransition || TransitionUtil.isClosingMode(mode)
+                        || (!isBackGesture && TransitionUtil.isOpeningMode(mode))) {
+                    notifyHomeVisibilityChanged(gestureToHomeTransition
+                            || TransitionUtil.isOpeningType(mode));
                 }
             }
         }
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 edb2e1c..d5929f0 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
@@ -30,7 +30,6 @@
 import static android.view.WindowManager.TRANSIT_TO_FRONT;
 import static android.view.WindowManager.fixScale;
 import static android.window.TransitionInfo.FLAGS_IS_NON_APP_WINDOW;
-import static android.window.TransitionInfo.FLAG_BACK_GESTURE_ANIMATED;
 import static android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY;
 import static android.window.TransitionInfo.FLAG_IS_BEHIND_STARTING_WINDOW;
 import static android.window.TransitionInfo.FLAG_IS_OCCLUDED;
@@ -40,7 +39,6 @@
 
 import static com.android.systemui.shared.Flags.returnAnimationFrameworkLongLived;
 import static com.android.window.flags.Flags.ensureWallpaperInTransitions;
-import static com.android.window.flags.Flags.migratePredictiveBackTransition;
 import static com.android.wm.shell.shared.TransitionUtil.isClosingType;
 import static com.android.wm.shell.shared.TransitionUtil.isOpeningType;
 
@@ -848,12 +846,6 @@
                 info.getChanges().remove(i);
                 continue;
             }
-            // The change has already animated by back gesture, don't need to play transition
-            // animation on it.
-            if (!migratePredictiveBackTransition()
-                    && change.hasFlags(FLAG_BACK_GESTURE_ANIMATED)) {
-                info.getChanges().remove(i);
-            }
         }
         // There does not need animation when:
         // A. Transfer starting window. Apply transfer starting window directly if there is no other
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 046cb20..9fbda46 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
@@ -795,6 +795,8 @@
         }
         decoration.closeHandleMenu();
         mDesktopTasksController.openNewWindow(decoration.mTaskInfo);
+        mDesktopModeUiEventLogger.log(decoration.mTaskInfo,
+                DesktopUiEventEnum.DESKTOP_WINDOW_MULTI_INSTANCE_NEW_WINDOW_CLICK);
     }
 
     private void onManageWindows(DesktopModeWindowDecoration decoration) {
@@ -811,6 +813,9 @@
                         decoration.closeManageWindowsMenu();
                         mDesktopTasksController.openInstance(decoration.mTaskInfo,
                                 requestedTaskId);
+                        mDesktopModeUiEventLogger.log(decoration.mTaskInfo,
+                                DesktopUiEventEnum
+                                        .DESKTOP_WINDOW_MULTI_INSTANCE_MANAGE_WINDOWS_ICON_CLICK);
                         return Unit.INSTANCE;
                     }
                 )
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 3f65d93..1264c01 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
@@ -231,6 +231,7 @@
     fun disposeStatusBarInputLayer() {
         if (!statusBarInputLayerExists) return
         statusBarInputLayerExists = false
+        statusBarInputLayer?.view?.setOnTouchListener(null)
         handler.post {
             statusBarInputLayer?.releaseView()
             statusBarInputLayer = null
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/EnterDesktopViaStaticDesktopOverviewTaskTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/EnterDesktopViaStaticDesktopOverviewTaskTest.kt
new file mode 100644
index 0000000..8d3c39f
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/EnterDesktopViaStaticDesktopOverviewTaskTest.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.wm.shell.functional
+
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.EnterDesktopViaStaticDesktopOverviewTask
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/* Functional test for [EnterDesktopViaStaticDesktopOverviewTask]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class EnterDesktopViaStaticDesktopOverviewTaskTest : EnterDesktopViaStaticDesktopOverviewTask()
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopViaStaticDesktopOverviewTask.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopViaStaticDesktopOverviewTask.kt
new file mode 100644
index 0000000..cbe672c
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopViaStaticDesktopOverviewTask.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.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.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Base Test Class")
+abstract class EnterDesktopViaStaticDesktopOverviewTask constructor() {
+
+    private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+    private val tapl = LauncherInstrumentation()
+    private val wmHelper = WindowManagerStateHelper(instrumentation)
+    private val device = UiDevice.getInstance(instrumentation)
+    private val desktopApp = DesktopModeAppHelper(SimpleAppHelper(instrumentation))
+
+    @Rule
+    @JvmField
+    val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, Rotation.ROTATION_0)
+
+    @Before
+    fun setup() {
+        Assume.assumeTrue(Flags.enableDesktopWindowingMode() && tapl.isTablet)
+        // Clear all tasks
+        val overview = tapl.goHome().switchToOverview()
+        if (overview.hasTasks()) {
+            overview.dismissAllTasks()
+        }
+        desktopApp.enterDesktopMode(wmHelper, device)
+        tapl.goHome().switchToOverview()
+    }
+
+    @Test
+    open fun enterDesktopViaStaticDesktopOverviewTask() {
+        tapl.overview.getCurrentTask().open()
+    }
+
+    @After
+    fun teardown() {
+        desktopApp.exit(wmHelper)
+    }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/Android.bp b/libs/WindowManager/Shell/tests/flicker/pip/Android.bp
index 6e0dcdb..b8ec1d7 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/Android.bp
+++ b/libs/WindowManager/Shell/tests/flicker/pip/Android.bp
@@ -75,8 +75,8 @@
     ],
     static_libs: ["WMShellFlickerTestsBase"],
     test_suites: [
-        "device-tests",
         "csuite",
+        "device-tests",
     ],
     data: ["trace_config/*"],
 }
@@ -117,9 +117,13 @@
         "com.android.wm.shell.flicker.pip.ShowPipAndRotateDisplay",
         "com.android.wm.shell.flicker.pip.nonmatchparent.BottomHalfAutoEnterPipOnGoToHomeTest",
         "com.android.wm.shell.flicker.pip.nonmatchparent.BottomHalfEnterPipOnUserLeaveHintTest",
+        "com.android.wm.shell.flicker.pip.nonmatchparent.BottomHalfEnterPipToOtherOrientation",
         "com.android.wm.shell.flicker.pip.nonmatchparent.BottomHalfEnterPipViaAppUiButtonTest",
         "com.android.wm.shell.flicker.pip.nonmatchparent.BottomHalfExitPipToAppViaExpandButtonTest",
         "com.android.wm.shell.flicker.pip.nonmatchparent.BottomHalfExitPipToAppViaIntentTest",
+        "com.android.wm.shell.flicker.pip.nonmatchparent.BottomHalfSetRequestedOrientationWhilePinned",
+        "com.android.wm.shell.flicker.pip.nonmatchparent.BottomHalfFromSplitScreenAutoEnterPipOnGoToHomeTest",
+        "com.android.wm.shell.flicker.pip.nonmatchparent.BottomHalfFromSplitScreenEnterPipOnUserLeaveHintTest",
     ],
     test_suites: ["device-tests"],
 }
@@ -308,5 +312,33 @@
     test_suites: ["device-tests"],
 }
 
+test_module_config {
+    name: "WMShellFlickerTestsPip-BottomHalfEnterPipToOtherOrientation",
+    base: "WMShellFlickerTestsPip",
+    include_filters: ["com.android.wm.shell.flicker.pip.nonmatchparent.BottomHalfEnterPipToOtherOrientation"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "WMShellFlickerTestsPip-BottomHalfSetRequestedOrientationWhilePinned",
+    base: "WMShellFlickerTestsPip",
+    include_filters: ["com.android.wm.shell.flicker.pip.nonmatchparent.BottomHalfSetRequestedOrientationWhilePinned"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "WMShellFlickerTestsPip-BottomHalfFromSplitScreenAutoEnterPipOnGoToHomeTest",
+    base: "WMShellFlickerTestsPip",
+    include_filters: ["com.android.wm.shell.flicker.pip.nonmatchparent.BottomHalfFromSplitScreenAutoEnterPipOnGoToHomeTest"],
+    test_suites: ["device-tests"],
+}
+
+test_module_config {
+    name: "WMShellFlickerTestsPip-BottomHalfFromSplitScreenEnterPipOnUserLeaveHintTest",
+    base: "WMShellFlickerTestsPip",
+    include_filters: ["com.android.wm.shell.flicker.pip.nonmatchparent.BottomHalfFromSplitScreenEnterPipOnUserLeaveHintTest"],
+    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 84d53d5..597674e 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
@@ -114,7 +114,7 @@
     /** Checks that [pipApp] window is animated towards default position in right bottom corner */
     @FlakyTest(bugId = 255578530)
     @Test
-    fun pipLayerMovesTowardsRightBottomCorner() {
+    open fun pipLayerMovesTowardsRightBottomCorner() {
         // in gestural nav the swipe makes PiP first go upwards
         Assume.assumeFalse(flicker.scenario.isGesturalNavigation)
         flicker.assertLayers {
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt
index 49efd1d..a6f8150 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/EnterPipToOtherOrientation.kt
@@ -16,7 +16,6 @@
 
 package com.android.wm.shell.flicker.pip
 
-import android.app.Activity
 import android.platform.test.annotations.Postsubmit
 import android.platform.test.annotations.Presubmit
 import android.platform.test.annotations.RequiresFlagsDisabled
@@ -38,9 +37,10 @@
 import com.android.wm.shell.flicker.pip.common.PipTransition
 import com.android.wm.shell.flicker.pip.common.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_LANDSCAPE
 import com.android.wm.shell.flicker.pip.common.PipTransition.BroadcastActionTrigger.Companion.ORIENTATION_PORTRAIT
-import org.junit.Assume
-import org.junit.Before
+import org.junit.Assume.assumeFalse
+import org.junit.Assume.assumeTrue
 import org.junit.FixMethodOrder
+import org.junit.Ignore
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.junit.runners.MethodSorters
@@ -72,10 +72,10 @@
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 @RequiresFlagsDisabled(Flags.FLAG_ENABLE_PIP2)
-class EnterPipToOtherOrientation(flicker: LegacyFlickerTest) : PipTransition(flicker) {
+open class EnterPipToOtherOrientation(flicker: LegacyFlickerTest) : PipTransition(flicker) {
     override val pipApp: PipAppHelper = PipAppHelper(instrumentation)
-    private val testApp = FixedOrientationAppHelper(instrumentation)
-    private val startingBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_90)
+    internal val testApp = FixedOrientationAppHelper(instrumentation)
+    internal val startingBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_90)
     private val endingBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_0)
 
     override val thisTransition: FlickerBuilder.() -> Unit = {
@@ -112,32 +112,27 @@
     }
 
     /**
-     * This test is not compatible with Tablets. When using [Activity.setRequestedOrientation] to
-     * fix a orientation, Tablets instead keep the same orientation and add letterboxes
-     */
-    @Before
-    fun setup() {
-        Assume.assumeFalse(tapl.isTablet)
-    }
-
-    /**
      * Checks that all parts of the screen are covered at the start and end of the transition
      */
     @Presubmit
     @Test
-    fun entireScreenCoveredAtStartAndEnd() = flicker.entireScreenCovered()
+    fun entireScreenCoveredAtStartAndEnd() {
+        assumeFalse(tapl.isTablet)
+        flicker.entireScreenCovered()
+    }
 
     /** Checks [pipApp] window remains visible and on top throughout the transition */
     @Presubmit
     @Test
     fun pipAppWindowIsAlwaysOnTop() {
+        assumeFalse(tapl.isTablet)
         flicker.assertWm { isAppWindowOnTop(pipApp) }
     }
 
     /** Checks that [testApp] window is not visible at the start */
     @Presubmit
     @Test
-    fun testAppWindowInvisibleOnStart() {
+    open fun testAppWindowInvisibleOnStart() {
         flicker.assertWmStart { isAppWindowInvisible(testApp) }
     }
 
@@ -145,13 +140,15 @@
     @Presubmit
     @Test
     fun testAppWindowVisibleOnEnd() {
+        assumeFalse(tapl.isTablet)
         flicker.assertWmEnd { isAppWindowVisible(testApp) }
     }
 
     /** Checks that [testApp] layer is not visible at the start */
     @Presubmit
     @Test
-    fun testAppLayerInvisibleOnStart() {
+    open fun testAppLayerInvisibleOnStart() {
+        assumeFalse(tapl.isTablet)
         flicker.assertLayersStart { isInvisible(testApp) }
     }
 
@@ -159,6 +156,7 @@
     @Presubmit
     @Test
     fun testAppLayerVisibleOnEnd() {
+        assumeFalse(tapl.isTablet)
         flicker.assertLayersEnd { isVisible(testApp) }
     }
 
@@ -168,8 +166,8 @@
      */
     @Presubmit
     @Test
-    fun pipAppLayerCoversFullScreenOnStart() {
-        Assume.assumeFalse(tapl.isTablet)
+    open fun pipAppLayerCoversFullScreenOnStart() {
+        assumeFalse(tapl.isTablet)
         flicker.assertLayersStart { visibleRegion(pipApp).coversExactly(startingBounds) }
     }
 
@@ -177,10 +175,10 @@
      * Checks that the visible region of [pipApp] covers the full display area at the start of the
      * transition
      */
-    @Postsubmit
+    @Ignore("TODO(b/356277166): enable the tablet test")
     @Test
-    fun pipAppLayerPlusLetterboxCoversFullScreenOnStartTablet() {
-        Assume.assumeFalse(tapl.isTablet)
+    open fun pipAppLayerPlusLetterboxCoversFullScreenOnStartTablet() {
+        assumeTrue(tapl.isTablet)
         flicker.assertLayersStart {
             visibleRegion(pipApp.or(ComponentNameMatcher.LETTERBOX)).coversExactly(startingBounds)
         }
@@ -193,6 +191,7 @@
     @Presubmit
     @Test
     fun testAppPlusPipLayerCoversFullScreenOnEnd() {
+        assumeFalse(tapl.isTablet)
         flicker.assertLayersEnd {
             val pipRegion = visibleRegion(pipApp).region
             visibleRegion(testApp).plus(pipRegion).coversExactly(endingBounds)
@@ -202,6 +201,7 @@
     @Postsubmit
     @Test
     fun menuOverlayMatchesTaskSurface() {
+        assumeFalse(tapl.isTablet)
         flicker.assertLayersEnd {
             val pipAppRegion = visibleRegion(pipApp)
             val pipMenuRegion = visibleRegion(ComponentNameMatcher.PIP_MENU_OVERLAY)
@@ -212,6 +212,7 @@
     @Presubmit
     @Test
     fun pipLayerRemainInsideVisibleBounds() {
+        assumeFalse(tapl.isTablet)
         // during the transition we assert the center point is within the display bounds, since it
         // might go outside of bounds as we resize from landscape fullscreen to destination bounds,
         // and once the animation is over we assert that it's fully within the display bounds, at
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt
index f9a9df4..2f4c800 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/ExitPipToAppViaIntentTest.kt
@@ -64,7 +64,7 @@
         }
         transitions {
             // This will bring PipApp to fullscreen
-            pipApp.exitPipToFullScreenViaIntent(wmHelper)
+            pipApp.exitPipToOriginalTaskViaIntent(wmHelper)
             // Wait until the other app is no longer visible
             wmHelper.StateSyncBuilder().withWindowSurfaceDisappeared(testApp).waitForAndVerify()
         }
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenAutoEnterPipOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenAutoEnterPipOnGoToHomeTest.kt
index 1c40d89..f9c60ad 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenAutoEnterPipOnGoToHomeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenAutoEnterPipOnGoToHomeTest.kt
@@ -65,11 +65,11 @@
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 @RequiresFlagsDisabled(Flags.FLAG_ENABLE_PIP2)
-class FromSplitScreenAutoEnterPipOnGoToHomeTest(flicker: LegacyFlickerTest) :
+open class FromSplitScreenAutoEnterPipOnGoToHomeTest(flicker: LegacyFlickerTest) :
     AutoEnterPipOnGoToHomeTest(flicker) {
     private val portraitDisplayBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_0)
     /** Second app used to enter split screen mode */
-    private val secondAppForSplitScreen =
+    internal val secondAppForSplitScreen =
         SimpleAppHelper(
             instrumentation,
             ActivityOptions.SplitScreen.Primary.LABEL,
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenEnterPipOnUserLeaveHintTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenEnterPipOnUserLeaveHintTest.kt
index 79e2e4e..805f4c2 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenEnterPipOnUserLeaveHintTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/FromSplitScreenEnterPipOnUserLeaveHintTest.kt
@@ -67,12 +67,13 @@
 @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
 @RequiresFlagsDisabled(Flags.FLAG_ENABLE_PIP2)
-class FromSplitScreenEnterPipOnUserLeaveHintTest(flicker: LegacyFlickerTest) :
+@FlakyTest(bugId = 386333280)
+open class FromSplitScreenEnterPipOnUserLeaveHintTest(flicker: LegacyFlickerTest) :
     EnterPipTransition(flicker) {
     override val pipApp: PipAppHelper = PipAppHelper(instrumentation)
     private val portraitDisplayBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_0)
     /** Second app used to enter split screen mode */
-    private val secondAppForSplitScreen =
+    internal val secondAppForSplitScreen =
         SimpleAppHelper(
             instrumentation,
             ActivityOptions.SplitScreen.Primary.LABEL,
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 9d46ac1..86c32de 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
@@ -43,8 +43,26 @@
 import org.junit.runners.Parameterized
 
 /**
- * Test exiting Pip with orientation changes. To run this test:
- * `atest WMShellFlickerTestsPip1:SetRequestedOrientationWhilePinned`
+ * Test leaving pip while changing orientation (from pip window in portrait to app in landscape)
+ *
+ * To run this test: `atest WMShellFlickerTestsPip:SetRequestedOrientationWhilePinned`
+ *
+ * Actions:
+ * ```
+ *     Launch [pipApp] on a fixed landscape orientation
+ *     Broadcast action [ACTION_ENTER_PIP] to enter pip mode in portrait
+ *     Restore PIP from the original task to landscape
+ * ```
+ *
+ * Notes:
+ * ```
+ *     1. Some default assertions (e.g., nav bar, status bar and screen covered)
+ *        are inherited [PipTransition]
+ *     2. Part of the test setup occurs automatically via
+ *        [android.tools.flicker.legacy.runner.TransitionRunner],
+ *        including configuring navigation mode, initial orientation and ensuring no
+ *        apps are running before setup
+ * ```
  */
 @RequiresDevice
 @RunWith(Parameterized::class)
@@ -53,7 +71,7 @@
 @RequiresFlagsDisabled(Flags.FLAG_ENABLE_PIP2)
 open class SetRequestedOrientationWhilePinned(flicker: LegacyFlickerTest) : PipTransition(flicker) {
     private val startingBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_0)
-    private val endingBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_90)
+    internal open val endingBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_90)
 
     override val thisTransition: FlickerBuilder.() -> Unit = {
         transitions {
@@ -131,7 +149,7 @@
 
     @Presubmit
     @Test
-    fun pipAppLayerCoversFullScreen() {
+    open fun pipAppLayerCoversDisplayBoundsOnEnd() {
         flicker.assertLayersEnd { visibleRegion(pipApp).coversExactly(endingBounds) }
     }
 
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/nonmatchparent/BottomHalfEnterPipToOtherOrientation.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/nonmatchparent/BottomHalfEnterPipToOtherOrientation.kt
new file mode 100644
index 0000000..d65f158
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/nonmatchparent/BottomHalfEnterPipToOtherOrientation.kt
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.pip.nonmatchparent
+
+import android.platform.test.annotations.Presubmit
+import android.platform.test.annotations.RequiresFlagsDisabled
+import android.platform.test.annotations.RequiresFlagsEnabled
+import android.tools.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.flicker.legacy.LegacyFlickerTest
+import android.tools.traces.component.ComponentNameMatcher
+import com.android.server.wm.flicker.helpers.BottomHalfPipAppHelper
+import com.android.server.wm.flicker.helpers.PipAppHelper
+import com.android.wm.shell.Flags
+import com.android.wm.shell.flicker.pip.EnterPipToOtherOrientation
+import org.junit.Assume.assumeFalse
+import org.junit.Assume.assumeTrue
+import org.junit.FixMethodOrder
+import org.junit.Ignore
+import org.junit.Test
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test entering pip while changing orientation (from bottom half app in landscape to pip window in
+ * portrait)
+ *
+ * To run this test: `atest WMShellFlickerTestsPip:BottomHalfEnterPipToOtherOrientation`
+ *
+ * Actions:
+ * ```
+ *     Launch [testApp] on a fixed portrait orientation
+ *     Launch [pipApp] on a fixed landscape orientation
+ *     Broadcast action [ACTION_ENTER_PIP] to enter pip mode
+ * ```
+ *
+ * Notes:
+ * ```
+ *     1. Some default assertions (e.g., nav bar, status bar and screen covered)
+ *        are inherited [PipTransition]
+ *     2. Part of the test setup occurs automatically via
+ *        [android.tools.flicker.legacy.runner.TransitionRunner],
+ *        including configuring navigation mode, initial orientation and ensuring no
+ *        apps are running before setup
+ * ```
+ */
+// TODO(b/380796448): re-enable tests after the support of non-match parent PIP animation for PIP2.
+@RequiresFlagsDisabled(Flags.FLAG_ENABLE_PIP2)
+@RequiresFlagsEnabled(com.android.window.flags.Flags.FLAG_BETTER_SUPPORT_NON_MATCH_PARENT_ACTIVITY)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class BottomHalfEnterPipToOtherOrientation(flicker: LegacyFlickerTest) :
+    EnterPipToOtherOrientation(flicker)
+{
+    override val pipApp: PipAppHelper = BottomHalfPipAppHelper(instrumentation)
+
+    @Presubmit
+    @Test
+    override fun pipAppLayerCoversFullScreenOnStart() {
+        // Test app and pip app should covers the entire screen on start.
+        assumeFalse(tapl.isTablet)
+        flicker.assertLayersStart {
+            visibleRegion(pipApp.or(testApp)).coversExactly(startingBounds)
+        }
+    }
+
+    @Ignore("TODO(b/356277166): enable the tablet test")
+    @Test
+    override fun pipAppLayerPlusLetterboxCoversFullScreenOnStartTablet() {
+        // Test app and pip app should covers the entire screen on start.
+        assumeTrue(tapl.isTablet)
+        flicker.assertLayersStart {
+            visibleRegion(pipApp.or(ComponentNameMatcher.LETTERBOX)).coversExactly(startingBounds)
+        }
+    }
+
+    @Presubmit
+    @Test
+    override fun testAppWindowInvisibleOnStart() {
+        // Test app and pip app should covers the entire screen on start.
+    }
+
+    @Presubmit
+    @Test
+    override fun testAppLayerInvisibleOnStart() {
+        // Test app and pip app should covers the entire screen on start.
+    }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/nonmatchparent/BottomHalfExitPipToAppViaIntentTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/nonmatchparent/BottomHalfExitPipToAppViaIntentTest.kt
index 8ed9cd2..8a10c784 100644
--- a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/nonmatchparent/BottomHalfExitPipToAppViaIntentTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/nonmatchparent/BottomHalfExitPipToAppViaIntentTest.kt
@@ -67,7 +67,7 @@
         }
         transitions {
             // This will bring PipApp to fullscreen
-            pipApp.exitPipToFullScreenViaIntent(wmHelper)
+            pipApp.exitPipToOriginalTaskViaIntent(wmHelper)
             // Wait until the transition idle and test and pip app still shows.
             wmHelper.StateSyncBuilder().withLayerVisible(testApp).withLayerVisible(pipApp)
                 .withAppTransitionIdle().waitForAndVerify()
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/nonmatchparent/BottomHalfFromSplitScreenAutoEnterPipOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/nonmatchparent/BottomHalfFromSplitScreenAutoEnterPipOnGoToHomeTest.kt
new file mode 100644
index 0000000..1311917
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/nonmatchparent/BottomHalfFromSplitScreenAutoEnterPipOnGoToHomeTest.kt
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY 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.pip.nonmatchparent
+
+import android.platform.test.annotations.Presubmit
+import android.platform.test.annotations.RequiresDevice
+import android.platform.test.annotations.RequiresFlagsDisabled
+import android.platform.test.annotations.RequiresFlagsEnabled
+import android.tools.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.flicker.legacy.FlickerBuilder
+import android.tools.flicker.legacy.LegacyFlickerTest
+import android.tools.traces.parsers.toFlickerComponent
+import com.android.server.wm.flicker.helpers.BottomHalfPipAppHelper
+import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.window.flags.Flags
+import com.android.wm.shell.flicker.pip.FromSplitScreenAutoEnterPipOnGoToHomeTest
+import com.android.wm.shell.flicker.utils.SplitScreenUtils
+import org.junit.Assume
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test entering pip from a bottom half layout app via auto-enter property when navigating to home
+ * from split screen.
+ *
+ * To run this test:
+ *     `atest WMShellFlickerTestsPip:BottomHalfFromSplitScreenAutoEnterPipOnGoToHomeTest`
+ *
+ * Actions:
+ * ```
+ *     Launch an app in full screen
+ *     Open all apps and drag another app icon to enter split screen
+ *     Select "Auto-enter PiP" radio button
+ *     Layout the [pipApp] to the bottom half
+ *     Press Home button or swipe up to go Home and put [pipApp] in pip mode
+ * ```
+ *
+ * Notes:
+ * ```
+ *     1. All assertions are inherited from [EnterPipTest]
+ *     2. Part of the test setup occurs automatically via
+ *        [android.tools.flicker.legacy.runner.TransitionRunner],
+ *        including configuring navigation mode, initial orientation and ensuring no
+ *        apps are running before setup
+ * ```
+ */
+// TODO(b/380796448): re-enable tests after the support of non-match parent PIP animation for PIP2.
+@RequiresFlagsDisabled(com.android.wm.shell.Flags.FLAG_ENABLE_PIP2)
+@RequiresFlagsEnabled(Flags.FLAG_BETTER_SUPPORT_NON_MATCH_PARENT_ACTIVITY)
+@RequiresDevice
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class BottomHalfFromSplitScreenAutoEnterPipOnGoToHomeTest(flicker: LegacyFlickerTest) :
+    FromSplitScreenAutoEnterPipOnGoToHomeTest(flicker)
+{
+    override val pipApp = BottomHalfPipAppHelper(
+        instrumentation,
+        useLaunchingActivity = true,
+        // Set the activity to fill task to enable auto enter pip via the radio option.
+        fillTaskOnCreate = true
+    )
+
+    /** Defines the transition used to run the test */
+    override val transition: FlickerBuilder.() -> Unit
+        get() = {
+            setup {
+                secondAppForSplitScreen.launchViaIntent(wmHelper)
+                pipApp.launchViaIntent(wmHelper)
+                tapl.goHome()
+                SplitScreenUtils.enterSplit(
+                    wmHelper,
+                    tapl,
+                    device,
+                    pipApp,
+                    secondAppForSplitScreen,
+                    flicker.scenario.startRotation
+                )
+                pipApp.enableAutoEnterForPipActivity()
+                // Set BottomHalfPipActivity to bottom half layout to continue the test.
+                pipApp.toggleBottomHalfLayout()
+                wmHelper.StateSyncBuilder()
+                    .withLayerVisible(
+                        ActivityOptions.BottomHalfPip.LAUNCHING_APP_COMPONENT.toFlickerComponent()
+                    ).waitForAndVerify()
+            }
+            teardown {
+                pipApp.exit(wmHelper)
+                secondAppForSplitScreen.exit(wmHelper)
+            }
+            transitions { tapl.goHome() }
+        }
+
+    @Presubmit
+    @Test
+    override fun pipLayerMovesTowardsRightBottomCorner() {
+        // For bottom half layout in split secondary, the start position may be lower than the final
+        // pip task position.
+    }
+
+    /**
+     * Verifies the left edge of the pip layer moves to the right continuously.
+     */
+    @Presubmit
+    @Test
+    fun pipLayerMovesTowardsRight() {
+        // in gestural nav the swipe makes PiP first go upwards
+        Assume.assumeFalse(flicker.scenario.isGesturalNavigation)
+        flicker.assertLayers {
+            val pipLayerList = this.layers { pipApp.layerMatchesAnyOf(it) && it.isVisible }
+            pipLayerList.zipWithNext { previous, current ->
+                current.visibleRegion.isLeftEdgeToTheRight(previous.visibleRegion.region)
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/nonmatchparent/BottomHalfFromSplitScreenEnterPipOnUserLeaveHintTest.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/nonmatchparent/BottomHalfFromSplitScreenEnterPipOnUserLeaveHintTest.kt
new file mode 100644
index 0000000..2e34b6a
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/nonmatchparent/BottomHalfFromSplitScreenEnterPipOnUserLeaveHintTest.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.wm.shell.flicker.pip.nonmatchparent
+
+import android.platform.test.annotations.RequiresDevice
+import android.platform.test.annotations.RequiresFlagsDisabled
+import android.platform.test.annotations.RequiresFlagsEnabled
+import android.tools.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.flicker.legacy.FlickerBuilder
+import android.tools.flicker.legacy.LegacyFlickerTest
+import android.tools.traces.parsers.toFlickerComponent
+import com.android.server.wm.flicker.helpers.BottomHalfPipAppHelper
+import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.window.flags.Flags
+import com.android.wm.shell.flicker.pip.FromSplitScreenEnterPipOnUserLeaveHintTest
+import com.android.wm.shell.flicker.utils.SplitScreenUtils
+import org.junit.FixMethodOrder
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test entering pip from a bottom half layout app via enter-pip-on-user-leave property when
+ * navigating to home from split screen.
+ *
+ * To run this test:
+ *     `atest WMShellFlickerTestsPip:BottomHalfFromSplitScreenEnterPipOnUserLeaveHintTest`
+ *
+ * Actions:
+ * ```
+ *     Launch an app in full screen
+ *     Open all apps and drag another app icon to enter split screen
+ *     Select "Enter PiP on user leave" radio button
+ *     Layout the [pipApp] to the bottom half
+ *     Press Home button or swipe up to go Home and put [pipApp] in pip mode
+ * ```
+ *
+ * Notes:
+ * ```
+ *     1. All assertions are inherited from [EnterPipTest]
+ *     2. Part of the test setup occurs automatically via
+ *        [android.tools.flicker.legacy.runner.TransitionRunner],
+ *        including configuring navigation mode, initial orientation and ensuring no
+ *        apps are running before setup
+ * ```
+ */
+// TODO(b/380796448): re-enable tests after the support of non-match parent PIP animation for PIP2.
+@RequiresFlagsDisabled(com.android.wm.shell.Flags.FLAG_ENABLE_PIP2)
+@RequiresFlagsEnabled(Flags.FLAG_BETTER_SUPPORT_NON_MATCH_PARENT_ACTIVITY)
+@RequiresDevice
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class BottomHalfFromSplitScreenEnterPipOnUserLeaveHintTest(flicker: LegacyFlickerTest) :
+    FromSplitScreenEnterPipOnUserLeaveHintTest(flicker)
+{
+    override val pipApp = BottomHalfPipAppHelper(
+        instrumentation,
+        useLaunchingActivity = true,
+        // Set the activity to fill task to enable user leave hint via the radio option.
+        fillTaskOnCreate = true
+    )
+
+    /** Defines the transition used to run the test */
+    override val transition: FlickerBuilder.() -> Unit
+    get() = {
+        setup {
+            secondAppForSplitScreen.launchViaIntent(wmHelper)
+            pipApp.launchViaIntent(wmHelper)
+            tapl.goHome()
+            SplitScreenUtils.enterSplit(
+                wmHelper,
+                tapl,
+                device,
+                pipApp,
+                secondAppForSplitScreen,
+                flicker.scenario.startRotation
+            )
+            pipApp.enableEnterPipOnUserLeaveHint()
+            // Set BottomHalfPipActivity to bottom half layout to continue the test.
+            pipApp.toggleBottomHalfLayout()
+            wmHelper.StateSyncBuilder()
+                .withLayerVisible(
+                    ActivityOptions.BottomHalfPip.LAUNCHING_APP_COMPONENT.toFlickerComponent()
+                ).waitForAndVerify()
+        }
+        teardown {
+            pipApp.exit(wmHelper)
+            secondAppForSplitScreen.exit(wmHelper)
+        }
+        transitions { tapl.goHome() }
+    }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/nonmatchparent/BottomHalfSetRequestedOrientationWhilePinned.kt b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/nonmatchparent/BottomHalfSetRequestedOrientationWhilePinned.kt
new file mode 100644
index 0000000..42f5a50
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/pip/src/com/android/wm/shell/flicker/pip/nonmatchparent/BottomHalfSetRequestedOrientationWhilePinned.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.flicker.pip.nonmatchparent
+
+import android.platform.test.annotations.Presubmit
+import android.platform.test.annotations.RequiresFlagsDisabled
+import android.platform.test.annotations.RequiresFlagsEnabled
+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.traces.parsers.toFlickerComponent
+import com.android.server.wm.flicker.helpers.BottomHalfPipAppHelper
+import com.android.server.wm.flicker.helpers.PipAppHelper
+import com.android.server.wm.flicker.testapp.ActivityOptions.BottomHalfPip
+import com.android.wm.shell.Flags
+import com.android.wm.shell.flicker.pip.SetRequestedOrientationWhilePinned
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test leaving pip while changing orientation (from pip window in portrait to bottom half app in
+ * landscape)
+ *
+ * To run this test: `atest WMShellFlickerTestsPip:BottomHalfSetRequestedOrientationWhilePinned`
+ *
+ * Actions:
+ * ```
+ *     Launch bottom half [pipApp] on a fixed landscape orientation via launching app
+ *     Broadcast action [ACTION_ENTER_PIP] to enter pip mode
+ *     Restore PIP from the original task to landscape
+ * ```
+ *
+ * Notes:
+ * ```
+ *     1. Some default assertions (e.g., nav bar, status bar and screen covered)
+ *        are inherited [PipTransition]
+ *     2. Part of the test setup occurs automatically via
+ *        [android.tools.flicker.legacy.runner.TransitionRunner],
+ *        including configuring navigation mode, initial orientation and ensuring no
+ *        apps are running before setup
+ * ```
+ */
+// TODO(b/380796448): re-enable tests after the support of non-match parent PIP animation for PIP2.
+@RequiresFlagsDisabled(Flags.FLAG_ENABLE_PIP2)
+@RequiresFlagsEnabled(com.android.window.flags.Flags.FLAG_BETTER_SUPPORT_NON_MATCH_PARENT_ACTIVITY)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class BottomHalfSetRequestedOrientationWhilePinned(flicker: LegacyFlickerTest) :
+    SetRequestedOrientationWhilePinned(flicker)
+{
+    override val pipApp: PipAppHelper = BottomHalfPipAppHelper(
+        instrumentation,
+        useLaunchingActivity = true
+    )
+
+    override val thisTransition: FlickerBuilder.() -> Unit = {
+        transitions {
+            // Launch the activity back into fullscreen and ensure that it is now in landscape
+            pipApp.exitPipToOriginalTaskViaIntent(wmHelper)
+            // System bar may fade out during fixed rotation.
+            wmHelper
+                .StateSyncBuilder()
+                .withTopVisibleApp(pipApp)
+                .withRotation(Rotation.ROTATION_90)
+                .withNavOrTaskBarVisible()
+                .withStatusBarVisible()
+                .waitForAndVerify()
+        }
+    }
+
+    @Presubmit
+    @Test
+    override fun pipAppLayerCoversDisplayBoundsOnEnd() {
+        flicker.assertLayersEnd {
+            visibleRegion(pipApp
+                .or(BottomHalfPip.LAUNCHING_APP_COMPONENT.toFlickerComponent()))
+                .coversExactly(endingBounds)
+        }
+    }
+}
\ No newline at end of file
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 47ee7bb..bbdb90f 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
@@ -61,9 +61,7 @@
 import android.os.RemoteException;
 import android.platform.test.annotations.EnableFlags;
 import android.platform.test.flag.junit.SetFlagsRule;
-import android.provider.Settings;
 import android.testing.AndroidTestingRunner;
-import android.testing.TestableContentResolver;
 import android.testing.TestableLooper;
 import android.view.IRemoteAnimationRunner;
 import android.view.KeyEvent;
@@ -84,7 +82,6 @@
 import androidx.annotation.Nullable;
 import androidx.test.filters.SmallTest;
 
-import com.android.internal.util.test.FakeSettingsProvider;
 import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
 import com.android.wm.shell.ShellTestCase;
 import com.android.wm.shell.TestShellExecutor;
@@ -109,7 +106,6 @@
 @RunWith(AndroidTestingRunner.class)
 public class BackAnimationControllerTest extends ShellTestCase {
 
-    private static final String ANIMATION_ENABLED = "1";
     private final TestShellExecutor mShellExecutor = new TestShellExecutor();
 
     private ShellInit mShellInit;
@@ -148,8 +144,6 @@
     private Transitions.TransitionHandler mTakeoverHandler;
 
     private BackAnimationController mController;
-    private TestableContentResolver mContentResolver;
-    private TestableLooper mTestableLooper;
 
     private DefaultCrossActivityBackAnimation mDefaultCrossActivityBackAnimation;
     private CrossTaskBackAnimation mCrossTaskBackAnimation;
@@ -166,11 +160,6 @@
         MockitoAnnotations.initMocks(this);
         mContext.addMockSystemService(InputManager.class, mInputManager);
         mContext.getApplicationInfo().privateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
-        mContentResolver = new TestableContentResolver(mContext);
-        mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
-        Settings.Global.putString(mContentResolver, Settings.Global.ENABLE_BACK_ANIMATION,
-                ANIMATION_ENABLED);
-        mTestableLooper = TestableLooper.get(this);
         mShellInit = spy(new ShellInit(mShellExecutor));
         mDefaultCrossActivityBackAnimation = new DefaultCrossActivityBackAnimation(mContext,
                 mAnimationBackground, mRootTaskDisplayAreaOrganizer, mHandler);
@@ -187,10 +176,8 @@
                         mShellInit,
                         mShellController,
                         mShellExecutor,
-                        new Handler(mTestableLooper.getLooper()),
                         mActivityTaskManager,
                         mContext,
-                        mContentResolver,
                         mAnimationBackground,
                         mShellBackAnimationRegistry,
                         mShellCommandHandler,
@@ -342,47 +329,6 @@
     }
 
     @Test
-    public void animationDisabledFromSettings() throws RemoteException {
-        // Toggle the setting off
-        Settings.Global.putString(mContentResolver, Settings.Global.ENABLE_BACK_ANIMATION, "0");
-        ShellInit shellInit = new ShellInit(mShellExecutor);
-        mController =
-                new BackAnimationController(
-                        shellInit,
-                        mShellController,
-                        mShellExecutor,
-                        new Handler(mTestableLooper.getLooper()),
-                        mActivityTaskManager,
-                        mContext,
-                        mContentResolver,
-                        mAnimationBackground,
-                        mShellBackAnimationRegistry,
-                        mShellCommandHandler,
-                        mTransitions,
-                        mHandler);
-        shellInit.init();
-        registerAnimation(BackNavigationInfo.TYPE_RETURN_TO_HOME);
-
-        ArgumentCaptor<BackMotionEvent> backEventCaptor =
-                ArgumentCaptor.forClass(BackMotionEvent.class);
-
-        createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME,
-                /* enableAnimation = */ false,
-                /* isAnimationCallback = */ false);
-
-        triggerBackGesture();
-        releaseBackGesture();
-
-        verify(mAppCallback, times(1)).onBackInvoked();
-
-        verify(mAnimatorCallback, never()).onBackStarted(any());
-        verify(mAnimatorCallback, never()).onBackProgressed(backEventCaptor.capture());
-        verify(mAnimatorCallback, never()).onBackInvoked();
-        verify(mBackAnimationRunner, never()).onAnimationStart(
-                anyInt(), any(), any(), any(), any());
-    }
-
-    @Test
     public void gestureQueued_WhenPreviousTransitionHasNotYetEnded() throws RemoteException {
         registerAnimation(BackNavigationInfo.TYPE_RETURN_TO_HOME);
         createNavigationInfo(BackNavigationInfo.TYPE_RETURN_TO_HOME,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java
index bf54e79..6d7a18d 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java
@@ -187,14 +187,14 @@
         mTargetProgressCalled.await(1, TimeUnit.SECONDS);
         assertNotNull(mReceivedBackEvent);
 
-        // Trigger back invoked animation
         CountDownLatch finishCallbackCalled = new CountDownLatch(1);
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(
-                () -> mProgressAnimator.onBackInvoked(finishCallbackCalled::countDown));
-
-        // remove onBackCancelled finishCallback (while progress is still animating to 0)
-        InstrumentationRegistry.getInstrumentation().runOnMainSync(
-                () -> mProgressAnimator.removeOnBackInvokedFinishCallback());
+        // Trigger back invoked animation and remove onBackInvoked finishCallback (while progress
+        // is still animating to 1)
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+                    mProgressAnimator.onBackInvoked(finishCallbackCalled::countDown);
+                    mProgressAnimator.removeOnBackInvokedFinishCallback();
+                }
+        );
 
         // call reset (which triggers the finishCallback invocation, if one is present)
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> mProgressAnimator.reset());
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 784e190..b5c9fa1 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
@@ -62,11 +62,12 @@
 import com.android.wm.shell.desktopmode.DesktopUserRepositories;
 import com.android.wm.shell.sysui.ShellController;
 import com.android.wm.shell.sysui.ShellInit;
-import com.android.wm.shell.transition.FocusTransitionObserver;
 import com.android.wm.shell.transition.Transitions;
 
 import dagger.Lazy;
 
+import java.util.Optional;
+
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
@@ -76,8 +77,6 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-import java.util.Optional;
-
 /**
  * Tests for {@link CompatUIController}.
  *
@@ -128,8 +127,6 @@
     private DesktopUserRepositories mDesktopUserRepositories;
     @Mock
     private DesktopRepository mDesktopRepository;
-    @Mock
-    private FocusTransitionObserver mFocusTransitionObserver;
 
     @Captor
     ArgumentCaptor<OnInsetsChangedListener> mOnInsetsChangedListenerCaptor;
@@ -165,8 +162,7 @@
                 mMockDisplayController, mMockDisplayInsetsController, mMockImeController,
                 mMockSyncQueue, mMockExecutor, mMockTransitionsLazy, mDockStateReader,
                 mCompatUIConfiguration, mCompatUIShellCommandHandler, mAccessibilityManager,
-                mCompatUIStatusManager, Optional.of(mDesktopUserRepositories),
-                mFocusTransitionObserver) {
+                mCompatUIStatusManager, Optional.of(mDesktopUserRepositories)) {
             @Override
             CompatUIWindowManager createCompatUiWindowManager(Context context, TaskInfo taskInfo,
                     ShellTaskOrganizer.TaskListener taskListener) {
@@ -284,7 +280,6 @@
         doReturn(false).when(mMockRestartDialogLayout).updateCompatInfo(any(), any(), anyBoolean());
 
         TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true);
-        when(mFocusTransitionObserver.hasGlobalFocus((RunningTaskInfo) taskInfo)).thenReturn(true);
         mController.onCompatInfoChanged(new CompatUIInfo(taskInfo, mMockTaskListener));
 
         verify(mController).createCompatUiWindowManager(any(), eq(taskInfo), eq(mMockTaskListener));
@@ -416,7 +411,6 @@
         // Verify button remains hidden while IME is showing.
         TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true);
         mController.onCompatInfoChanged(new CompatUIInfo(taskInfo, mMockTaskListener));
-        when(mFocusTransitionObserver.hasGlobalFocus((RunningTaskInfo) taskInfo)).thenReturn(false);
 
         verify(mMockCompatLayout).updateCompatInfo(taskInfo, mMockTaskListener,
                 /* canShow= */ false);
@@ -449,7 +443,6 @@
         // Verify button remains hidden while keyguard is showing.
         TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true);
         mController.onCompatInfoChanged(new CompatUIInfo(taskInfo, mMockTaskListener));
-        when(mFocusTransitionObserver.hasGlobalFocus((RunningTaskInfo) taskInfo)).thenReturn(false);
 
         verify(mMockCompatLayout).updateCompatInfo(taskInfo, mMockTaskListener,
                 /* canShow= */ false);
@@ -530,7 +523,6 @@
     @RequiresFlagsDisabled(Flags.FLAG_APP_COMPAT_UI_FRAMEWORK)
     public void testRestartLayoutRecreatedIfNeeded() {
         final TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true);
-        when(mFocusTransitionObserver.hasGlobalFocus((RunningTaskInfo) taskInfo)).thenReturn(false);
         doReturn(true).when(mMockRestartDialogLayout)
                 .needsToBeRecreated(any(TaskInfo.class),
                         any(ShellTaskOrganizer.TaskListener.class));
@@ -546,7 +538,6 @@
     @RequiresFlagsDisabled(Flags.FLAG_APP_COMPAT_UI_FRAMEWORK)
     public void testRestartLayoutNotRecreatedIfNotNeeded() {
         final TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true);
-        when(mFocusTransitionObserver.hasGlobalFocus((RunningTaskInfo) taskInfo)).thenReturn(false);
         doReturn(false).when(mMockRestartDialogLayout)
                 .needsToBeRecreated(any(TaskInfo.class),
                         any(ShellTaskOrganizer.TaskListener.class));
@@ -567,8 +558,7 @@
 
         // Create new task
         final TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true,
-                /* isVisible */ true);
-        when(mFocusTransitionObserver.hasGlobalFocus((RunningTaskInfo) taskInfo)).thenReturn(true);
+                /* isVisible */ true, /* isFocused */ true);
 
         // Simulate new task being shown
         mController.updateActiveTaskInfo(taskInfo);
@@ -584,8 +574,7 @@
     public void testUpdateActiveTaskInfo_newTask_notVisibleOrFocused_notUpdated() {
         // Create new task
         final TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true,
-                /* isVisible */ true);
-        when(mFocusTransitionObserver.hasGlobalFocus((RunningTaskInfo) taskInfo)).thenReturn(true);
+                /* isVisible */ true, /* isFocused */ true);
 
         // Simulate task being shown
         mController.updateActiveTaskInfo(taskInfo);
@@ -603,8 +592,7 @@
 
         // Create visible but NOT focused task
         final TaskInfo taskInfo1 = createTaskInfo(DISPLAY_ID, newTaskId, /* hasSizeCompat= */ true,
-                /* isVisible */ true);
-        when(mFocusTransitionObserver.hasGlobalFocus((RunningTaskInfo) taskInfo)).thenReturn(false);
+                /* isVisible */ true, /* isFocused */ false);
 
         // Simulate new task being shown
         mController.updateActiveTaskInfo(taskInfo1);
@@ -616,8 +604,7 @@
 
         // Create focused but NOT visible task
         final TaskInfo taskInfo2 = createTaskInfo(DISPLAY_ID, newTaskId, /* hasSizeCompat= */ true,
-                /* isVisible */ false);
-        when(mFocusTransitionObserver.hasGlobalFocus((RunningTaskInfo) taskInfo)).thenReturn(true);
+                /* isVisible */ false, /* isFocused */ true);
 
         // Simulate new task being shown
         mController.updateActiveTaskInfo(taskInfo2);
@@ -629,8 +616,7 @@
 
         // Create NOT focused but NOT visible task
         final TaskInfo taskInfo3 = createTaskInfo(DISPLAY_ID, newTaskId, /* hasSizeCompat= */ true,
-                /* isVisible */ false);
-        when(mFocusTransitionObserver.hasGlobalFocus((RunningTaskInfo) taskInfo)).thenReturn(false);
+                /* isVisible */ false, /* isFocused */ false);
 
         // Simulate new task being shown
         mController.updateActiveTaskInfo(taskInfo3);
@@ -646,8 +632,7 @@
     public void testUpdateActiveTaskInfo_sameTask_notUpdated() {
         // Create new task
         final TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true,
-                /* isVisible */ true);
-        when(mFocusTransitionObserver.hasGlobalFocus((RunningTaskInfo) taskInfo)).thenReturn(true);
+                /* isVisible */ true, /* isFocused */ true);
 
         // Simulate new task being shown
         mController.updateActiveTaskInfo(taskInfo);
@@ -675,8 +660,7 @@
     public void testUpdateActiveTaskInfo_transparentTask_notUpdated() {
         // Create new task
         final TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true,
-                /* isVisible */ true);
-        when(mFocusTransitionObserver.hasGlobalFocus((RunningTaskInfo) taskInfo)).thenReturn(true);
+                /* isVisible */ true, /* isFocused */ true);
 
         // Simulate new task being shown
         mController.updateActiveTaskInfo(taskInfo);
@@ -694,8 +678,7 @@
 
         // Create transparent task
         final TaskInfo taskInfo1 = createTaskInfo(DISPLAY_ID, newTaskId, /* hasSizeCompat= */ true,
-                /* isVisible */ true, /* isTopActivityTransparent */ true);
-        when(mFocusTransitionObserver.hasGlobalFocus((RunningTaskInfo) taskInfo)).thenReturn(true);
+                /* isVisible */ true, /* isFocused */ true, /* isTopActivityTransparent */ true);
 
         // Simulate new task being shown
         mController.updateActiveTaskInfo(taskInfo1);
@@ -711,7 +694,6 @@
     public void testLetterboxEduLayout_notCreatedWhenLetterboxEducationIsDisabled() {
         TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true);
         taskInfo.appCompatTaskInfo.setLetterboxEducationEnabled(false);
-        when(mFocusTransitionObserver.hasGlobalFocus((RunningTaskInfo) taskInfo)).thenReturn(false);
 
         mController.onCompatInfoChanged(new CompatUIInfo(taskInfo, mMockTaskListener));
 
@@ -725,7 +707,6 @@
     public void testUpdateActiveTaskInfo_removeAllComponentWhenInDesktopModeFlagEnabled() {
         TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true);
         when(mDesktopUserRepositories.getCurrent().getVisibleTaskCount(DISPLAY_ID)).thenReturn(0);
-        when(mFocusTransitionObserver.hasGlobalFocus((RunningTaskInfo) taskInfo)).thenReturn(false);
 
         mController.onCompatInfoChanged(new CompatUIInfo(taskInfo, mMockTaskListener));
 
@@ -744,7 +725,6 @@
     public void testUpdateActiveTaskInfo_removeAllComponentWhenInDesktopModeFlagDisabled() {
         when(mDesktopUserRepositories.getCurrent().getVisibleTaskCount(DISPLAY_ID)).thenReturn(0);
         TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true);
-        when(mFocusTransitionObserver.hasGlobalFocus((RunningTaskInfo) taskInfo)).thenReturn(false);
 
         mController.onCompatInfoChanged(new CompatUIInfo(taskInfo, mMockTaskListener));
 
@@ -759,22 +739,23 @@
 
     private static TaskInfo createTaskInfo(int displayId, int taskId, boolean hasSizeCompat) {
         return createTaskInfo(displayId, taskId, hasSizeCompat, /* isVisible */ false,
-                /* isTopActivityTransparent */ false);
+                /* isFocused */ false, /* isTopActivityTransparent */ false);
     }
 
     private static TaskInfo createTaskInfo(int displayId, int taskId, boolean hasSizeCompat,
-            boolean isVisible) {
+            boolean isVisible, boolean isFocused) {
         return createTaskInfo(displayId, taskId, hasSizeCompat,
-                isVisible, /* isTopActivityTransparent */ false);
+                isVisible, isFocused, /* isTopActivityTransparent */ false);
     }
 
     private static TaskInfo createTaskInfo(int displayId, int taskId, boolean hasSizeCompat,
-            boolean isVisible, boolean isTopActivityTransparent) {
+            boolean isVisible, boolean isFocused, boolean isTopActivityTransparent) {
         RunningTaskInfo taskInfo = new RunningTaskInfo();
         taskInfo.taskId = taskId;
         taskInfo.displayId = displayId;
         taskInfo.appCompatTaskInfo.setTopActivityInSizeCompat(hasSizeCompat);
         taskInfo.isVisible = isVisible;
+        taskInfo.isFocused = isFocused;
         taskInfo.isTopActivityTransparent = isTopActivityTransparent;
         taskInfo.appCompatTaskInfo.setLetterboxEducationEnabled(true);
         taskInfo.appCompatTaskInfo.setTopActivityLetterboxed(true);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/CloseDesktopTaskTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/CloseDesktopTaskTransitionHandlerTest.kt
index db00f41..04f9ada 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/CloseDesktopTaskTransitionHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/CloseDesktopTaskTransitionHandlerTest.kt
@@ -142,7 +142,7 @@
         changeMode: Int = WindowManager.TRANSIT_CLOSE,
         task: RunningTaskInfo,
     ): TransitionInfo =
-        TransitionInfo(type, 0 /* flags */).apply {
+        TransitionInfo(type, /* flags= */ 0).apply {
             addChange(
                 TransitionInfo.Change(mock(), closingTaskLeash).apply {
                     mode = changeMode
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopBackNavigationTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopBackNavigationTransitionHandlerTest.kt
index d14c640..c705f5a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopBackNavigationTransitionHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopBackNavigationTransitionHandlerTest.kt
@@ -153,7 +153,7 @@
         changeMode: Int = WindowManager.TRANSIT_TO_BACK,
         task: RunningTaskInfo,
     ): TransitionInfo =
-        TransitionInfo(type, 0 /* flags */).apply {
+        TransitionInfo(type, /* flags= */ 0).apply {
             addChange(
                 TransitionInfo.Change(mock(), closingTaskLeash).apply {
                     mode = changeMode
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt
index 3cf84d9..372e47c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopMixedTransitionHandlerTest.kt
@@ -777,7 +777,7 @@
         task: RunningTaskInfo,
         withWallpaper: Boolean = false,
     ): TransitionInfo =
-        TransitionInfo(WindowManager.TRANSIT_CLOSE, 0 /* flags */).apply {
+        TransitionInfo(WindowManager.TRANSIT_CLOSE, /* flags= */ 0).apply {
             addChange(
                 TransitionInfo.Change(mock(), closingTaskLeash).apply {
                     mode = changeMode
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 e032616..692b503 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
@@ -1267,7 +1267,7 @@
         // Set task as systemUI package
         val systemUIPackageName =
             context.resources.getString(com.android.internal.R.string.config_systemUi)
-        val baseComponent = ComponentName(systemUIPackageName, /* class */ "")
+        val baseComponent = ComponentName(systemUIPackageName, /* cls= */ "")
         val task =
             setUpFullscreenTask().apply {
                 baseActivity = baseComponent
@@ -1284,7 +1284,7 @@
         // Set task as systemUI package
         val systemUIPackageName =
             context.resources.getString(com.android.internal.R.string.config_systemUi)
-        val baseComponent = ComponentName(systemUIPackageName, /* class */ "")
+        val baseComponent = ComponentName(systemUIPackageName, /* cls= */ "")
         val task =
             setUpFullscreenTask().apply {
                 baseActivity = baseComponent
@@ -1497,7 +1497,7 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_ON_SYSTEM_USER)
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER)
     fun moveToFullscreen_tdaFullscreen_windowingModeUndefined_removesWallpaperActivity() {
         val task = setUpFreeformTask()
         assertNotNull(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY))
@@ -1530,7 +1530,7 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_ON_SYSTEM_USER)
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER)
     fun moveToFullscreen_tdaFreeform_windowingModeFullscreen_removesWallpaperActivity() {
         val task = setUpFreeformTask()
 
@@ -1757,12 +1757,12 @@
 
         controller.moveToNextDisplay(task.taskId)
 
-        with(getLatestWct(type = TRANSIT_CHANGE)) {
-            val wallpaperChange =
-                hierarchyOps.find { op -> op.container == wallpaperToken.asBinder() }
-            assertThat(wallpaperChange).isNotNull()
-            assertThat(wallpaperChange!!.type).isEqualTo(HIERARCHY_OP_TYPE_REMOVE_TASK)
-        }
+        val wallpaperChange =
+            getLatestWct(type = TRANSIT_CHANGE).hierarchyOps.find { op ->
+                op.container == wallpaperToken.asBinder()
+            }
+        assertNotNull(wallpaperChange)
+        assertThat(wallpaperChange.type).isEqualTo(HIERARCHY_OP_TYPE_REMOVE_TASK)
     }
 
     @Test
@@ -1792,15 +1792,13 @@
 
         controller.moveToNextDisplay(task.taskId)
 
-        with(getLatestWct(type = TRANSIT_CHANGE)) {
-            val taskChange = changes[task.token.asBinder()]
-            assertThat(taskChange).isNotNull()
-            // To preserve DP size, pixel size is changed to 320x240. The ratio of the left margin
-            // to the right margin and the ratio of the top margin to bottom margin are also
-            // preserved.
-            assertThat(taskChange!!.configuration.windowConfiguration.bounds)
-                .isEqualTo(Rect(240, 160, 560, 400))
-        }
+        val taskChange = getLatestWct(type = TRANSIT_CHANGE).changes[task.token.asBinder()]
+        assertNotNull(taskChange)
+        // To preserve DP size, pixel size is changed to 320x240. The ratio of the left margin
+        // to the right margin and the ratio of the top margin to bottom margin are also
+        // preserved.
+        assertThat(taskChange.configuration.windowConfiguration.bounds)
+            .isEqualTo(Rect(240, 160, 560, 400))
     }
 
     @Test
@@ -1831,12 +1829,10 @@
 
         controller.moveToNextDisplay(task.taskId)
 
-        with(getLatestWct(type = TRANSIT_CHANGE)) {
-            val taskChange = changes[task.token.asBinder()]
-            assertThat(taskChange).isNotNull()
-            assertThat(taskChange!!.configuration.windowConfiguration.bounds)
-                .isEqualTo(Rect(960, 480, 1280, 720))
-        }
+        val taskChange = getLatestWct(type = TRANSIT_CHANGE).changes[task.token.asBinder()]
+        assertNotNull(taskChange)
+        assertThat(taskChange.configuration.windowConfiguration.bounds)
+            .isEqualTo(Rect(960, 480, 1280, 720))
     }
 
     @Test
@@ -1864,13 +1860,11 @@
 
         controller.moveToNextDisplay(task.taskId)
 
-        with(getLatestWct(type = TRANSIT_CHANGE)) {
-            val taskChange = changes[task.token.asBinder()]
-            assertThat(taskChange).isNotNull()
-            // DP size is preserved. The window is centered in the destination display.
-            assertThat(taskChange!!.configuration.windowConfiguration.bounds)
-                .isEqualTo(Rect(320, 120, 960, 600))
-        }
+        val taskChange = getLatestWct(type = TRANSIT_CHANGE).changes[task.token.asBinder()]
+        assertNotNull(taskChange)
+        // DP size is preserved. The window is centered in the destination display.
+        assertThat(taskChange.configuration.windowConfiguration.bounds)
+            .isEqualTo(Rect(320, 120, 960, 600))
     }
 
     @Test
@@ -1903,14 +1897,12 @@
 
         controller.moveToNextDisplay(task.taskId)
 
-        with(getLatestWct(type = TRANSIT_CHANGE)) {
-            val taskChange = changes[task.token.asBinder()]
-            assertThat(taskChange).isNotNull()
-            assertThat(taskChange!!.configuration.windowConfiguration.bounds.left).isAtLeast(0)
-            assertThat(taskChange.configuration.windowConfiguration.bounds.top).isAtLeast(0)
-            assertThat(taskChange.configuration.windowConfiguration.bounds.right).isAtMost(640)
-            assertThat(taskChange.configuration.windowConfiguration.bounds.bottom).isAtMost(480)
-        }
+        val taskChange = getLatestWct(type = TRANSIT_CHANGE).changes[task.token.asBinder()]
+        assertNotNull(taskChange)
+        assertThat(taskChange.configuration.windowConfiguration.bounds.left).isAtLeast(0)
+        assertThat(taskChange.configuration.windowConfiguration.bounds.top).isAtLeast(0)
+        assertThat(taskChange.configuration.windowConfiguration.bounds.right).isAtMost(640)
+        assertThat(taskChange.configuration.windowConfiguration.bounds.bottom).isAtMost(480)
     }
 
     @Test
@@ -1973,7 +1965,7 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_ON_SYSTEM_USER)
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER)
     fun onDesktopWindowClose_singleActiveTask_hasWallpaperActivityToken() {
         val task = setUpFreeformTask()
 
@@ -2019,7 +2011,7 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_ON_SYSTEM_USER)
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER)
     fun onDesktopWindowClose_multipleActiveTasks_isOnlyNonClosingTask() {
         val task1 = setUpFreeformTask()
         val task2 = setUpFreeformTask()
@@ -2033,7 +2025,7 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_ON_SYSTEM_USER)
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER)
     fun onDesktopWindowClose_multipleActiveTasks_hasMinimized() {
         val task1 = setUpFreeformTask()
         val task2 = setUpFreeformTask()
@@ -2103,7 +2095,7 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_ON_SYSTEM_USER)
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER)
     fun onTaskMinimize_singleActiveTask_hasWallpaperActivityToken_removesWallpaper() {
         val task = setUpFreeformTask()
         val transition = Binder()
@@ -2155,7 +2147,7 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_ON_SYSTEM_USER)
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER)
     fun onDesktopWindowMinimize_multipleActiveTasks_minimizesTheOnlyVisibleTask_removesWallpaper() {
         val task1 = setUpFreeformTask(active = true)
         val task2 = setUpFreeformTask(active = true)
@@ -2722,7 +2714,7 @@
         // Set task as systemUI package
         val systemUIPackageName =
             context.resources.getString(com.android.internal.R.string.config_systemUi)
-        val baseComponent = ComponentName(systemUIPackageName, /* class */ "")
+        val baseComponent = ComponentName(systemUIPackageName, /* cls= */ "")
         val task =
             setUpFreeformTask().apply {
                 baseActivity = baseComponent
@@ -2743,7 +2735,7 @@
         // Set task as systemUI package
         val systemUIPackageName =
             context.resources.getString(com.android.internal.R.string.config_systemUi)
-        val baseComponent = ComponentName(systemUIPackageName, /* class */ "")
+        val baseComponent = ComponentName(systemUIPackageName, /* cls= */ "")
         val task =
             setUpFullscreenTask().apply {
                 baseActivity = baseComponent
@@ -2816,7 +2808,7 @@
     @Test
     @EnableFlags(
         Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
-        Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_ON_SYSTEM_USER,
+        Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER,
     )
     fun handleRequest_backTransition_singleTaskWithToken_removesWallpaper() {
         val task = setUpFreeformTask()
@@ -2857,7 +2849,7 @@
     @EnableFlags(
         Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
         Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION,
-        Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_ON_SYSTEM_USER,
+        Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER,
     )
     fun handleRequest_backTransition_multipleTasksSingleNonClosing_removesWallpaperAndTask() {
         val task1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
@@ -2875,7 +2867,7 @@
     @Test
     @EnableFlags(
         Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
-        Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_ON_SYSTEM_USER,
+        Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER,
     )
     fun handleRequest_backTransition_multipleTasksSingleNonMinimized_removesWallpaperAndTask() {
         val task1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
@@ -2942,7 +2934,7 @@
     @Test
     @EnableFlags(
         Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
-        Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_ON_SYSTEM_USER,
+        Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER,
     )
     fun handleRequest_closeTransition_singleTaskWithToken_withWallpaper_removesWallpaper() {
         val task = setUpFreeformTask()
@@ -2982,7 +2974,7 @@
     @Test
     @EnableFlags(
         Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
-        Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_ON_SYSTEM_USER,
+        Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER,
     )
     fun handleRequest_closeTransition_multipleTasksSingleNonClosing_removesWallpaper() {
         val task1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
@@ -3000,7 +2992,7 @@
     @Test
     @EnableFlags(
         Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
-        Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_ON_SYSTEM_USER,
+        Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER,
     )
     fun handleRequest_closeTransition_multipleTasksSingleNonMinimized_removesWallpaper() {
         val task1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
@@ -3092,7 +3084,7 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_ON_SYSTEM_USER)
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER)
     fun moveFocusedTaskToFullscreen_onlyVisibleNonMinimizedTask_removesWallpaperActivity() {
         val task1 = setUpFreeformTask()
         val task2 = setUpFreeformTask()
@@ -3376,11 +3368,11 @@
         spyController.onDragPositioningEnd(
             task,
             mockSurface,
-            Point(100, -100), /* position */
-            PointF(200f, -200f), /* inputCoordinate */
-            Rect(100, -100, 500, 1000), /* currentDragBounds */
-            Rect(0, 50, 2000, 2000), /* validDragArea */
-            Rect() /* dragStartBounds */,
+            position = Point(100, -100),
+            inputCoordinate = PointF(200f, -200f),
+            currentDragBounds = Rect(100, -100, 500, 1000),
+            validDragArea = Rect(0, 50, 2000, 2000),
+            dragStartBounds = Rect(),
             motionEvent,
             desktopWindowDecoration,
         )
@@ -3415,11 +3407,11 @@
         spyController.onDragPositioningEnd(
             task,
             mockSurface,
-            Point(100, 200), /* position */
-            PointF(200f, 300f), /* inputCoordinate */
-            currentDragBounds, /* currentDragBounds */
-            Rect(0, 50, 2000, 2000) /* validDragArea */,
-            Rect() /* dragStartBounds */,
+            position = Point(100, 200),
+            inputCoordinate = PointF(200f, 300f),
+            currentDragBounds = currentDragBounds,
+            validDragArea = Rect(0, 50, 2000, 2000),
+            dragStartBounds = Rect(),
             motionEvent,
             desktopWindowDecoration,
         )
@@ -3459,11 +3451,11 @@
         spyController.onDragPositioningEnd(
             task,
             mockSurface,
-            Point(100, 50), /* position */
-            PointF(200f, 300f), /* inputCoordinate */
-            Rect(100, 50, 500, 1000), /* currentDragBounds */
-            Rect(0, 50, 2000, 2000) /* validDragArea */,
-            Rect() /* dragStartBounds */,
+            position = Point(100, 50),
+            inputCoordinate = PointF(200f, 300f),
+            currentDragBounds = Rect(100, 50, 500, 1000),
+            validDragArea = Rect(0, 50, 2000, 2000),
+            dragStartBounds = Rect(),
             motionEvent,
             desktopWindowDecoration,
         )
@@ -3498,11 +3490,11 @@
         spyController.onDragPositioningEnd(
             task,
             mockSurface,
-            Point(100, 50), /* position */
-            PointF(200f, 300f), /* inputCoordinate */
+            position = Point(100, 50),
+            inputCoordinate = PointF(200f, 300f),
             currentDragBounds,
-            Rect(0, 50, 2000, 2000) /* validDragArea */,
-            Rect() /* dragStartBounds */,
+            validDragArea = Rect(0, 50, 2000, 2000),
+            dragStartBounds = Rect(),
             motionEvent,
             desktopWindowDecoration,
         )
@@ -3555,11 +3547,11 @@
         spyController.onDragPositioningEnd(
             task,
             mockSurface,
-            Point(100, 50), /* position */
-            PointF(200f, 300f), /* inputCoordinate */
-            currentDragBounds, /* currentDragBounds */
-            Rect(0, 50, 2000, 2000) /* validDragArea */,
-            Rect() /* dragStartBounds */,
+            position = Point(100, 50),
+            inputCoordinate = PointF(200f, 300f),
+            currentDragBounds = currentDragBounds,
+            validDragArea = Rect(0, 50, 2000, 2000),
+            dragStartBounds = Rect(),
             motionEvent,
             desktopWindowDecoration,
         )
@@ -3604,7 +3596,7 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_ON_SYSTEM_USER)
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER)
     fun enterSplit_onlyVisibleNonMinimizedTask_removesWallpaperActivity() {
         val task1 = setUpFreeformTask()
         val task2 = setUpFreeformTask()
@@ -5053,7 +5045,7 @@
         task: RunningTaskInfo?,
         @WindowManager.TransitionType type: Int = TRANSIT_OPEN,
     ): TransitionRequestInfo {
-        return TransitionRequestInfo(type, task, null /* remoteTransition */)
+        return TransitionRequestInfo(type, task, /* remoteTransition= */ null)
     }
 
     private companion object {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
index 52602f2..c8214b3 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt
@@ -193,10 +193,10 @@
         desktopTasksLimiter
             .getTransitionObserver()
             .onTransitionReady(
-                Binder() /* transition */,
+                /* transition= */ Binder(),
                 TransitionInfoBuilder(TRANSIT_OPEN).addChange(TRANSIT_TO_BACK, task).build(),
-                StubTransaction() /* startTransaction */,
-                StubTransaction(), /* finishTransaction */
+                /* startTransaction= */ StubTransaction(),
+                /* finishTransaction= */ StubTransaction(),
             )
 
         assertThat(desktopTaskRepo.isMinimizedTask(taskId = task.taskId)).isFalse()
@@ -217,10 +217,10 @@
         desktopTasksLimiter
             .getTransitionObserver()
             .onTransitionReady(
-                taskTransition /* transition */,
+                /* transition= */ taskTransition,
                 TransitionInfoBuilder(TRANSIT_OPEN).addChange(TRANSIT_TO_BACK, task).build(),
-                StubTransaction() /* startTransaction */,
-                StubTransaction(), /* finishTransaction */
+                /* startTransaction= */ StubTransaction(),
+                /* finishTransaction= */ StubTransaction(),
             )
 
         assertThat(desktopTaskRepo.isMinimizedTask(taskId = task.taskId)).isFalse()
@@ -242,8 +242,8 @@
             .onTransitionReady(
                 transition,
                 TransitionInfoBuilder(TRANSIT_OPEN).build(),
-                StubTransaction() /* startTransaction */,
-                StubTransaction(), /* finishTransaction */
+                /* startTransaction= */ StubTransaction(),
+                /* finishTransaction= */ StubTransaction(),
             )
 
         assertThat(desktopTaskRepo.isMinimizedTask(taskId = task.taskId)).isFalse()
@@ -265,8 +265,8 @@
             .onTransitionReady(
                 transition,
                 TransitionInfoBuilder(TRANSIT_OPEN).build(),
-                StubTransaction() /* startTransaction */,
-                StubTransaction(), /* finishTransaction */
+                /* startTransaction= */ StubTransaction(),
+                /* finishTransaction= */ StubTransaction(),
             )
 
         assertThat(desktopTaskRepo.isMinimizedTask(taskId = task.taskId)).isTrue()
@@ -287,8 +287,8 @@
             .onTransitionReady(
                 transition,
                 TransitionInfoBuilder(TRANSIT_OPEN).addChange(TRANSIT_TO_BACK, task).build(),
-                StubTransaction() /* startTransaction */,
-                StubTransaction(), /* finishTransaction */
+                /* startTransaction= */ StubTransaction(),
+                /* finishTransaction= */ StubTransaction(),
             )
 
         assertThat(desktopTaskRepo.isMinimizedTask(taskId = task.taskId)).isTrue()
@@ -316,8 +316,8 @@
             .onTransitionReady(
                 transition,
                 TransitionInfo(TRANSIT_OPEN, TransitionInfo.FLAG_NONE).apply { addChange(change) },
-                StubTransaction() /* startTransaction */,
-                StubTransaction(), /* finishTransaction */
+                /* startTransaction= */ StubTransaction(),
+                /* finishTransaction= */ StubTransaction(),
             )
 
         assertThat(desktopTaskRepo.isMinimizedTask(taskId = task.taskId)).isTrue()
@@ -344,8 +344,8 @@
             .onTransitionReady(
                 newTransition,
                 TransitionInfoBuilder(TRANSIT_OPEN).addChange(TRANSIT_TO_BACK, task).build(),
-                StubTransaction() /* startTransaction */,
-                StubTransaction(), /* finishTransaction */
+                /* startTransaction= */ StubTransaction(),
+                /* finishTransaction= */ StubTransaction(),
             )
 
         assertThat(desktopTaskRepo.isMinimizedTask(taskId = task.taskId)).isTrue()
@@ -552,8 +552,8 @@
             .onTransitionReady(
                 transition,
                 TransitionInfoBuilder(TRANSIT_OPEN).addChange(TRANSIT_TO_BACK, task).build(),
-                StubTransaction() /* startTransaction */,
-                StubTransaction(), /* finishTransaction */
+                /* startTransaction= */ StubTransaction(),
+                /* finishTransaction= */ StubTransaction(),
             )
 
         desktopTasksLimiter.getTransitionObserver().onTransitionStarting(transition)
@@ -584,8 +584,8 @@
             .onTransitionReady(
                 transition,
                 TransitionInfoBuilder(TRANSIT_OPEN).addChange(TRANSIT_TO_BACK, task).build(),
-                StubTransaction() /* startTransaction */,
-                StubTransaction(), /* finishTransaction */
+                /* startTransaction= */ StubTransaction(),
+                /* finishTransaction= */ StubTransaction(),
             )
 
         desktopTasksLimiter.getTransitionObserver().onTransitionStarting(transition)
@@ -617,8 +617,8 @@
             .onTransitionReady(
                 mergedTransition,
                 TransitionInfoBuilder(TRANSIT_OPEN).addChange(TRANSIT_TO_BACK, task).build(),
-                StubTransaction() /* startTransaction */,
-                StubTransaction(), /* finishTransaction */
+                /* startTransaction= */ StubTransaction(),
+                /* finishTransaction= */ StubTransaction(),
             )
 
         desktopTasksLimiter.getTransitionObserver().onTransitionStarting(mergedTransition)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt
index d491d44..89ab65a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt
@@ -239,7 +239,7 @@
     }
 
     @Test
-    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_ON_SYSTEM_USER)
+    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WALLPAPER_ACTIVITY_FOR_SYSTEM_USER)
     fun closeLastTask_wallpaperTokenExists_wallpaperIsRemoved() {
         val mockTransition = Mockito.mock(IBinder::class.java)
         val task = createTaskInfo(1, WINDOWING_MODE_FREEFORM)
@@ -305,7 +305,7 @@
         type: Int = TRANSIT_TO_BACK,
         withWallpaper: Boolean = false,
     ): TransitionInfo {
-        return TransitionInfo(type, 0 /* flags */).apply {
+        return TransitionInfo(type, /* flags= */ 0).apply {
             addChange(
                 Change(mock(), mock()).apply {
                     mode = type
@@ -331,7 +331,7 @@
         task: RunningTaskInfo?,
         type: Int = TRANSIT_OPEN,
     ): TransitionInfo {
-        return TransitionInfo(TRANSIT_OPEN, 0 /* flags */).apply {
+        return TransitionInfo(TRANSIT_OPEN, /* flags= */ 0).apply {
             addChange(
                 Change(mock(), mock()).apply {
                     mode = TRANSIT_OPEN
@@ -344,7 +344,7 @@
     }
 
     private fun createCloseTransition(task: RunningTaskInfo?): TransitionInfo {
-        return TransitionInfo(TRANSIT_CLOSE, 0 /* flags */).apply {
+        return TransitionInfo(TRANSIT_CLOSE, /* flags= */ 0).apply {
             addChange(
                 Change(mock(), mock()).apply {
                     mode = TRANSIT_CLOSE
@@ -357,7 +357,7 @@
     }
 
     private fun createToBackTransition(task: RunningTaskInfo?): TransitionInfo {
-        return TransitionInfo(TRANSIT_TO_BACK, 0 /* flags */).apply {
+        return TransitionInfo(TRANSIT_TO_BACK, /* flags= */ 0).apply {
             addChange(
                 Change(mock(), mock()).apply {
                     mode = TRANSIT_TO_BACK
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 2216d54..341df02 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
@@ -677,7 +677,7 @@
     }
 
     private fun createTransitionInfo(type: Int, draggedTask: RunningTaskInfo): TransitionInfo {
-        return TransitionInfo(type, 0 /* flags */).apply {
+        return TransitionInfo(type, /* flags= */ 0).apply {
             addChange( // Home.
                 TransitionInfo.Change(mock(), homeTaskLeash).apply {
                     parent = null
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java
index e40bbad..32bb8bb 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/DragAndDropControllerTest.java
@@ -150,4 +150,25 @@
         mController.onDrag(dragLayout, event);
         verify(mDragAndDropListener, never()).onDragStarted();
     }
+
+    @Test
+    public void testOnDragStarted_withNoClipData() {
+        final View dragLayout = mock(View.class);
+        final Display display = mock(Display.class);
+        doReturn(display).when(dragLayout).getDisplay();
+        doReturn(DEFAULT_DISPLAY).when(display).getDisplayId();
+
+        final ClipData clipData = createAppClipData(MIMETYPE_APPLICATION_SHORTCUT);
+        final DragEvent event = mock(DragEvent.class);
+        doReturn(ACTION_DRAG_STARTED).when(event).getAction();
+        doReturn(null).when(event).getClipData();
+        doReturn(clipData.getDescription()).when(event).getClipDescription();
+
+        // Ensure there's a target so that onDrag will execute
+        mController.addDisplayDropTarget(0, mContext, mock(WindowManager.class),
+                mock(FrameLayout.class), mock(DragLayout.class));
+
+        // Verify the listener is called on a valid drag action.
+        mController.onDrag(dragLayout, event);
+    }
 }
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/SplitDragPolicyTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/SplitDragPolicyTest.java
index 0cf15ba..a284663 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/SplitDragPolicyTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/draganddrop/SplitDragPolicyTest.java
@@ -220,7 +220,7 @@
         setRunningTask(mHomeTask);
         DragSession dragSession = new DragSession(mActivityTaskManager,
                 mLandscapeDisplayLayout, data, 0 /* dragFlags */);
-        dragSession.initialize();
+        dragSession.initialize(false /* skipUpdateRunningTask */);
         mPolicy.start(dragSession, mLoggerSessionId);
         ArrayList<Target> targets = assertExactTargetTypes(
                 mPolicy.getTargets(mInsets), TYPE_FULLSCREEN);
@@ -235,7 +235,7 @@
         setRunningTask(mFullscreenAppTask);
         DragSession dragSession = new DragSession(mActivityTaskManager,
                 mLandscapeDisplayLayout, data, 0 /* dragFlags */);
-        dragSession.initialize();
+        dragSession.initialize(false /* skipUpdateRunningTask */);
         mPolicy.start(dragSession, mLoggerSessionId);
         ArrayList<Target> targets = assertExactTargetTypes(
                 mPolicy.getTargets(mInsets), TYPE_SPLIT_LEFT, TYPE_SPLIT_RIGHT);
@@ -255,7 +255,7 @@
         setRunningTask(mFullscreenAppTask);
         DragSession dragSession = new DragSession(mActivityTaskManager,
                 mPortraitDisplayLayout, data, 0 /* dragFlags */);
-        dragSession.initialize();
+        dragSession.initialize(false /* skipUpdateRunningTask */);
         mPolicy.start(dragSession, mLoggerSessionId);
         ArrayList<Target> targets = assertExactTargetTypes(
                 mPolicy.getTargets(mInsets), TYPE_SPLIT_TOP, TYPE_SPLIT_BOTTOM);
@@ -276,7 +276,7 @@
         setRunningTask(mFullscreenAppTask);
         DragSession dragSession = new DragSession(mActivityTaskManager,
                 mLandscapeDisplayLayout, mActivityClipData, 0 /* dragFlags */);
-        dragSession.initialize();
+        dragSession.initialize(false /* skipUpdateRunningTask */);
         mPolicy.start(dragSession, mLoggerSessionId);
         ArrayList<Target> targets = mPolicy.getTargets(mInsets);
         for (Target t : targets) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java
index 6d37ed7..53f6cda 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipAnimationControllerTest.java
@@ -189,7 +189,7 @@
         // Apply fraction 1 to compute the end value.
         animator.applySurfaceControlTransaction(mLeash, tx, 1);
 
-        assertEquals("Expect use main window frame", mTaskInfo.topActivityMainWindowFrame,
+        assertEquals("Expect main window frame", mTaskInfo.topActivityMainWindowFrame,
                 animator.mCurrentValue);
 
         // PiP to fullscreen.
@@ -200,9 +200,11 @@
                 endBounds, null, TRANSITION_DIRECTION_TO_PIP, 0, ROTATION_270,
                 false /* alwaysAnimateTaskBounds */);
         animator.applySurfaceControlTransaction(mLeash, tx, 1);
+        final Rect rotatedEndBounds = new Rect(endBounds);
+        rotateBounds(rotatedEndBounds, startBounds, ROTATION_270);
 
-        assertEquals("Expect use main window frame", mTaskInfo.topActivityMainWindowFrame,
-                animator.mCurrentValue);
+        assertEquals("Expect rotated bounds. We only use main window frame for "
+                + "leave-pip animation", rotatedEndBounds, animator.mCurrentValue);
     }
 
     @Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedTaskInfoTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedTaskInfoTest.kt
index fd3adab..3209664 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedTaskInfoTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedTaskInfoTest.kt
@@ -40,6 +40,7 @@
 
 /**
  * Tests for [GroupedTaskInfo]
+ * Build & Run: atest WMShellUnitTests:GroupedTaskInfoTest
  */
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
@@ -47,7 +48,7 @@
 
     @Test
     fun testSingleTask_hasCorrectType() {
-        assertThat(singleTaskGroupInfo().type).isEqualTo(TYPE_FULLSCREEN)
+        assertThat(singleTaskGroupInfo().isBaseType(TYPE_FULLSCREEN)).isTrue()
     }
 
     @Test
@@ -66,7 +67,7 @@
 
     @Test
     fun testSplitTasks_hasCorrectType() {
-        assertThat(splitTasksGroupInfo().type).isEqualTo(TYPE_SPLIT)
+        assertThat(splitTasksGroupInfo().isBaseType(TYPE_SPLIT)).isTrue()
     }
 
     @Test
@@ -87,8 +88,8 @@
 
     @Test
     fun testFreeformTasks_hasCorrectType() {
-        assertThat(freeformTasksGroupInfo(freeformTaskIds = arrayOf(1)).type)
-            .isEqualTo(TYPE_FREEFORM)
+        assertThat(freeformTasksGroupInfo(freeformTaskIds = arrayOf(1)).isBaseType(TYPE_FREEFORM))
+            .isTrue()
     }
 
     @Test
@@ -111,83 +112,155 @@
     }
 
     @Test
+    fun testMixedWithFullscreenBase_hasCorrectType() {
+        assertThat(mixedTaskGroupInfoWithFullscreenBase().isBaseType(TYPE_FULLSCREEN)).isTrue()
+    }
+
+    @Test
+    fun testMixedWithSplitBase_hasCorrectType() {
+        assertThat(mixedTaskGroupInfoWithSplitBase().isBaseType(TYPE_SPLIT)).isTrue()
+    }
+
+    @Test
+    fun testMixedWithFreeformBase_hasCorrectType() {
+        assertThat(mixedTaskGroupInfoWithFreeformBase().isBaseType(TYPE_FREEFORM)).isTrue()
+    }
+
+    @Test
+    fun testMixed_disallowEmptyMixed() {
+        assertThrows(IllegalArgumentException::class.java) {
+            GroupedTaskInfo.forMixed(listOf())
+        }
+    }
+
+    @Test
+    fun testMixed_disallowNestedMixed() {
+        assertThrows(IllegalArgumentException::class.java) {
+            GroupedTaskInfo.forMixed(listOf(
+                GroupedTaskInfo.forMixed(listOf(singleTaskGroupInfo()))))
+        }
+    }
+
+    @Test
+    fun testMixed_disallowNonMixedAccessors() {
+        val mixed = mixedTaskGroupInfoWithFullscreenBase()
+        assertThrows(IllegalStateException::class.java) {
+            mixed.taskInfo1
+        }
+        assertThrows(IllegalStateException::class.java) {
+            mixed.taskInfo2
+        }
+        assertThrows(IllegalStateException::class.java) {
+            mixed.splitBounds
+        }
+        assertThrows(IllegalStateException::class.java) {
+            mixed.minimizedTaskIds
+        }
+    }
+
+    @Test
     fun testParcelling_singleTask() {
-        val recentTaskInfo = singleTaskGroupInfo()
+        val taskInfo = singleTaskGroupInfo()
         val parcel = Parcel.obtain()
-        recentTaskInfo.writeToParcel(parcel, 0)
+        taskInfo.writeToParcel(parcel, 0)
         parcel.setDataPosition(0)
         // Read the object back from the parcel
-        val recentTaskInfoParcel: GroupedTaskInfo =
+        val taskInfoFromParcel: GroupedTaskInfo =
             GroupedTaskInfo.CREATOR.createFromParcel(parcel)
-        assertThat(recentTaskInfoParcel.type).isEqualTo(TYPE_FULLSCREEN)
-        assertThat(recentTaskInfoParcel.taskInfo1.taskId).isEqualTo(1)
-        assertThat(recentTaskInfoParcel.taskInfo2).isNull()
+        assertThat(taskInfoFromParcel.isBaseType(TYPE_FULLSCREEN)).isTrue()
+        assertThat(taskInfoFromParcel.taskInfo1.taskId).isEqualTo(1)
+        assertThat(taskInfoFromParcel.taskInfo2).isNull()
     }
 
     @Test
     fun testParcelling_splitTasks() {
-        val recentTaskInfo = splitTasksGroupInfo()
+        val taskInfo = splitTasksGroupInfo()
         val parcel = Parcel.obtain()
-        recentTaskInfo.writeToParcel(parcel, 0)
+        taskInfo.writeToParcel(parcel, 0)
         parcel.setDataPosition(0)
         // Read the object back from the parcel
-        val recentTaskInfoParcel: GroupedTaskInfo =
+        val taskInfoFromParcel: GroupedTaskInfo =
             GroupedTaskInfo.CREATOR.createFromParcel(parcel)
-        assertThat(recentTaskInfoParcel.type).isEqualTo(TYPE_SPLIT)
-        assertThat(recentTaskInfoParcel.taskInfo1.taskId).isEqualTo(1)
-        assertThat(recentTaskInfoParcel.taskInfo2).isNotNull()
-        assertThat(recentTaskInfoParcel.taskInfo2!!.taskId).isEqualTo(2)
-        assertThat(recentTaskInfoParcel.splitBounds).isNotNull()
-        assertThat(recentTaskInfoParcel.splitBounds!!.snapPosition).isEqualTo(SNAP_TO_2_50_50)
+        assertThat(taskInfoFromParcel.isBaseType(TYPE_SPLIT)).isTrue()
+        assertThat(taskInfoFromParcel.taskInfo1.taskId).isEqualTo(1)
+        assertThat(taskInfoFromParcel.taskInfo2).isNotNull()
+        assertThat(taskInfoFromParcel.taskInfo2!!.taskId).isEqualTo(2)
+        assertThat(taskInfoFromParcel.splitBounds).isNotNull()
+        assertThat(taskInfoFromParcel.splitBounds!!.snapPosition).isEqualTo(SNAP_TO_2_50_50)
     }
 
     @Test
     fun testParcelling_freeformTasks() {
-        val recentTaskInfo = freeformTasksGroupInfo(freeformTaskIds = arrayOf(1, 2, 3))
+        val taskInfo = freeformTasksGroupInfo(freeformTaskIds = arrayOf(1, 2, 3))
         val parcel = Parcel.obtain()
-        recentTaskInfo.writeToParcel(parcel, 0)
+        taskInfo.writeToParcel(parcel, 0)
         parcel.setDataPosition(0)
         // Read the object back from the parcel
-        val recentTaskInfoParcel: GroupedTaskInfo =
+        val taskInfoFromParcel: GroupedTaskInfo =
             GroupedTaskInfo.CREATOR.createFromParcel(parcel)
-        assertThat(recentTaskInfoParcel.type).isEqualTo(TYPE_FREEFORM)
-        assertThat(recentTaskInfoParcel.taskInfoList).hasSize(3)
+        assertThat(taskInfoFromParcel.isBaseType(TYPE_FREEFORM)).isTrue()
+        assertThat(taskInfoFromParcel.taskInfoList).hasSize(3)
         // Only compare task ids
         val taskIdComparator = Correspondence.transforming<TaskInfo, Int>(
             { it?.taskId }, "has taskId of"
         )
-        assertThat(recentTaskInfoParcel.taskInfoList).comparingElementsUsing(taskIdComparator)
-            .containsExactly(1, 2, 3)
+        assertThat(taskInfoFromParcel.taskInfoList).comparingElementsUsing(taskIdComparator)
+            .containsExactly(1, 2, 3).inOrder()
     }
 
     @Test
     fun testParcelling_freeformTasks_minimizedTasks() {
-        val recentTaskInfo = freeformTasksGroupInfo(
+        val taskInfo = freeformTasksGroupInfo(
             freeformTaskIds = arrayOf(1, 2, 3), minimizedTaskIds = arrayOf(2))
 
         val parcel = Parcel.obtain()
-        recentTaskInfo.writeToParcel(parcel, 0)
+        taskInfo.writeToParcel(parcel, 0)
         parcel.setDataPosition(0)
 
         // Read the object back from the parcel
-        val recentTaskInfoParcel: GroupedTaskInfo =
+        val taskInfoFromParcel: GroupedTaskInfo =
             GroupedTaskInfo.CREATOR.createFromParcel(parcel)
-        assertThat(recentTaskInfoParcel.type).isEqualTo(TYPE_FREEFORM)
-        assertThat(recentTaskInfoParcel.minimizedTaskIds).isEqualTo(arrayOf(2).toIntArray())
+        assertThat(taskInfoFromParcel.isBaseType(TYPE_FREEFORM)).isTrue()
+        assertThat(taskInfoFromParcel.minimizedTaskIds).isEqualTo(arrayOf(2).toIntArray())
     }
 
     @Test
-    fun testGetTaskById_singleTasks() {
+    fun testParcelling_mixedTasks() {
+        val taskInfo = GroupedTaskInfo.forMixed(listOf(
+                freeformTasksGroupInfo(freeformTaskIds = arrayOf(4, 5, 6),
+                    minimizedTaskIds = arrayOf(5)),
+                splitTasksGroupInfo(firstId = 2, secondId = 3),
+                singleTaskGroupInfo(id = 1)))
+
+        val parcel = Parcel.obtain()
+        taskInfo.writeToParcel(parcel, 0)
+        parcel.setDataPosition(0)
+
+        // Read the object back from the parcel
+        val taskInfoFromParcel: GroupedTaskInfo =
+            GroupedTaskInfo.CREATOR.createFromParcel(parcel)
+        assertThat(taskInfoFromParcel.isBaseType(TYPE_FREEFORM)).isTrue()
+        assertThat(taskInfoFromParcel.baseGroupedTask.minimizedTaskIds).isEqualTo(
+            arrayOf(5).toIntArray())
+        for (i in 1..6) {
+            assertThat(taskInfoFromParcel.containsTask(i)).isTrue()
+        }
+        assertThat(taskInfoFromParcel.taskInfoList).hasSize(taskInfo.taskInfoList.size)
+    }
+
+    @Test
+    fun testTaskProperties_singleTasks() {
         val task1 = createTaskInfo(id = 1234)
 
         val taskInfo = GroupedTaskInfo.forFullscreenTasks(task1)
 
         assertThat(taskInfo.getTaskById(1234)).isEqualTo(task1)
         assertThat(taskInfo.containsTask(1234)).isTrue()
+        assertThat(taskInfo.taskInfoList).isEqualTo(listOf(task1))
     }
 
     @Test
-    fun testGetTaskById_multipleTasks() {
+    fun testTaskProperties_splitTasks() {
         val task1 = createTaskInfo(id = 1)
         val task2 = createTaskInfo(id = 2)
         val splitBounds = SplitBounds(Rect(), Rect(), 1, 2, SNAP_TO_2_50_50)
@@ -198,6 +271,41 @@
         assertThat(taskInfo.getTaskById(2)).isEqualTo(task2)
         assertThat(taskInfo.containsTask(1)).isTrue()
         assertThat(taskInfo.containsTask(2)).isTrue()
+        assertThat(taskInfo.taskInfoList).isEqualTo(listOf(task1, task2))
+    }
+
+    @Test
+    fun testTaskProperties_freeformTasks() {
+        val task1 = createTaskInfo(id = 1)
+        val task2 = createTaskInfo(id = 2)
+
+        val taskInfo = GroupedTaskInfo.forFreeformTasks(listOf(task1, task2), setOf())
+
+        assertThat(taskInfo.getTaskById(1)).isEqualTo(task1)
+        assertThat(taskInfo.getTaskById(2)).isEqualTo(task2)
+        assertThat(taskInfo.containsTask(1)).isTrue()
+        assertThat(taskInfo.containsTask(2)).isTrue()
+        assertThat(taskInfo.taskInfoList).isEqualTo(listOf(task1, task2))
+    }
+
+    @Test
+    fun testTaskProperties_mixedTasks() {
+        val task1 = createTaskInfo(id = 1)
+        val task2 = createTaskInfo(id = 2)
+        val task3 = createTaskInfo(id = 3)
+        val splitBounds = SplitBounds(Rect(), Rect(), 1, 2, SNAP_TO_2_50_50)
+
+        val splitTasks = GroupedTaskInfo.forSplitTasks(task1, task2, splitBounds)
+        val fullscreenTasks = GroupedTaskInfo.forFullscreenTasks(task3)
+        val mixedTasks = GroupedTaskInfo.forMixed(listOf(splitTasks, fullscreenTasks))
+
+        assertThat(mixedTasks.getTaskById(1)).isEqualTo(task1)
+        assertThat(mixedTasks.getTaskById(2)).isEqualTo(task2)
+        assertThat(mixedTasks.getTaskById(3)).isEqualTo(task3)
+        assertThat(mixedTasks.containsTask(1)).isTrue()
+        assertThat(mixedTasks.containsTask(2)).isTrue()
+        assertThat(mixedTasks.containsTask(3)).isTrue()
+        assertThat(mixedTasks.taskInfoList).isEqualTo(listOf(task1, task2, task3))
     }
 
     private fun createTaskInfo(id: Int) = ActivityManager.RecentTaskInfo().apply {
@@ -205,14 +313,14 @@
         token = WindowContainerToken(mock(IWindowContainerToken::class.java))
     }
 
-    private fun singleTaskGroupInfo(): GroupedTaskInfo {
-        val task = createTaskInfo(id = 1)
+    private fun singleTaskGroupInfo(id: Int = 1): GroupedTaskInfo {
+        val task = createTaskInfo(id)
         return GroupedTaskInfo.forFullscreenTasks(task)
     }
 
-    private fun splitTasksGroupInfo(): GroupedTaskInfo {
-        val task1 = createTaskInfo(id = 1)
-        val task2 = createTaskInfo(id = 2)
+    private fun splitTasksGroupInfo(firstId: Int = 1, secondId: Int = 2): GroupedTaskInfo {
+        val task1 = createTaskInfo(firstId)
+        val task2 = createTaskInfo(secondId)
         val splitBounds = SplitBounds(Rect(), Rect(), 1, 2, SNAP_TO_2_50_50)
         return GroupedTaskInfo.forSplitTasks(task1, task2, splitBounds)
     }
@@ -225,4 +333,22 @@
             freeformTaskIds.map { createTaskInfo(it) }.toList(),
             minimizedTaskIds.toSet())
     }
+
+    private fun mixedTaskGroupInfoWithFullscreenBase(): GroupedTaskInfo {
+        return GroupedTaskInfo.forMixed(listOf(
+            singleTaskGroupInfo(id = 1),
+            singleTaskGroupInfo(id = 2)))
+    }
+
+    private fun mixedTaskGroupInfoWithSplitBase(): GroupedTaskInfo {
+        return GroupedTaskInfo.forMixed(listOf(
+            splitTasksGroupInfo(firstId = 2, secondId = 3),
+            singleTaskGroupInfo(id = 1)))
+    }
+
+    private fun mixedTaskGroupInfoWithFreeformBase(): GroupedTaskInfo {
+        return GroupedTaskInfo.forMixed(listOf(
+            freeformTasksGroupInfo(freeformTaskIds = arrayOf(2, 3, 4)),
+            singleTaskGroupInfo(id = 1)))
+    }
 }
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 22b45e8..7e5d6ce 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
@@ -24,6 +24,9 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
 import static com.android.launcher3.Flags.FLAG_ENABLE_USE_TOP_VISIBLE_ACTIVITY_FOR_EXCLUDE_FROM_RECENT_TASK;
 import static com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE;
+import static com.android.wm.shell.shared.GroupedTaskInfo.TYPE_FREEFORM;
+import static com.android.wm.shell.shared.GroupedTaskInfo.TYPE_FULLSCREEN;
+import static com.android.wm.shell.shared.GroupedTaskInfo.TYPE_SPLIT;
 import static com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_50_50;
 
 import static org.junit.Assert.assertEquals;
@@ -346,9 +349,9 @@
         GroupedTaskInfo singleGroup2 = recentTasks.get(2);
 
         // Check that groups have expected types
-        assertEquals(GroupedTaskInfo.TYPE_FREEFORM, freeformGroup.getType());
-        assertEquals(GroupedTaskInfo.TYPE_FULLSCREEN, singleGroup1.getType());
-        assertEquals(GroupedTaskInfo.TYPE_FULLSCREEN, singleGroup2.getType());
+        assertTrue(freeformGroup.isBaseType(TYPE_FREEFORM));
+        assertTrue(singleGroup1.isBaseType(TYPE_FULLSCREEN));
+        assertTrue(singleGroup2.isBaseType(TYPE_FULLSCREEN));
 
         // Check freeform group entries
         assertEquals(t1, freeformGroup.getTaskInfoList().get(0));
@@ -385,9 +388,9 @@
         GroupedTaskInfo singleGroup = recentTasks.get(2);
 
         // Check that groups have expected types
-        assertEquals(GroupedTaskInfo.TYPE_SPLIT, splitGroup.getType());
-        assertEquals(GroupedTaskInfo.TYPE_FREEFORM, freeformGroup.getType());
-        assertEquals(GroupedTaskInfo.TYPE_FULLSCREEN, singleGroup.getType());
+        assertTrue(splitGroup.isBaseType(TYPE_SPLIT));
+        assertTrue(freeformGroup.isBaseType(TYPE_FREEFORM));
+        assertTrue(singleGroup.isBaseType(TYPE_FULLSCREEN));
 
         // Check freeform group entries
         assertEquals(t3, freeformGroup.getTaskInfoList().get(0));
@@ -420,10 +423,10 @@
 
         // Expect no grouping of tasks
         assertEquals(4, recentTasks.size());
-        assertEquals(GroupedTaskInfo.TYPE_FULLSCREEN, recentTasks.get(0).getType());
-        assertEquals(GroupedTaskInfo.TYPE_FULLSCREEN, recentTasks.get(1).getType());
-        assertEquals(GroupedTaskInfo.TYPE_FULLSCREEN, recentTasks.get(2).getType());
-        assertEquals(GroupedTaskInfo.TYPE_FULLSCREEN, recentTasks.get(3).getType());
+        assertTrue(recentTasks.get(0).isBaseType(TYPE_FULLSCREEN));
+        assertTrue(recentTasks.get(1).isBaseType(TYPE_FULLSCREEN));
+        assertTrue(recentTasks.get(2).isBaseType(TYPE_FULLSCREEN));
+        assertTrue(recentTasks.get(3).isBaseType(TYPE_FULLSCREEN));
 
         assertEquals(t1, recentTasks.get(0).getTaskInfo1());
         assertEquals(t2, recentTasks.get(1).getTaskInfo1());
@@ -457,9 +460,9 @@
         GroupedTaskInfo singleGroup2 = recentTasks.get(2);
 
         // Check that groups have expected types
-        assertEquals(GroupedTaskInfo.TYPE_FREEFORM, freeformGroup.getType());
-        assertEquals(GroupedTaskInfo.TYPE_FULLSCREEN, singleGroup1.getType());
-        assertEquals(GroupedTaskInfo.TYPE_FULLSCREEN, singleGroup2.getType());
+        assertTrue(freeformGroup.isBaseType(TYPE_FREEFORM));
+        assertTrue(singleGroup1.isBaseType(TYPE_FULLSCREEN));
+        assertTrue(singleGroup2.isBaseType(TYPE_FULLSCREEN));
 
         // Check freeform group entries
         assertEquals(3, freeformGroup.getTaskInfoList().size());
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 bca9c3f..bb9703f 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
@@ -106,7 +106,6 @@
     @Mock RootTaskDisplayAreaOrganizer mRootTDAOrganizer;
     @Mock ShellExecutor mMainExecutor;
     @Mock Handler mMainHandler;
-    @Mock ShellExecutor mBgExecutor;
     @Mock DisplayController mDisplayController;
     @Mock DisplayImeController mDisplayImeController;
     @Mock DisplayInsetsController mDisplayInsetsController;
@@ -138,8 +137,7 @@
                 mDisplayInsetsController, mDragAndDropController, mTransitions, mTransactionPool,
                 mIconProvider, Optional.of(mRecentTasks), mLaunchAdjacentController,
                 Optional.of(mWindowDecorViewModel), Optional.of(mDesktopTasksController),
-                mStageCoordinator, mMultiInstanceHelper, mSplitState, mMainExecutor, mMainHandler,
-                mBgExecutor));
+                mStageCoordinator, mMultiInstanceHelper, mSplitState, mMainExecutor, mMainHandler));
     }
 
     @Test
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 ada7b4af..ae0c9d6 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
@@ -79,15 +79,15 @@
                 StageTaskListener sideStage, DisplayController displayController,
                 DisplayImeController imeController, DisplayInsetsController insetsController,
                 SplitLayout splitLayout, Transitions transitions, TransactionPool transactionPool,
-                ShellExecutor mainExecutor, Handler mainHandler, ShellExecutor bgExecutor,
+                ShellExecutor mainExecutor, Handler mainHandler,
                 Optional<RecentTasksController> recentTasks,
                 LaunchAdjacentController launchAdjacentController,
                 Optional<WindowDecorViewModel> windowDecorViewModel, SplitState splitState,
                 Optional<DesktopTasksController> desktopTasksController) {
             super(context, displayId, syncQueue, taskOrganizer, mainStage,
                     sideStage, displayController, imeController, insetsController, splitLayout,
-                    transitions, transactionPool, mainExecutor, mainHandler, bgExecutor,
-                    recentTasks, launchAdjacentController, windowDecorViewModel, splitState,
+                    transitions, transactionPool, mainExecutor, mainHandler, recentTasks,
+                    launchAdjacentController, windowDecorViewModel, splitState,
                     desktopTasksController);
 
             // Prepare root task for testing.
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 ffa8b60..b0fdfac 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
@@ -111,7 +111,6 @@
     @Mock private WindowDecorViewModel mWindowDecorViewModel;
     @Mock private SplitState mSplitState;
     @Mock private ShellExecutor mMainExecutor;
-    @Mock private ShellExecutor mBgExecutor;
     @Mock private Handler mMainHandler;
     @Mock private LaunchAdjacentController mLaunchAdjacentController;
     @Mock private DefaultMixedHandler mMixedHandler;
@@ -137,19 +136,18 @@
         mSplitLayout = SplitTestUtils.createMockSplitLayout();
         mMainStage = spy(new StageTaskListener(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock(
                 StageTaskListener.StageListenerCallbacks.class), mSyncQueue,
-                mIconProvider, mMainExecutor, mBgExecutor, Optional.of(mWindowDecorViewModel),
-                STAGE_TYPE_MAIN));
+                mIconProvider, Optional.of(mWindowDecorViewModel), STAGE_TYPE_MAIN));
         mMainStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface());
         mSideStage = spy(new StageTaskListener(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock(
                 StageTaskListener.StageListenerCallbacks.class), mSyncQueue,
-                mIconProvider, mMainExecutor, mBgExecutor, Optional.of(mWindowDecorViewModel),
-                STAGE_TYPE_SIDE));
+                mIconProvider, Optional.of(mWindowDecorViewModel), STAGE_TYPE_SIDE));
         mSideStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface());
         mStageCoordinator = new SplitTestUtils.TestStageCoordinator(mContext, DEFAULT_DISPLAY,
                 mSyncQueue, mTaskOrganizer, mMainStage, mSideStage, mDisplayController,
                 mDisplayImeController, mDisplayInsetsController, mSplitLayout, mTransitions,
-                mTransactionPool, mMainExecutor, mMainHandler, mBgExecutor, Optional.empty(),
+                mTransactionPool, mMainExecutor, mMainHandler, Optional.empty(),
                 mLaunchAdjacentController, Optional.empty(), mSplitState, Optional.empty());
+
         mStageCoordinator.setMixedHandler(mMixedHandler);
         mSplitScreenTransitions = mStageCoordinator.getSplitTransitions();
         doAnswer((Answer<IBinder>) invocation -> mock(IBinder.class))
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 9d1df86..2d0ea5f 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
@@ -119,8 +119,6 @@
     private DefaultMixedHandler mDefaultMixedHandler;
     @Mock
     private SplitState mSplitState;
-    @Mock
-    private ShellExecutor mBgExecutor;
 
     private final Rect mBounds1 = new Rect(10, 20, 30, 40);
     private final Rect mBounds2 = new Rect(5, 10, 15, 20);
@@ -143,9 +141,9 @@
         mStageCoordinator = spy(new StageCoordinator(mContext, DEFAULT_DISPLAY, mSyncQueue,
                 mTaskOrganizer, mMainStage, mSideStage, mDisplayController, mDisplayImeController,
                 mDisplayInsetsController, mSplitLayout, mTransitions, mTransactionPool,
-                mMainExecutor, mMainHandler, mBgExecutor, Optional.empty(),
-                mLaunchAdjacentController, Optional.empty(), mSplitState,
-                Optional.empty()));
+                mMainExecutor, mMainHandler, Optional.empty(), mLaunchAdjacentController,
+                Optional.empty(), mSplitState, Optional.empty()));
+
         mDividerLeash = new SurfaceControl.Builder().setName("fakeDivider").build();
 
         when(mSplitLayout.getTopLeftBounds()).thenReturn(mBounds1);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageOrderOperatorTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageOrderOperatorTests.kt
index 62b830d..3b4a86a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageOrderOperatorTests.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageOrderOperatorTests.kt
@@ -23,7 +23,6 @@
 import com.android.wm.shell.Flags.enableFlexibleSplit
 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.SyncTransactionQueue
 import com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_33_66
 import com.android.wm.shell.shared.split.SplitScreenConstants.SNAP_TO_2_50_50
@@ -42,10 +41,6 @@
 class StageOrderOperatorTests : ShellTestCase() {
 
     @Mock
-    lateinit var mMainExecutor: ShellExecutor
-    @Mock
-    lateinit var mBgExecutor: ShellExecutor
-    @Mock
     lateinit var mTaskOrganizer: ShellTaskOrganizer
     @Mock
     lateinit var mSyncQueue: SyncTransactionQueue
@@ -67,8 +62,6 @@
             stageListenerCallbacks,
             mSyncQueue,
             iconProvider,
-            mMainExecutor,
-            mBgExecutor,
             windowDecorViewModel,
             )
         assert(stageOrderOperator.activeStages.size == 0)
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 effc6a7..fe91440 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
@@ -43,7 +43,6 @@
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.ShellTestCase;
 import com.android.wm.shell.TestRunningTaskInfoBuilder;
-import com.android.wm.shell.common.ShellExecutor;
 import com.android.wm.shell.common.SyncTransactionQueue;
 import com.android.wm.shell.windowdecor.WindowDecorViewModel;
 
@@ -74,10 +73,6 @@
     @Mock
     private SyncTransactionQueue mSyncQueue;
     @Mock
-    private ShellExecutor mMainExecutor;
-    @Mock
-    private ShellExecutor mBgExecutor;
-    @Mock
     private IconProvider mIconProvider;
     @Mock
     private WindowDecorViewModel mWindowDecorViewModel;
@@ -100,8 +95,6 @@
                 mCallbacks,
                 mSyncQueue,
                 mIconProvider,
-                mMainExecutor,
-                mBgExecutor,
                 Optional.of(mWindowDecorViewModel),
                 STAGE_TYPE_UNDEFINED);
         mRootTask = new TestRunningTaskInfoBuilder().build();
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 8bdefa7..3e53ee5 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
@@ -39,8 +39,6 @@
 import android.os.IBinder;
 import android.os.Looper;
 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.view.SurfaceControl;
@@ -51,7 +49,6 @@
 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;
@@ -197,27 +194,6 @@
     }
 
     @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);
-        ActivityManager.RunningTaskInfo taskInfo = mock(ActivityManager.RunningTaskInfo.class);
-        when(change.getTaskInfo()).thenReturn(taskInfo);
-        when(info.getChanges()).thenReturn(new ArrayList<>(List.of(change)));
-
-        when(change.hasFlags(FLAG_BACK_GESTURE_ANIMATED)).thenReturn(true);
-        setupTransitionInfo(taskInfo, change, ACTIVITY_TYPE_HOME, TRANSIT_CHANGE, true);
-
-        mHomeTransitionObserver.onTransitionReady(mock(IBinder.class),
-                info,
-                mock(SurfaceControl.Transaction.class),
-                mock(SurfaceControl.Transaction.class));
-
-        verify(mListener, times(1)).onHomeVisibilityChanged(true);
-    }
-
-    @Test
-    @RequiresFlagsEnabled(Flags.FLAG_MIGRATE_PREDICTIVE_BACK_TRANSITION)
     public void testHomeActivityWithBackGestureNotifiesHomeIsVisibleAfterClose()
             throws RemoteException {
         TransitionInfo info = mock(TransitionInfo.class);
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 7dac085..6b02aef 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
@@ -20,7 +20,6 @@
 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 static android.app.assist.AssistContent.EXTRA_SESSION_TRANSFER_WEB_URI;
 import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
 import static android.view.InsetsSource.FLAG_FORCE_CONSUMING;
 import static android.view.InsetsSource.FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR;
@@ -1176,7 +1175,7 @@
 
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB)
-    public void webUriLink_webUriLinkUsedWhenWhenAvailable() {
+    public void sessionTransferUri_sessionTransferUriUsedWhenWhenAvailable() {
         final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
         final DesktopModeWindowDecoration decor = createWindowDecoration(
                 taskInfo, TEST_URI1 /* captured link */, TEST_URI2 /* web uri */,
@@ -1188,7 +1187,7 @@
 
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB)
-    public void webUriLink_webUriLinkUsedWhenSessionTransferUriUnavailable() {
+    public void webUri_webUriUsedWhenSessionTransferUriUnavailable() {
         final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
         final DesktopModeWindowDecoration decor = createWindowDecoration(
                 taskInfo, TEST_URI1 /* captured link */, TEST_URI2 /* web uri */,
@@ -1200,7 +1199,7 @@
 
     @Test
     @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB)
-    public void genericLink_genericLinkUsedWhenCapturedLinkAndWebUriUnavailable() {
+    public void genericLink_genericLinkUsedWhenCapturedLinkAndAssistContentUriUnavailable() {
         final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
         final DesktopModeWindowDecoration decor = createWindowDecoration(
                 taskInfo, null /* captured link */, null /* web uri */,
@@ -1490,7 +1489,7 @@
         taskInfo.capturedLink = capturedLink;
         taskInfo.capturedLinkTimestamp = System.currentTimeMillis();
         mAssistContent.setWebUri(webUri);
-        mAssistContent.getExtras().putObject(EXTRA_SESSION_TRANSFER_WEB_URI, sessionTransferUri);
+        mAssistContent.setSessionTransferUri(sessionTransferUri);
         final String genericLinkString = genericLink == null ? null : genericLink.toString();
         doReturn(genericLinkString).when(mMockGenericLinksParser).getGenericLink(any());
         // Relayout to set captured link
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index cc4a29b..12b1dd7 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -80,6 +80,7 @@
         "LoadedArsc.cpp",
         "Locale.cpp",
         "LocaleData.cpp",
+        "LocaleDataLookup.cpp",
         "misc.cpp",
         "NinePatch.cpp",
         "ObbFile.cpp",
@@ -225,6 +226,7 @@
         "tests/Idmap_test.cpp",
         "tests/LoadedArsc_test.cpp",
         "tests/Locale_test.cpp",
+        "tests/LocaleDataLookup_test.cpp",
         "tests/NinePatch_test.cpp",
         "tests/ResourceTimer_test.cpp",
         "tests/ResourceUtils_test.cpp",
diff --git a/libs/androidfw/ApkAssets.cpp b/libs/androidfw/ApkAssets.cpp
index dbb8914..e693fcf 100644
--- a/libs/androidfw/ApkAssets.cpp
+++ b/libs/androidfw/ApkAssets.cpp
@@ -162,10 +162,13 @@
   return assets_provider_->GetDebugName();
 }
 
-bool ApkAssets::IsUpToDate() const {
+UpToDate ApkAssets::IsUpToDate() const {
   // Loaders are invalidated by the app, not the system, so assume they are up to date.
-  return IsLoader() || ((!loaded_idmap_ || loaded_idmap_->IsUpToDate())
-                        && assets_provider_->IsUpToDate());
+  if (IsLoader()) {
+    return UpToDate::Always;
+  }
+  const auto idmap_res = loaded_idmap_ ? loaded_idmap_->IsUpToDate() : UpToDate::Always;
+  return combine(idmap_res, [this] { return assets_provider_->IsUpToDate(); });
 }
 
 }  // namespace android
diff --git a/libs/androidfw/AssetsProvider.cpp b/libs/androidfw/AssetsProvider.cpp
index 2d3c065..11b12eb 100644
--- a/libs/androidfw/AssetsProvider.cpp
+++ b/libs/androidfw/AssetsProvider.cpp
@@ -24,9 +24,8 @@
 #include <ziparchive/zip_archive.h>
 
 namespace android {
-namespace {
-constexpr const char* kEmptyDebugString = "<empty>";
-} // namespace
+
+static constexpr std::string_view kEmptyDebugString = "<empty>";
 
 std::unique_ptr<Asset> AssetsProvider::Open(const std::string& path, Asset::AccessMode mode,
                                             bool* file_exists) const {
@@ -86,11 +85,9 @@
 }
 
 ZipAssetsProvider::ZipAssetsProvider(ZipArchiveHandle handle, PathOrDebugName&& path,
-                                     package_property_t flags, time_t last_mod_time)
-    : zip_handle_(handle),
-      name_(std::move(path)),
-      flags_(flags),
-      last_mod_time_(last_mod_time) {}
+                                     package_property_t flags, ModDate last_mod_time)
+    : zip_handle_(handle), name_(std::move(path)), flags_(flags), last_mod_time_(last_mod_time) {
+}
 
 std::unique_ptr<ZipAssetsProvider> ZipAssetsProvider::Create(std::string path,
                                                              package_property_t flags,
@@ -104,10 +101,10 @@
     return {};
   }
 
-  struct stat sb{.st_mtime = -1};
+  ModDate mod_date = kInvalidModDate;
   // Skip all up-to-date checks if the file won't ever change.
-  if (!isReadonlyFilesystem(path.c_str())) {
-    if ((released_fd < 0 ? stat(path.c_str(), &sb) : fstat(released_fd, &sb)) < 0) {
+  if (isKnownWritablePath(path.c_str()) || !isReadonlyFilesystem(GetFileDescriptor(handle))) {
+    if (mod_date = getFileModDate(GetFileDescriptor(handle)); mod_date == kInvalidModDate) {
       // Stat requires execute permissions on all directories path to the file. If the process does
       // not have execute permissions on this file, allow the zip to be opened but IsUpToDate() will
       // always have to return true.
@@ -116,7 +113,7 @@
   }
 
   return std::unique_ptr<ZipAssetsProvider>(
-      new ZipAssetsProvider(handle, PathOrDebugName::Path(std::move(path)), flags, sb.st_mtime));
+      new ZipAssetsProvider(handle, PathOrDebugName::Path(std::move(path)), flags, mod_date));
 }
 
 std::unique_ptr<ZipAssetsProvider> ZipAssetsProvider::Create(base::unique_fd fd,
@@ -137,10 +134,10 @@
     return {};
   }
 
-  struct stat sb{.st_mtime = -1};
+  ModDate mod_date = kInvalidModDate;
   // Skip all up-to-date checks if the file won't ever change.
   if (!isReadonlyFilesystem(released_fd)) {
-    if (fstat(released_fd, &sb) < 0) {
+    if (mod_date = getFileModDate(released_fd); mod_date == kInvalidModDate) {
       // Stat requires execute permissions on all directories path to the file. If the process does
       // not have execute permissions on this file, allow the zip to be opened but IsUpToDate() will
       // always have to return true.
@@ -150,7 +147,7 @@
   }
 
   return std::unique_ptr<ZipAssetsProvider>(new ZipAssetsProvider(
-      handle, PathOrDebugName::DebugName(std::move(friendly_name)), flags, sb.st_mtime));
+      handle, PathOrDebugName::DebugName(std::move(friendly_name)), flags, mod_date));
 }
 
 std::unique_ptr<Asset> ZipAssetsProvider::OpenInternal(const std::string& path,
@@ -282,21 +279,16 @@
   return name_.GetDebugName();
 }
 
-bool ZipAssetsProvider::IsUpToDate() const {
-  if (last_mod_time_ == -1) {
-    return true;
+UpToDate ZipAssetsProvider::IsUpToDate() const {
+  if (last_mod_time_ == kInvalidModDate) {
+    return UpToDate::Always;
   }
-  struct stat sb{};
-  if (fstat(GetFileDescriptor(zip_handle_.get()), &sb) < 0) {
-    // If fstat fails on the zip archive, return true so the zip archive the resource system does
-    // attempt to refresh the ApkAsset.
-    return true;
-  }
-  return last_mod_time_ == sb.st_mtime;
+  return fromBool(last_mod_time_ == getFileModDate(GetFileDescriptor(zip_handle_.get())));
 }
 
-DirectoryAssetsProvider::DirectoryAssetsProvider(std::string&& path, time_t last_mod_time)
-    : dir_(std::move(path)), last_mod_time_(last_mod_time) {}
+DirectoryAssetsProvider::DirectoryAssetsProvider(std::string&& path, ModDate last_mod_time)
+    : dir_(std::move(path)), last_mod_time_(last_mod_time) {
+}
 
 std::unique_ptr<DirectoryAssetsProvider> DirectoryAssetsProvider::Create(std::string path) {
   struct stat sb;
@@ -317,7 +309,7 @@
 
   const bool isReadonly = isReadonlyFilesystem(path.c_str());
   return std::unique_ptr<DirectoryAssetsProvider>(
-      new DirectoryAssetsProvider(std::move(path), isReadonly ? -1 : sb.st_mtime));
+      new DirectoryAssetsProvider(std::move(path), isReadonly ? kInvalidModDate : getModDate(sb)));
 }
 
 std::unique_ptr<Asset> DirectoryAssetsProvider::OpenInternal(const std::string& path,
@@ -346,17 +338,11 @@
   return dir_;
 }
 
-bool DirectoryAssetsProvider::IsUpToDate() const {
-  if (last_mod_time_ == -1) {
-    return true;
+UpToDate DirectoryAssetsProvider::IsUpToDate() const {
+  if (last_mod_time_ == kInvalidModDate) {
+    return UpToDate::Always;
   }
-  struct stat sb;
-  if (stat(dir_.c_str(), &sb) < 0) {
-    // If stat fails on the zip archive, return true so the zip archive the resource system does
-    // attempt to refresh the ApkAsset.
-    return true;
-  }
-  return last_mod_time_ == sb.st_mtime;
+  return fromBool(last_mod_time_ == getFileModDate(dir_.c_str()));
 }
 
 MultiAssetsProvider::MultiAssetsProvider(std::unique_ptr<AssetsProvider>&& primary,
@@ -369,8 +355,14 @@
 
 std::unique_ptr<AssetsProvider> MultiAssetsProvider::Create(
     std::unique_ptr<AssetsProvider>&& primary, std::unique_ptr<AssetsProvider>&& secondary) {
-  if (primary == nullptr || secondary == nullptr) {
-    return nullptr;
+  if (primary == nullptr && secondary == nullptr) {
+    return EmptyAssetsProvider::Create();
+  }
+  if (!primary) {
+    return secondary;
+  }
+  if (!secondary) {
+    return primary;
   }
   return std::unique_ptr<MultiAssetsProvider>(new MultiAssetsProvider(std::move(primary),
                                                                       std::move(secondary)));
@@ -397,8 +389,8 @@
   return debug_name_;
 }
 
-bool MultiAssetsProvider::IsUpToDate() const {
-  return primary_->IsUpToDate() && secondary_->IsUpToDate();
+UpToDate MultiAssetsProvider::IsUpToDate() const {
+  return combine(primary_->IsUpToDate(), [this] { return secondary_->IsUpToDate(); });
 }
 
 EmptyAssetsProvider::EmptyAssetsProvider(std::optional<std::string>&& path) :
@@ -438,12 +430,12 @@
   if (path_.has_value()) {
     return *path_;
   }
-  const static std::string kEmpty = kEmptyDebugString;
+  constexpr static std::string kEmpty{kEmptyDebugString};
   return kEmpty;
 }
 
-bool EmptyAssetsProvider::IsUpToDate() const {
-  return true;
+UpToDate EmptyAssetsProvider::IsUpToDate() const {
+  return UpToDate::Always;
 }
 
 }  // namespace android
diff --git a/libs/androidfw/Idmap.cpp b/libs/androidfw/Idmap.cpp
index 3ecd82b..262e7df 100644
--- a/libs/androidfw/Idmap.cpp
+++ b/libs/androidfw/Idmap.cpp
@@ -22,9 +22,10 @@
 #include "android-base/logging.h"
 #include "android-base/stringprintf.h"
 #include "android-base/utf8.h"
-#include "androidfw/misc.h"
+#include "androidfw/AssetManager.h"
 #include "androidfw/ResourceTypes.h"
 #include "androidfw/Util.h"
+#include "androidfw/misc.h"
 #include "utils/ByteOrder.h"
 #include "utils/Trace.h"
 
@@ -268,11 +269,16 @@
       configurations_(configs),
       overlay_entries_(overlay_entries),
       string_pool_(std::move(string_pool)),
-      idmap_fd_(
-          android::base::utf8::open(idmap_path.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY | O_PATH)),
       overlay_apk_path_(overlay_apk_path),
       target_apk_path_(target_apk_path),
-      idmap_last_mod_time_(getFileModDate(idmap_fd_.get())) {
+      idmap_last_mod_time_(kInvalidModDate) {
+  if (!isReadonlyFilesystem(std::string(overlay_apk_path_).c_str()) ||
+      !(target_apk_path_ == AssetManager::TARGET_APK_PATH ||
+        isReadonlyFilesystem(std::string(target_apk_path_).c_str()))) {
+    idmap_fd_.reset(
+        android::base::utf8::open(idmap_path.c_str(), O_RDONLY | O_CLOEXEC | O_BINARY | O_PATH));
+    idmap_last_mod_time_ = getFileModDate(idmap_fd_);
+  }
 }
 
 std::unique_ptr<LoadedIdmap> LoadedIdmap::Load(StringPiece idmap_path, StringPiece idmap_data) {
@@ -381,8 +387,11 @@
                       overlay_entries, std::move(idmap_string_pool), *overlay_path, *target_path));
 }
 
-bool LoadedIdmap::IsUpToDate() const {
-  return idmap_last_mod_time_ == getFileModDate(idmap_fd_.get());
+UpToDate LoadedIdmap::IsUpToDate() const {
+  if (idmap_last_mod_time_ == kInvalidModDate) {
+    return UpToDate::Always;
+  }
+  return fromBool(idmap_last_mod_time_ == getFileModDate(idmap_fd_.get()));
 }
 
 }  // namespace android
diff --git a/libs/androidfw/LocaleData.cpp b/libs/androidfw/LocaleData.cpp
index 020cef6..1b23d90 100644
--- a/libs/androidfw/LocaleData.cpp
+++ b/libs/androidfw/LocaleData.cpp
@@ -23,39 +23,18 @@
 #include <unordered_set>
 
 #include <androidfw/LocaleData.h>
+#include <androidfw/LocaleDataLookup.h>
 
 namespace android {
 
-#include "LocaleDataTables.cpp"
-
-inline uint32_t packLocale(const char* language, const char* region) {
-    return (((uint8_t) language[0]) << 24u) | (((uint8_t) language[1]) << 16u) |
-           (((uint8_t) region[0]) << 8u) | ((uint8_t) region[1]);
-}
-
-inline uint32_t dropRegion(uint32_t packed_locale) {
-    return packed_locale & 0xFFFF0000LU;
-}
-
-inline bool hasRegion(uint32_t packed_locale) {
-    return (packed_locale & 0x0000FFFFLU) != 0;
-}
-
-const size_t SCRIPT_LENGTH = 4;
-const size_t SCRIPT_PARENTS_COUNT = sizeof(SCRIPT_PARENTS)/sizeof(SCRIPT_PARENTS[0]);
 const uint32_t PACKED_ROOT = 0; // to represent the root locale
+const uint32_t MAX_PARENT_DEPTH = getMaxAncestorTreeDepth();
 
 uint32_t findParent(uint32_t packed_locale, const char* script) {
     if (hasRegion(packed_locale)) {
-        for (size_t i = 0; i < SCRIPT_PARENTS_COUNT; i++) {
-            if (memcmp(script, SCRIPT_PARENTS[i].script, SCRIPT_LENGTH) == 0) {
-                auto map = SCRIPT_PARENTS[i].map;
-                auto lookup_result = map->find(packed_locale);
-                if (lookup_result != map->end()) {
-                    return lookup_result->second;
-                }
-                break;
-            }
+        auto parent_key = findParentLocalePackedKey(script, packed_locale);
+        if (parent_key != 0) {
+            return parent_key;
         }
         return dropRegion(packed_locale);
     }
@@ -111,17 +90,6 @@
     return supported_ancestor_count + request_ancestors_index - 1;
 }
 
-inline bool isRepresentative(uint32_t language_and_region, const char* script) {
-    const uint64_t packed_locale = (
-            (((uint64_t) language_and_region) << 32u) |
-            (((uint64_t) script[0]) << 24u) |
-            (((uint64_t) script[1]) << 16u) |
-            (((uint64_t) script[2]) <<  8u) |
-            ((uint64_t) script[3]));
-
-    return (REPRESENTATIVE_LOCALES.count(packed_locale) != 0);
-}
-
 const uint32_t US_SPANISH = 0x65735553LU; // es-US
 const uint32_t MEXICAN_SPANISH = 0x65734D58LU; // es-MX
 const uint32_t LATIN_AMERICAN_SPANISH = 0x6573A424LU; // es-419
@@ -185,8 +153,8 @@
 
     // If we are here, left and right are equidistant from the request. We will
     // try and see if any of them is a representative locale.
-    const bool left_is_representative = isRepresentative(left, requested_script);
-    const bool right_is_representative = isRepresentative(right, requested_script);
+    const bool left_is_representative = isLocaleRepresentative(left, requested_script);
+    const bool right_is_representative = isLocaleRepresentative(right, requested_script);
     if (left_is_representative != right_is_representative) {
         return (int) left_is_representative - (int) right_is_representative;
     }
@@ -204,14 +172,14 @@
         return;
     }
     uint32_t lookup_key = packLocale(language, region);
-    auto lookup_result = LIKELY_SCRIPTS.find(lookup_key);
-    if (lookup_result == LIKELY_SCRIPTS.end()) {
+    auto lookup_result = lookupLikelyScript(lookup_key);
+    if (lookup_result == nullptr) {
         // We couldn't find the locale. Let's try without the region
         if (region[0] != '\0') {
             lookup_key = dropRegion(lookup_key);
-            lookup_result = LIKELY_SCRIPTS.find(lookup_key);
-            if (lookup_result != LIKELY_SCRIPTS.end()) {
-                memcpy(out, SCRIPT_CODES[lookup_result->second], SCRIPT_LENGTH);
+            lookup_result = lookupLikelyScript(lookup_key);
+            if (lookup_result != nullptr) {
+                memcpy(out, lookup_result, SCRIPT_LENGTH);
                 return;
             }
         }
@@ -220,7 +188,7 @@
         return;
     } else {
         // We found the locale.
-        memcpy(out, SCRIPT_CODES[lookup_result->second], SCRIPT_LENGTH);
+        memcpy(out, lookup_result, SCRIPT_LENGTH);
     }
 }
 
diff --git a/libs/androidfw/LocaleDataLookup.cpp b/libs/androidfw/LocaleDataLookup.cpp
new file mode 100644
index 0000000..6e751a7
--- /dev/null
+++ b/libs/androidfw/LocaleDataLookup.cpp
@@ -0,0 +1,14947 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+// Auto-generated by ./tools/localedata/extract_icu_data.py
+
+#include <androidfw/LocaleDataLookup.h>
+
+namespace android {
+
+constexpr const char SCRIPT_CODES[][4] = {
+    /* 0  */ {'A', 'g', 'h', 'b'},
+    /* 1  */ {'A', 'h', 'o', 'm'},
+    /* 2  */ {'A', 'r', 'a', 'b'},
+    /* 3  */ {'A', 'r', 'm', 'i'},
+    /* 4  */ {'A', 'r', 'm', 'n'},
+    /* 5  */ {'A', 'v', 's', 't'},
+    /* 6  */ {'B', 'a', 'm', 'u'},
+    /* 7  */ {'B', 'a', 's', 's'},
+    /* 8  */ {'B', 'a', 't', 'k'},
+    /* 9  */ {'B', 'e', 'n', 'g'},
+    /* 10 */ {'B', 'o', 'p', 'o'},
+    /* 11 */ {'B', 'r', 'a', 'h'},
+    /* 12 */ {'C', 'a', 'k', 'm'},
+    /* 13 */ {'C', 'a', 'n', 's'},
+    /* 14 */ {'C', 'a', 'r', 'i'},
+    /* 15 */ {'C', 'h', 'a', 'm'},
+    /* 16 */ {'C', 'h', 'e', 'r'},
+    /* 17 */ {'C', 'h', 'r', 's'},
+    /* 18 */ {'C', 'o', 'p', 't'},
+    /* 19 */ {'C', 'p', 'r', 't'},
+    /* 20 */ {'C', 'y', 'r', 'l'},
+    /* 21 */ {'D', 'e', 'v', 'a'},
+    /* 22 */ {'E', 'g', 'y', 'p'},
+    /* 23 */ {'E', 'l', 'y', 'm'},
+    /* 24 */ {'E', 't', 'h', 'i'},
+    /* 25 */ {'G', 'e', 'o', 'r'},
+    /* 26 */ {'G', 'o', 'n', 'g'},
+    /* 27 */ {'G', 'o', 'n', 'm'},
+    /* 28 */ {'G', 'o', 't', 'h'},
+    /* 29 */ {'G', 'r', 'a', 'n'},
+    /* 30 */ {'G', 'r', 'e', 'k'},
+    /* 31 */ {'G', 'u', 'j', 'r'},
+    /* 32 */ {'G', 'u', 'r', 'u'},
+    /* 33 */ {'H', 'a', 'n', 'g'},
+    /* 34 */ {'H', 'a', 'n', 'i'},
+    /* 35 */ {'H', 'a', 'n', 's'},
+    /* 36 */ {'H', 'a', 'n', 't'},
+    /* 37 */ {'H', 'e', 'b', 'r'},
+    /* 38 */ {'H', 'l', 'u', 'w'},
+    /* 39 */ {'H', 'm', 'n', 'p'},
+    /* 40 */ {'I', 't', 'a', 'l'},
+    /* 41 */ {'J', 'a', 'v', 'a'},
+    /* 42 */ {'J', 'p', 'a', 'n'},
+    /* 43 */ {'K', 'a', 'l', 'i'},
+    /* 44 */ {'K', 'a', 'n', 'a'},
+    /* 45 */ {'K', 'a', 'w', 'i'},
+    /* 46 */ {'K', 'h', 'a', 'r'},
+    /* 47 */ {'K', 'h', 'm', 'r'},
+    /* 48 */ {'K', 'i', 't', 's'},
+    /* 49 */ {'K', 'n', 'd', 'a'},
+    /* 50 */ {'K', 'o', 'r', 'e'},
+    /* 51 */ {'L', 'a', 'n', 'a'},
+    /* 52 */ {'L', 'a', 'o', 'o'},
+    /* 53 */ {'L', 'a', 't', 'f'},
+    /* 54 */ {'L', 'a', 't', 'g'},
+    /* 55 */ {'L', 'a', 't', 'n'},
+    /* 56 */ {'L', 'e', 'p', 'c'},
+    /* 57 */ {'L', 'i', 'n', 'a'},
+    /* 58 */ {'L', 'i', 'n', 'b'},
+    /* 59 */ {'L', 'i', 's', 'u'},
+    /* 60 */ {'L', 'y', 'c', 'i'},
+    /* 61 */ {'L', 'y', 'd', 'i'},
+    /* 62 */ {'M', 'a', 'n', 'd'},
+    /* 63 */ {'M', 'a', 'n', 'i'},
+    /* 64 */ {'M', 'a', 'r', 'c'},
+    /* 65 */ {'M', 'e', 'd', 'f'},
+    /* 66 */ {'M', 'e', 'r', 'c'},
+    /* 67 */ {'M', 'l', 'y', 'm'},
+    /* 68 */ {'M', 'o', 'd', 'i'},
+    /* 69 */ {'M', 'o', 'n', 'g'},
+    /* 70 */ {'M', 'r', 'o', 'o'},
+    /* 71 */ {'M', 't', 'e', 'i'},
+    /* 72 */ {'M', 'y', 'm', 'r'},
+    /* 73 */ {'N', 'a', 'r', 'b'},
+    /* 74 */ {'N', 'e', 'w', 'a'},
+    /* 75 */ {'N', 'k', 'o', 'o'},
+    /* 76 */ {'N', 's', 'h', 'u'},
+    /* 77 */ {'O', 'g', 'a', 'm'},
+    /* 78 */ {'O', 'l', 'c', 'k'},
+    /* 79 */ {'O', 'r', 'k', 'h'},
+    /* 80 */ {'O', 'r', 'y', 'a'},
+    /* 81 */ {'O', 's', 'g', 'e'},
+    /* 82 */ {'O', 'u', 'g', 'r'},
+    /* 83 */ {'P', 'a', 'u', 'c'},
+    /* 84 */ {'P', 'h', 'l', 'i'},
+    /* 85 */ {'P', 'h', 'n', 'x'},
+    /* 86 */ {'P', 'l', 'r', 'd'},
+    /* 87 */ {'P', 'r', 't', 'i'},
+    /* 88 */ {'R', 'j', 'n', 'g'},
+    /* 89 */ {'R', 'o', 'h', 'g'},
+    /* 90 */ {'R', 'u', 'n', 'r'},
+    /* 91 */ {'S', 'a', 'm', 'r'},
+    /* 92 */ {'S', 'a', 'r', 'b'},
+    /* 93 */ {'S', 'a', 'u', 'r'},
+    /* 94 */ {'S', 'g', 'n', 'w'},
+    /* 95 */ {'S', 'i', 'n', 'h'},
+    /* 96 */ {'S', 'o', 'g', 'd'},
+    /* 97 */ {'S', 'o', 'r', 'a'},
+    /* 98 */ {'S', 'o', 'y', 'o'},
+    /* 99 */ {'S', 'u', 'n', 'u'},
+    /* 100 */ {'S', 'y', 'r', 'c'},
+    /* 101 */ {'T', 'a', 'g', 'b'},
+    /* 102 */ {'T', 'a', 'k', 'r'},
+    /* 103 */ {'T', 'a', 'l', 'e'},
+    /* 104 */ {'T', 'a', 'l', 'u'},
+    /* 105 */ {'T', 'a', 'm', 'l'},
+    /* 106 */ {'T', 'a', 'n', 'g'},
+    /* 107 */ {'T', 'a', 'v', 't'},
+    /* 108 */ {'T', 'e', 'l', 'u'},
+    /* 109 */ {'T', 'f', 'n', 'g'},
+    /* 110 */ {'T', 'h', 'a', 'a'},
+    /* 111 */ {'T', 'h', 'a', 'i'},
+    /* 112 */ {'T', 'i', 'b', 't'},
+    /* 113 */ {'T', 'n', 's', 'a'},
+    /* 114 */ {'T', 'o', 't', 'o'},
+    /* 115 */ {'U', 'g', 'a', 'r'},
+    /* 116 */ {'V', 'a', 'i', 'i'},
+    /* 117 */ {'W', 'c', 'h', 'o'},
+    /* 118 */ {'X', 'p', 'e', 'o'},
+    /* 119 */ {'X', 's', 'u', 'x'},
+    /* 120 */ {'Y', 'i', 'i', 'i'},
+    /* 121 */ {'~', '~', '~', 'A'},
+    /* 122 */ {'~', '~', '~', 'B'},
+};
+
+
+const char* lookupLikelyScript(uint32_t packed_lang_region) {
+    switch(packed_lang_region) {
+        case 0x98170000u: // xag -> Aghb
+            return SCRIPT_CODES[ 0u];
+        case 0xB8E00000u: // aho -> Ahom
+            return SCRIPT_CODES[ 1u];
+        case 0xB8000000u: // aao -> Arab
+        case 0x9C200000u: // abh -> Arab
+        case 0xD4200000u: // abv -> Arab
+        case 0xB0400000u: // acm -> Arab
+        case 0xC0400000u: // acq -> Arab
+        case 0xD8400000u: // acw -> Arab
+        case 0xDC400000u: // acx -> Arab
+        case 0x94600000u: // adf -> Arab
+        case 0x84800000u: // aeb -> Arab
+        case 0x88800000u: // aec -> Arab
+        case 0x90800000u: // aee -> Arab
+        case 0xC0800000u: // aeq -> Arab
+        case 0x84A00000u: // afb -> Arab
+        case 0x85000000u: // aib -> Arab
+        case 0xC1000000u: // aiq -> Arab
+        case 0x89E00000u: // apc -> Arab
+        case 0x8DE00000u: // apd -> Arab
+        case 0x61720000u: // ar -> Arab
+        case 0xC2200000u: // arq -> Arab
+        case 0xCA200000u: // ars -> Arab
+        case 0xE2200000u: // ary -> Arab
+        case 0xE6200000u: // arz -> Arab
+        case 0xAA400000u: // ask -> Arab
+        case 0xB6600000u: // atn -> Arab
+        case 0xA6800000u: // auj -> Arab
+        case 0xE6800000u: // auz -> Arab
+        case 0x8EA00000u: // avd -> Arab
+        case 0xAEA00000u: // avl -> Arab
+        case 0x9F000000u: // ayh -> Arab
+        case 0xAF000000u: // ayl -> Arab
+        case 0xB7000000u: // ayn -> Arab
+        case 0xBF000000u: // ayp -> Arab
+        case 0x617A4951u: // az-IQ -> Arab
+        case 0x617A4952u: // az-IR -> Arab
+        case 0x87200000u: // azb -> Arab
+        case 0xAC010000u: // bal -> Arab
+        case 0xE4610000u: // bdz -> Arab
+        case 0xA4810000u: // bej -> Arab
+        case 0xCCA10000u: // bft -> Arab
+        case 0xB4C10000u: // bgn -> Arab
+        case 0xBCC10000u: // bgp -> Arab
+        case 0x90E10000u: // bhe -> Arab
+        case 0xB0E10000u: // bhm -> Arab
+        case 0xB1210000u: // bjm -> Arab
+        case 0xA2010000u: // bqi -> Arab
+        case 0x9E210000u: // brh -> Arab
+        case 0xAA210000u: // brk -> Arab
+        case 0x9E410000u: // bsh -> Arab
+        case 0xAA410000u: // bsk -> Arab
+        case 0x98E20000u: // chg -> Arab
+        case 0x81220000u: // cja -> Arab
+        case 0x85420000u: // ckb -> Arab
+        case 0x9D620000u: // clh -> Arab
+        case 0x88430000u: // dcc -> Arab
+        case 0x94830000u: // def -> Arab
+        case 0x9C830000u: // deh -> Arab
+        case 0xACC30000u: // dgl -> Arab
+        case 0xA9830000u: // dmk -> Arab
+        case 0xAD830000u: // dml -> Arab
+        case 0x9E440000u: // esh -> Arab
+        case 0x66610000u: // fa -> Arab
+        case 0xE0050000u: // fay -> Arab
+        case 0xE4050000u: // faz -> Arab
+        case 0x81050000u: // fia -> Arab
+        case 0x86850000u: // fub -> Arab
+        case 0xE4260000u: // gbz -> Arab
+        case 0x98C60000u: // ggg -> Arab
+        case 0x80E60000u: // gha -> Arab
+        case 0xC4E60000u: // ghr -> Arab
+        case 0x99060000u: // gig -> Arab
+        case 0xA9260000u: // gjk -> Arab
+        case 0xD1260000u: // gju -> Arab
+        case 0x9D660000u: // glh -> Arab
+        case 0xA9660000u: // glk -> Arab
+        case 0x8AC60000u: // gwc -> Arab
+        case 0x96C60000u: // gwf -> Arab
+        case 0xCEC60000u: // gwt -> Arab
+        case 0xA3260000u: // gzi -> Arab
+        case 0x6861434Du: // ha-CM -> Arab
+        case 0x68615344u: // ha-SD -> Arab
+        case 0x88070000u: // hac -> Arab
+        case 0xE4070000u: // haz -> Arab
+        case 0x9D470000u: // hkh -> Arab
+        case 0x8DA70000u: // hnd -> Arab
+        case 0xB9A70000u: // hno -> Arab
+        case 0x9DC70000u: // hoh -> Arab
+        case 0xE6270000u: // hrz -> Arab
+        case 0xCA470000u: // hss -> Arab
+        case 0xAA480000u: // isk -> Arab
+        case 0x8C090000u: // jad -> Arab
+        case 0xCC090000u: // jat -> Arab
+        case 0xB4290000u: // jbn -> Arab
+        case 0x98690000u: // jdg -> Arab
+        case 0x8DA90000u: // jnd -> Arab
+        case 0x99C90000u: // jog -> Arab
+        case 0xD02A0000u: // kbu -> Arab
+        case 0xE02A0000u: // kby -> Arab
+        case 0xE04A0000u: // kcy -> Arab
+        case 0xB0AA0000u: // kfm -> Arab
+        case 0xD8EA0000u: // khw -> Arab
+        case 0x6B6B4146u: // kk-AF -> Arab
+        case 0x6B6B434Eu: // kk-CN -> Arab
+        case 0x6B6B4952u: // kk-IR -> Arab
+        case 0x6B6B4D4Eu: // kk-MN -> Arab
+        case 0xA56A0000u: // klj -> Arab
+        case 0xE58A0000u: // kmz -> Arab
+        case 0x6B730000u: // ks -> Arab
+        case 0xAE6A0000u: // ktl -> Arab
+        case 0x6B754C42u: // ku-LB -> Arab
+        case 0xDEAA0000u: // kvx -> Arab
+        case 0xBEEA0000u: // kxp -> Arab
+        case 0x6B79434Eu: // ky-CN -> Arab
+        case 0x9C0B0000u: // lah -> Arab
+        case 0xA14B0000u: // lki -> Arab
+        case 0x8A2B0000u: // lrc -> Arab
+        case 0xAA2B0000u: // lrk -> Arab
+        case 0xAE2B0000u: // lrl -> Arab
+        case 0x824B0000u: // lsa -> Arab
+        case 0xCA4B0000u: // lss -> Arab
+        case 0xD68B0000u: // luv -> Arab
+        case 0xE68B0000u: // luz -> Arab
+        case 0xE02C0000u: // mby -> Arab
+        case 0x906C0000u: // mde -> Arab
+        case 0x80AC0000u: // mfa -> Arab
+        case 0xA0AC0000u: // mfi -> Arab
+        case 0xA4EC0000u: // mhj -> Arab
+        case 0xA14C0000u: // mki -> Arab
+        case 0xA5AC0000u: // mnj -> Arab
+        case 0x6D734343u: // ms-CC -> Arab
+        case 0x92AC0000u: // mve -> Arab
+        case 0xE2AC0000u: // mvy -> Arab
+        case 0xB72C0000u: // mzn -> Arab
+        case 0xA16D0000u: // nli -> Arab
+        case 0xB16D0000u: // nlm -> Arab
+        case 0xE66D0000u: // ntz -> Arab
+        case 0xC30D0000u: // nyq -> Arab
+        case 0xA86E0000u: // odk -> Arab
+        case 0xD22E0000u: // oru -> Arab
+        case 0x826E0000u: // ota -> Arab
+        case 0x7061504Bu: // pa-PK -> Arab
+        case 0xCC2F0000u: // pbt -> Arab
+        case 0xACEF0000u: // phl -> Arab
+        case 0xC4EF0000u: // phr -> Arab
+        case 0xD4EF0000u: // phv -> Arab
+        case 0xA96F0000u: // plk -> Arab
+        case 0x8A2F0000u: // prc -> Arab
+        case 0x8E2F0000u: // prd -> Arab
+        case 0xDE2F0000u: // prx -> Arab
+        case 0x70730000u: // ps -> Arab
+        case 0x9E4F0000u: // psh -> Arab
+        case 0xA24F0000u: // psi -> Arab
+        case 0xCE4F0000u: // pst -> Arab
+        case 0xC2F00000u: // qxq -> Arab
+        case 0x84710000u: // rdb -> Arab
+        case 0xCD910000u: // rmt -> Arab
+        case 0xB4320000u: // sbn -> Arab
+        case 0xAC520000u: // scl -> Arab
+        case 0x73640000u: // sd -> Arab
+        case 0x84720000u: // sdb -> Arab
+        case 0x94720000u: // sdf -> Arab
+        case 0x98720000u: // sdg -> Arab
+        case 0x9C720000u: // sdh -> Arab
+        case 0xC8720000u: // sds -> Arab
+        case 0xC4D20000u: // sgr -> Arab
+        case 0xE0D20000u: // sgy -> Arab
+        case 0x8CF20000u: // shd -> Arab
+        case 0xB0F20000u: // shm -> Arab
+        case 0xD0F20000u: // shu -> Arab
+        case 0xD4F20000u: // shv -> Arab
+        case 0xE1120000u: // siy -> Arab
+        case 0xE5120000u: // siz -> Arab
+        case 0xC5520000u: // skr -> Arab
+        case 0xE1920000u: // smy -> Arab
+        case 0xBA120000u: // sqo -> Arab
+        case 0xCE120000u: // sqt -> Arab
+        case 0x9E320000u: // srh -> Arab
+        case 0xE6320000u: // srz -> Arab
+        case 0x9E520000u: // ssh -> Arab
+        case 0xCA720000u: // sts -> Arab
+        case 0x86D20000u: // swb -> Arab
+        case 0x7467504Bu: // tg-PK -> Arab
+        case 0xB9330000u: // tjo -> Arab
+        case 0xC9530000u: // tks -> Arab
+        case 0xD5D30000u: // tov -> Arab
+        case 0x82330000u: // tra -> Arab
+        case 0xB2330000u: // trm -> Arab
+        case 0xDA330000u: // trw -> Arab
+        case 0x75670000u: // ug -> Arab
+        case 0x75720000u: // ur -> Arab
+        case 0x9E540000u: // ush -> Arab
+        case 0x757A4146u: // uz-AF -> Arab
+        case 0xCB340000u: // uzs -> Arab
+        case 0x94150000u: // vaf -> Arab
+        case 0xC4D50000u: // vgr -> Arab
+        case 0x9D950000u: // vmh -> Arab
+        case 0xA8360000u: // wbk -> Arab
+        case 0xB9760000u: // wlo -> Arab
+        case 0x91B60000u: // wne -> Arab
+        case 0xA1B60000u: // wni -> Arab
+        case 0xD6560000u: // wsv -> Arab
+        case 0x90F70000u: // xhe -> Arab
+        case 0x81570000u: // xka -> Arab
+        case 0x89570000u: // xkc -> Arab
+        case 0xA5570000u: // xkj -> Arab
+        case 0xBD570000u: // xkp -> Arab
+        case 0xA2B70000u: // xvi -> Arab
+        case 0x98780000u: // ydg -> Arab
+        case 0x80390000u: // zba -> Arab
+        case 0xA4790000u: // zdj -> Arab
+        case 0xB2990000u: // zum -> Arab
+            return SCRIPT_CODES[ 2u];
+        case 0x8A200000u: // arc -> Armi
+            return SCRIPT_CODES[ 3u];
+        case 0xB2E00000u: // axm -> Armn
+        case 0x68790000u: // hy -> Armn
+        case 0xDB070000u: // hyw -> Armn
+        case 0xA1910000u: // rmi -> Armn
+            return SCRIPT_CODES[ 4u];
+        case 0x61650000u: // ae -> Avst
+            return SCRIPT_CODES[ 5u];
+        case 0xDC010000u: // bax -> Bamu
+            return SCRIPT_CODES[ 6u];
+        case 0xC2410000u: // bsq -> Bass
+            return SCRIPT_CODES[ 7u];
+        case 0x8E610000u: // btd -> Batk
+        case 0xB2610000u: // btm -> Batk
+            return SCRIPT_CODES[ 8u];
+        case 0xCDC00000u: // aot -> Beng
+        case 0x61730000u: // as -> Beng
+        case 0x626E0000u: // bn -> Beng
+        case 0xE1E10000u: // bpy -> Beng
+        case 0xE4620000u: // cdz -> Beng
+        case 0x9A620000u: // ctg -> Beng
+        case 0xC4830000u: // der -> Beng
+        case 0xCE260000u: // grt -> Beng
+        case 0xC06A0000u: // kdq -> Beng
+        case 0xA1AC0000u: // mni -> Beng
+        case 0x9C110000u: // rah -> Beng
+        case 0xCD510000u: // rkt -> Beng
+        case 0xC4720000u: // sdr -> Beng
+        case 0xAF120000u: // syl -> Beng
+        case 0xC5B40000u: // unr -> Beng
+        case 0xDDB40000u: // unx -> Beng
+            return SCRIPT_CODES[ 9u];
+        case 0xA5870000u: // hmj -> Bopo
+        case 0xC1870000u: // hmq -> Bopo
+            return SCRIPT_CODES[10u];
+        case 0xB8EA0000u: // kho -> Brah
+        case 0x814F0000u: // pka -> Brah
+        case 0x9D8F0000u: // pmh -> Brah
+        case 0xD24F0000u: // psu -> Brah
+        case 0xC2770000u: // xtq -> Brah
+            return SCRIPT_CODES[11u];
+        case 0xBC420000u: // ccp -> Cakm
+        case 0xD5B30000u: // tnv -> Cakm
+            return SCRIPT_CODES[12u];
+        case 0x63720000u: // cr -> Cans
+        case 0xA6220000u: // crj -> Cans
+        case 0xAA220000u: // crk -> Cans
+        case 0xAE220000u: // crl -> Cans
+        case 0xB2220000u: // crm -> Cans
+        case 0xDA420000u: // csw -> Cans
+        case 0x69750000u: // iu -> Cans
+        case 0xAA4D0000u: // nsk -> Cans
+        case 0x6F6A0000u: // oj -> Cans
+        case 0xC92E0000u: // ojs -> Cans
+            return SCRIPT_CODES[13u];
+        case 0xC4570000u: // xcr -> Cari
+            return SCRIPT_CODES[14u];
+        case 0xB1220000u: // cjm -> Cham
+            return SCRIPT_CODES[15u];
+        case 0xC4E20000u: // chr -> Cher
+            return SCRIPT_CODES[16u];
+        case 0xB8570000u: // xco -> Chrs
+            return SCRIPT_CODES[17u];
+        case 0xBDC20000u: // cop -> Copt
+            return SCRIPT_CODES[18u];
+        case 0xE0440000u: // ecy -> Cprt
+        case 0x8A260000u: // grc -> Cprt
+            return SCRIPT_CODES[19u];
+        case 0x61620000u: // ab -> Cyrl
+        case 0xE0600000u: // ady -> Cyrl
+        case 0xDCC00000u: // agx -> Cyrl
+        case 0xD5400000u: // akv -> Cyrl
+        case 0xC5600000u: // alr -> Cyrl
+        case 0xCD600000u: // alt -> Cyrl
+        case 0xA1A00000u: // ani -> Cyrl
+        case 0x8A000000u: // aqc -> Cyrl
+        case 0xD6600000u: // atv -> Cyrl
+        case 0x61760000u: // av -> Cyrl
+        case 0x617A5255u: // az-RU -> Cyrl
+        case 0x62610000u: // ba -> Cyrl
+        case 0x62650000u: // be -> Cyrl
+        case 0x62670000u: // bg -> Cyrl
+        case 0x9CE10000u: // bhh -> Cyrl
+        case 0x9DE10000u: // bph -> Cyrl
+        case 0x82810000u: // bua -> Cyrl
+        case 0xB2E10000u: // bxm -> Cyrl
+        case 0x63650000u: // ce -> Cyrl
+        case 0xB0E20000u: // chm -> Cyrl
+        case 0xA1220000u: // cji -> Cyrl
+        case 0xCD420000u: // ckt -> Cyrl
+        case 0xD9620000u: // clw -> Cyrl
+        case 0x9E220000u: // crh -> Cyrl
+        case 0x63750000u: // cu -> Cyrl
+        case 0x63760000u: // cv -> Cyrl
+        case 0xC4030000u: // dar -> Cyrl
+        case 0xB8630000u: // ddo -> Cyrl
+        case 0x99630000u: // dlg -> Cyrl
+        case 0x99A30000u: // dng -> Cyrl
+        case 0x95A40000u: // enf -> Cyrl
+        case 0x9DA40000u: // enh -> Cyrl
+        case 0x92A40000u: // eve -> Cyrl
+        case 0xB6A40000u: // evn -> Cyrl
+        case 0xB8660000u: // gdo -> Cyrl
+        case 0xB5060000u: // gin -> Cyrl
+        case 0x8D660000u: // gld -> Cyrl
+        case 0xE6870000u: // huz -> Cyrl
+        case 0x9DA80000u: // inh -> Cyrl
+        case 0xAE680000u: // itl -> Cyrl
+        case 0xCC490000u: // jct -> Cyrl
+        case 0xCC690000u: // jdt -> Cyrl
+        case 0x800A0000u: // kaa -> Cyrl
+        case 0xBC0A0000u: // kap -> Cyrl
+        case 0x8C2A0000u: // kbd -> Cyrl
+        case 0x804A0000u: // kca -> Cyrl
+        case 0xCC8A0000u: // ket -> Cyrl
+        case 0xD4EA0000u: // khv -> Cyrl
+        case 0xB10A0000u: // kim -> Cyrl
+        case 0x9D2A0000u: // kjh -> Cyrl
+        case 0x6B6B0000u: // kk -> Cyrl
+        case 0xA1CA0000u: // koi -> Cyrl
+        case 0xCDEA0000u: // kpt -> Cyrl
+        case 0xE1EA0000u: // kpy -> Cyrl
+        case 0x8A2A0000u: // krc -> Cyrl
+        case 0xAA2A0000u: // krk -> Cyrl
+        case 0xB28A0000u: // kum -> Cyrl
+        case 0x6B760000u: // kv -> Cyrl
+        case 0x82AA0000u: // kva -> Cyrl
+        case 0x6B790000u: // ky -> Cyrl
+        case 0x902B0000u: // lbe -> Cyrl
+        case 0xE48B0000u: // lez -> Cyrl
+        case 0x946C0000u: // mdf -> Cyrl
+        case 0x6D6B0000u: // mk -> Cyrl
+        case 0x6D6E0000u: // mn -> Cyrl
+        case 0xC9AC0000u: // mns -> Cyrl
+        case 0xA62C0000u: // mrj -> Cyrl
+        case 0xB26C0000u: // mtm -> Cyrl
+        case 0x8E8C0000u: // mud -> Cyrl
+        case 0xD70C0000u: // myv -> Cyrl
+        case 0x946D0000u: // ndf -> Cyrl
+        case 0x988D0000u: // neg -> Cyrl
+        case 0xB90D0000u: // nio -> Cyrl
+        case 0xD50D0000u: // niv -> Cyrl
+        case 0x99CD0000u: // nog -> Cyrl
+        case 0x800E0000u: // oaa -> Cyrl
+        case 0x880E0000u: // oac -> Cyrl
+        case 0xA98E0000u: // omk -> Cyrl
+        case 0xD62E0000u: // orv -> Cyrl
+        case 0x6F730000u: // os -> Cyrl
+        case 0xC00F0000u: // paq -> Cyrl
+        case 0xAA510000u: // rsk -> Cyrl
+        case 0x72750000u: // ru -> Cyrl
+        case 0x92910000u: // rue -> Cyrl
+        case 0xCE910000u: // rut -> Cyrl
+        case 0x9C120000u: // sah -> Cyrl
+        case 0xAC920000u: // sel -> Cyrl
+        case 0x9CD20000u: // sgh -> Cyrl
+        case 0x81120000u: // sia -> Cyrl
+        case 0x8D320000u: // sjd -> Cyrl
+        case 0xCD320000u: // sjt -> Cyrl
+        case 0x73720000u: // sr -> Cyrl
+        case 0xE2720000u: // sty -> Cyrl
+        case 0x84130000u: // tab -> Cyrl
+        case 0x74670000u: // tg -> Cyrl
+        case 0xB5130000u: // tin -> Cyrl
+        case 0x74740000u: // tt -> Cyrl
+        case 0xD7130000u: // tyv -> Cyrl
+        case 0x90740000u: // ude -> Cyrl
+        case 0xA0740000u: // udi -> Cyrl
+        case 0xB0740000u: // udm -> Cyrl
+        case 0x75674B5Au: // ug-KZ -> Cyrl
+        case 0x75674D4Eu: // ug-MN -> Cyrl
+        case 0x9CD40000u: // ugh -> Cyrl
+        case 0x756B0000u: // uk -> Cyrl
+        case 0x89740000u: // ulc -> Cyrl
+        case 0x757A434Eu: // uz-CN -> Cyrl
+        case 0xAC170000u: // xal -> Cyrl
+        case 0xC8170000u: // xas -> Cyrl
+        case 0xC0770000u: // xdq -> Cyrl
+        case 0xB1F70000u: // xpm -> Cyrl
+        case 0xB2370000u: // xrm -> Cyrl
+        case 0xB6370000u: // xrn -> Cyrl
+        case 0xBAD70000u: // xwo -> Cyrl
+        case 0xA0180000u: // yai -> Cyrl
+        case 0x99580000u: // ykg -> Cyrl
+        case 0x9D580000u: // ykh -> Cyrl
+        case 0xA9B80000u: // ynk -> Cyrl
+        case 0xAA380000u: // yrk -> Cyrl
+        case 0xC6580000u: // ysr -> Cyrl
+        case 0x9A980000u: // yug -> Cyrl
+        case 0xDE980000u: // yux -> Cyrl
+        case 0xB9590000u: // zko -> Cyrl
+        case 0xE5590000u: // zkz -> Cyrl
+            return SCRIPT_CODES[20u];
+        case 0xA0C00000u: // agi -> Deva
+        case 0xC4E00000u: // ahr -> Deva
+        case 0xBDA00000u: // anp -> Deva
+        case 0xC1A00000u: // anq -> Deva
+        case 0xC5A00000u: // anr -> Deva
+        case 0x9DE00000u: // aph -> Deva
+        case 0xC6400000u: // asr -> Deva
+        case 0x82C00000u: // awa -> Deva
+        case 0xBC010000u: // bap -> Deva
+        case 0x90810000u: // bee -> Deva
+        case 0x84A10000u: // bfb -> Deva
+        case 0xE0A10000u: // bfy -> Deva
+        case 0xE4A10000u: // bfz -> Deva
+        case 0x88C10000u: // bgc -> Deva
+        case 0x8CC10000u: // bgd -> Deva
+        case 0xC0C10000u: // bgq -> Deva
+        case 0xD8C10000u: // bgw -> Deva
+        case 0x80E10000u: // bha -> Deva
+        case 0x84E10000u: // bhb -> Deva
+        case 0x8CE10000u: // bhd -> Deva
+        case 0xA0E10000u: // bhi -> Deva
+        case 0xA4E10000u: // bhj -> Deva
+        case 0xB8E10000u: // bho -> Deva
+        case 0xCCE10000u: // bht -> Deva
+        case 0xD0E10000u: // bhu -> Deva
+        case 0xE1010000u: // biy -> Deva
+        case 0xA5210000u: // bjj -> Deva
+        case 0xA5810000u: // bmj -> Deva
+        case 0xC9A10000u: // bns -> Deva
+        case 0xDDE10000u: // bpx -> Deva
+        case 0x82210000u: // bra -> Deva
+        case 0x8E210000u: // brd -> Deva
+        case 0xDE210000u: // brx -> Deva
+        case 0xD6610000u: // btv -> Deva
+        case 0x9F010000u: // byh -> Deva
+        case 0xDB010000u: // byw -> Deva
+        case 0x9C620000u: // cdh -> Deva
+        case 0xA4620000u: // cdj -> Deva
+        case 0xB0620000u: // cdm -> Deva
+        case 0xDCE20000u: // chx -> Deva
+        case 0x9D020000u: // cih -> Deva
+        case 0xB6620000u: // ctn -> Deva
+        case 0xC0030000u: // daq -> Deva
+        case 0xA0E30000u: // dhi -> Deva
+        case 0xB8E30000u: // dho -> Deva
+        case 0xD8E30000u: // dhw -> Deva
+        case 0xA1C30000u: // doi -> Deva
+        case 0xC2230000u: // drq -> Deva
+        case 0xE2230000u: // dry -> Deva
+        case 0xE2630000u: // dty -> Deva
+        case 0x9E830000u: // duh -> Deva
+        case 0xCA830000u: // dus -> Deva
+        case 0xE6C30000u: // dwz -> Deva
+        case 0x99840000u: // emg -> Deva
+        case 0xD1840000u: // emu -> Deva
+        case 0xD1850000u: // fmu -> Deva
+        case 0xA8260000u: // gbk -> Deva
+        case 0xB0260000u: // gbm -> Deva
+        case 0xDC660000u: // gdx -> Deva
+        case 0x90E60000u: // ghe -> Deva
+        case 0xA5C60000u: // goj -> Deva
+        case 0xA9C60000u: // gok -> Deva
+        case 0xB5C60000u: // gon -> Deva
+        case 0x82260000u: // gra -> Deva
+        case 0xC6A60000u: // gvr -> Deva
+        case 0xBB060000u: // gyo -> Deva
+        case 0x68690000u: // hi -> Deva
+        case 0x95070000u: // hif -> Deva
+        case 0x85670000u: // hlb -> Deva
+        case 0x91A70000u: // hne -> Deva
+        case 0x89C70000u: // hoc -> Deva
+        case 0xA5C70000u: // hoj -> Deva
+        case 0xE1C70000u: // hoy -> Deva
+        case 0xCE870000u: // hut -> Deva
+        case 0x90890000u: // jee -> Deva
+        case 0xAD890000u: // jml -> Deva
+        case 0xADA90000u: // jnl -> Deva
+        case 0xC9A90000u: // jns -> Deva
+        case 0xAE890000u: // jul -> Deva
+        case 0xDC8A0000u: // kex -> Deva
+        case 0x84AA0000u: // kfb -> Deva
+        case 0xA8AA0000u: // kfk -> Deva
+        case 0xBCAA0000u: // kfp -> Deva
+        case 0xC0AA0000u: // kfq -> Deva
+        case 0xC4AA0000u: // kfr -> Deva
+        case 0xC8AA0000u: // kfs -> Deva
+        case 0xD0AA0000u: // kfu -> Deva
+        case 0xDCAA0000u: // kfx -> Deva
+        case 0xE0AA0000u: // kfy -> Deva
+        case 0xA4CA0000u: // kgj -> Deva
+        case 0xE0CA0000u: // kgy -> Deva
+        case 0xB4EA0000u: // khn -> Deva
+        case 0x950A0000u: // kif -> Deva
+        case 0xBD0A0000u: // kip -> Deva
+        case 0xAD2A0000u: // kjl -> Deva
+        case 0xB92A0000u: // kjo -> Deva
+        case 0xCD4A0000u: // kkt -> Deva
+        case 0x916A0000u: // kle -> Deva
+        case 0xC56A0000u: // klr -> Deva
+        case 0xA58A0000u: // kmj -> Deva
+        case 0xB5AA0000u: // knn -> Deva
+        case 0xA9CA0000u: // kok -> Deva
+        case 0x822A0000u: // kra -> Deva
+        case 0xD22A0000u: // kru -> Deva
+        case 0xE64A0000u: // ksz -> Deva
+        case 0x926A0000u: // kte -> Deva
+        case 0xD70A0000u: // kyv -> Deva
+        case 0xDB0A0000u: // kyw -> Deva
+        case 0x900B0000u: // lae -> Deva
+        case 0x942B0000u: // lbf -> Deva
+        case 0xB02B0000u: // lbm -> Deva
+        case 0xC42B0000u: // lbr -> Deva
+        case 0xB0EB0000u: // lhm -> Deva
+        case 0x950B0000u: // lif -> Deva
+        case 0x9D8B0000u: // lmh -> Deva
+        case 0xE1CB0000u: // loy -> Deva
+        case 0xD28B0000u: // luu -> Deva
+        case 0x980C0000u: // mag -> Deva
+        case 0xA00C0000u: // mai -> Deva
+        case 0xBCCC0000u: // mgp -> Deva
+        case 0xAD2C0000u: // mjl -> Deva
+        case 0xCD2C0000u: // mjt -> Deva
+        case 0xE52C0000u: // mjz -> Deva
+        case 0x854C0000u: // mkb -> Deva
+        case 0x914C0000u: // mke -> Deva
+        case 0x6D720000u: // mr -> Deva
+        case 0x8E2C0000u: // mrd -> Deva
+        case 0xC62C0000u: // mrr -> Deva
+        case 0xC66C0000u: // mtr -> Deva
+        case 0xCE8C0000u: // mut -> Deva
+        case 0xC6CC0000u: // mwr -> Deva
+        case 0xB80D0000u: // nao -> Deva
+        case 0x8C4D0000u: // ncd -> Deva
+        case 0x6E650000u: // ne -> Deva
+        case 0xD88D0000u: // new -> Deva
+        case 0xDD6D0000u: // nlx -> Deva
+        case 0xB18D0000u: // nmm -> Deva
+        case 0x91CD0000u: // noe -> Deva
+        case 0xA1CD0000u: // noi -> Deva
+        case 0xDECD0000u: // nwx -> Deva
+        case 0x816E0000u: // ola -> Deva
+        case 0xB5CE0000u: // oon -> Deva
+        case 0x9C4F0000u: // pch -> Deva
+        case 0xA04F0000u: // pci -> Deva
+        case 0x98CF0000u: // pgg -> Deva
+        case 0x8CEF0000u: // phd -> Deva
+        case 0xD8EF0000u: // phw -> Deva
+        case 0xB28F0000u: // pum -> Deva
+        case 0xC6CF0000u: // pwr -> Deva
+        case 0x80110000u: // raa -> Deva
+        case 0x84110000u: // rab -> Deva
+        case 0x94110000u: // raf -> Deva
+        case 0xA4110000u: // raj -> Deva
+        case 0xD4110000u: // rav -> Deva
+        case 0xA1310000u: // rji -> Deva
+        case 0xC9310000u: // rjs -> Deva
+        case 0xDA710000u: // rtw -> Deva
+        case 0xC6D10000u: // rwr -> Deva
+        case 0x73610000u: // sa -> Deva
+        case 0xA8520000u: // sck -> Deva
+        case 0xBC520000u: // scp -> Deva
+        case 0x7364494Eu: // sd-IN -> Deva
+        case 0xA4D20000u: // sgj -> Deva
+        case 0xBD320000u: // sjp -> Deva
+        case 0xA5520000u: // skj -> Deva
+        case 0xA1D20000u: // soi -> Deva
+        case 0xDE320000u: // srx -> Deva
+        case 0xD6D20000u: // swv -> Deva
+        case 0xDB120000u: // syw -> Deva
+        case 0xA4130000u: // taj -> Deva
+        case 0x84730000u: // tdb -> Deva
+        case 0x98730000u: // tdg -> Deva
+        case 0x9C730000u: // tdh -> Deva
+        case 0x90D30000u: // tge -> Deva
+        case 0x90F30000u: // the -> Deva
+        case 0x94F30000u: // thf -> Deva
+        case 0xACF30000u: // thl -> Deva
+        case 0xC0F30000u: // thq -> Deva
+        case 0xC4F30000u: // thr -> Deva
+        case 0xC8F30000u: // ths -> Deva
+        case 0xA5130000u: // tij -> Deva
+        case 0x85530000u: // tkb -> Deva
+        case 0xCD530000u: // tkt -> Deva
+        case 0xE6730000u: // ttz -> Deva
+        case 0xB2D30000u: // twm -> Deva
+        case 0xC5B44E50u: // unr-NP -> Deva
+        case 0x9C150000u: // vah -> Deva
+        case 0xC8150000u: // vas -> Deva
+        case 0xD4150000u: // vav -> Deva
+        case 0xE0150000u: // vay -> Deva
+        case 0xA9350000u: // vjk -> Deva
+        case 0xC4360000u: // wbr -> Deva
+        case 0x91960000u: // wme -> Deva
+        case 0xB2760000u: // wtm -> Deva
+        case 0xC5B70000u: // xnr -> Deva
+        case 0xC6570000u: // xsr -> Deva
+        case 0x9C380000u: // ybh -> Deva
+        case 0xA0380000u: // ybi -> Deva
+            return SCRIPT_CODES[21u];
+        case 0xE0C40000u: // egy -> Egyp
+            return SCRIPT_CODES[22u];
+        case 0xE1770000u: // xly -> Elym
+            return SCRIPT_CODES[23u];
+        case 0xA4C00000u: // agj -> Ethi
+        case 0x98E00000u: // ahg -> Ethi
+        case 0xD9600000u: // alw -> Ethi
+        case 0x616D0000u: // am -> Ethi
+        case 0xD1A00000u: // anu -> Ethi
+        case 0xB6C00000u: // awn -> Ethi
+        case 0xC0410000u: // bcq -> Ethi
+        case 0xCE410000u: // bst -> Ethi
+        case 0xB7010000u: // byn -> Ethi
+        case 0xDDC30000u: // dox -> Ethi
+        case 0xCA230000u: // drs -> Ethi
+        case 0xE4860000u: // gez -> Ethi
+        case 0xD5860000u: // gmv -> Ethi
+        case 0x95C60000u: // gof -> Ethi
+        case 0xD2260000u: // gru -> Ethi
+        case 0xC4070000u: // har -> Ethi
+        case 0xE0670000u: // hdy -> Ethi
+        case 0xC5C80000u: // ior -> Ethi
+        case 0xE20A0000u: // kqy -> Ethi
+        case 0x866A0000u: // ktb -> Ethi
+        case 0xDC6C0000u: // mdx -> Ethi
+        case 0xE06C0000u: // mdy -> Ethi
+        case 0xE68C0000u: // muz -> Ethi
+        case 0xE6AC0000u: // mvz -> Ethi
+        case 0xB30C0000u: // mym -> Ethi
+        case 0xD8D20000u: // sgw -> Ethi
+        case 0xD6720000u: // stv -> Ethi
+        case 0x74690000u: // ti -> Ethi
+        case 0x99130000u: // tig -> Ethi
+        case 0xAC160000u: // wal -> Ethi
+        case 0x91760000u: // wle -> Ethi
+        case 0xB4170000u: // xan -> Ethi
+        case 0x82D90000u: // zwa -> Ethi
+            return SCRIPT_CODES[24u];
+        case 0xAC210000u: // bbl -> Geor
+        case 0x90C90000u: // jge -> Geor
+        case 0x6B610000u: // ka -> Geor
+        case 0xD40E0000u: // oav -> Geor
+        case 0x82B20000u: // sva -> Geor
+        case 0x95970000u: // xmf -> Geor
+            return SCRIPT_CODES[25u];
+        case 0x9A560000u: // wsg -> Gong
+            return SCRIPT_CODES[26u];
+        case 0x9A440000u: // esg -> Gonm
+            return SCRIPT_CODES[27u];
+        case 0xCDC60000u: // got -> Goth
+            return SCRIPT_CODES[28u];
+        case 0xE26E0000u: // oty -> Gran
+            return SCRIPT_CODES[29u];
+        case 0xCC000000u: // aat -> Grek
+        case 0xDCC10000u: // bgx -> Grek
+        case 0x99E20000u: // cpg -> Grek
+        case 0xC4440000u: // ecr -> Grek
+        case 0x656C0000u: // el -> Grek
+        case 0xCDAF0000u: // pnt -> Grek
+        case 0xDC520000u: // scx -> Grek
+        case 0x8E530000u: // tsd -> Grek
+        case 0xB2940000u: // uum -> Grek
+        case 0x99F70000u: // xpg -> Grek
+        case 0xA4980000u: // yej -> Grek
+            return SCRIPT_CODES[30u];
+        case 0xA0620000u: // cdi -> Gujr
+        case 0xB4E30000u: // dhn -> Gujr
+        case 0x86830000u: // dub -> Gujr
+        case 0xC8060000u: // gas -> Gujr
+        case 0xAC260000u: // gbl -> Gujr
+        case 0x67750000u: // gu -> Gujr
+            return SCRIPT_CODES[31u];
+        case 0x70610000u: // pa -> Guru
+            return SCRIPT_CODES[32u];
+        case 0x91290000u: // jje -> Hang
+        case 0xB14E0000u: // okm -> Hang
+            return SCRIPT_CODES[33u];
+        case 0xD9C70000u: // how -> Hani
+        case 0xB94E0000u: // oko -> Hani
+        case 0xA2D20000u: // swi -> Hani
+        case 0x9C590000u: // zch -> Hani
+        case 0x9C990000u: // zeh -> Hani
+        case 0x84D90000u: // zgb -> Hani
+        case 0xB0D90000u: // zgm -> Hani
+        case 0xB4D90000u: // zgn -> Hani
+        case 0x8CF90000u: // zhd -> Hani
+        case 0xA5790000u: // zlj -> Hani
+        case 0xB5790000u: // zln -> Hani
+        case 0xC1790000u: // zlq -> Hani
+        case 0x92190000u: // zqe -> Hani
+        case 0x9B190000u: // zyg -> Hani
+        case 0xB7190000u: // zyn -> Hani
+        case 0xA7390000u: // zzj -> Hani
+            return SCRIPT_CODES[34u];
+        case 0xB8620000u: // cdo -> Hans
+        case 0xE1220000u: // cjy -> Hans
+        case 0xBDA20000u: // cnp -> Hans
+        case 0xBE420000u: // csp -> Hans
+        case 0x9F220000u: // czh -> Hans
+        case 0xB4060000u: // gan -> Hans
+        case 0xA8070000u: // hak -> Hans
+        case 0xB6470000u: // hsn -> Hans
+        case 0x9F2B0000u: // lzh -> Hans
+        case 0xB40D0000u: // nan -> Hans
+        case 0xD2960000u: // wuu -> Hans
+        case 0x9298434Eu: // yue-CN -> Hans
+        case 0x7A680000u: // zh -> Hans
+            return SCRIPT_CODES[35u];
+        case 0x8A6B0000u: // ltc -> Hant
+        case 0x92980000u: // yue -> Hant
+        case 0x7A684155u: // zh-AU -> Hant
+        case 0x7A68424Eu: // zh-BN -> Hant
+        case 0x7A684742u: // zh-GB -> Hant
+        case 0x7A684746u: // zh-GF -> Hant
+        case 0x7A68484Bu: // zh-HK -> Hant
+        case 0x7A684944u: // zh-ID -> Hant
+        case 0x7A684D4Fu: // zh-MO -> Hant
+        case 0x7A685041u: // zh-PA -> Hant
+        case 0x7A685046u: // zh-PF -> Hant
+        case 0x7A685048u: // zh-PH -> Hant
+        case 0x7A685352u: // zh-SR -> Hant
+        case 0x7A685448u: // zh-TH -> Hant
+        case 0x7A685457u: // zh-TW -> Hant
+        case 0x7A685553u: // zh-US -> Hant
+        case 0x7A68564Eu: // zh-VN -> Hant
+            return SCRIPT_CODES[36u];
+        case 0xA5000000u: // aij -> Hebr
+        case 0xAB220000u: // czk -> Hebr
+        case 0xB8270000u: // hbo -> Hebr
+        case 0x68650000u: // he -> Hebr
+        case 0xE2870000u: // huy -> Hebr
+        case 0xAA680000u: // itk -> Hebr
+        case 0x69770000u: // iw -> Hebr
+        case 0x90290000u: // jbe -> Hebr
+        case 0x6A690000u: // ji -> Hebr
+        case 0x81E90000u: // jpa -> Hebr
+        case 0xC5E90000u: // jpr -> Hebr
+        case 0x86290000u: // jrb -> Hebr
+        case 0x93090000u: // jye -> Hebr
+        case 0x8C0B0000u: // lad -> Hebr
+        case 0x8E4B0000u: // lsd -> Hebr
+        case 0x9A330000u: // trg -> Hebr
+        case 0x8CF80000u: // yhd -> Hebr
+        case 0x79690000u: // yi -> Hebr
+        case 0x9D180000u: // yih -> Hebr
+        case 0x8E980000u: // yud -> Hebr
+        case 0xBE390000u: // zrp -> Hebr
+            return SCRIPT_CODES[37u];
+        case 0xD1670000u: // hlu -> Hluw
+            return SCRIPT_CODES[38u];
+        case 0xA5A70000u: // hnj -> Hmnp
+        case 0xDACC0000u: // mww -> Hmnp
+            return SCRIPT_CODES[39u];
+        case 0xCE640000u: // ett -> Ital
+        case 0x8A4E0000u: // osc -> Ital
+        case 0xB4CF0000u: // pgn -> Ital
+        case 0x92B70000u: // xve -> Ital
+            return SCRIPT_CODES[40u];
+        case 0xA24E0000u: // osi -> Java
+        case 0xC8930000u: // tes -> Java
+            return SCRIPT_CODES[41u];
+        case 0xC9800000u: // ams -> Jpan
+        case 0x6A610000u: // ja -> Jpan
+        case 0xA1D80000u: // yoi -> Jpan
+            return SCRIPT_CODES[42u];
+        case 0xE1440000u: // eky -> Kali
+        case 0xE2AA0000u: // kvy -> Kali
+        case 0xD30A0000u: // kyu -> Kali
+            return SCRIPT_CODES[43u];
+        case 0xB5000000u: // ain -> Kana
+        case 0xD3110000u: // ryu -> Kana
+            return SCRIPT_CODES[44u];
+        case 0xD80A0000u: // kaw -> Kawi
+            return SCRIPT_CODES[45u];
+        case 0x8CCF0000u: // pgd -> Khar
+        case 0x822F0000u: // pra -> Khar
+            return SCRIPT_CODES[46u];
+        case 0x86210000u: // brb -> Khmr
+        case 0x6B6D0000u: // km -> Khmr
+        case 0xC62A0000u: // krr -> Khmr
+        case 0xD62A0000u: // krv -> Khmr
+        case 0xE54E0000u: // okz -> Khmr
+        case 0x844F0000u: // pcb -> Khmr
+        case 0x81510000u: // rka -> Khmr
+        case 0xD1920000u: // smu -> Khmr
+        case 0xD1F30000u: // tpu -> Khmr
+        case 0xB0F70000u: // xhm -> Khmr
+            return SCRIPT_CODES[47u];
+        case 0xCD590000u: // zkt -> Kits
+            return SCRIPT_CODES[48u];
+        case 0xDA210000u: // brw -> Knda
+        case 0x80AA0000u: // kfa -> Knda
+        case 0x8CAA0000u: // kfd -> Knda
+        case 0x98AA0000u: // kfg -> Knda
+        case 0x6B6E0000u: // kn -> Knda
+        case 0xE0530000u: // tcy -> Knda
+        case 0x8D950000u: // vmd -> Knda
+            return SCRIPT_CODES[49u];
+        case 0x6B6F0000u: // ko -> Kore
+            return SCRIPT_CODES[50u];
+        case 0xD2820000u: // cuu -> Lana
+        case 0x9D4A0000u: // kkh -> Lana
+        case 0x8DCD0000u: // nod -> Lana
+            return SCRIPT_CODES[51u];
+        case 0xA9600000u: // alk -> Laoo
+        case 0xD6210000u: // brv -> Laoo
+        case 0x992A0000u: // kjg -> Laoo
+        case 0x968A0000u: // kuf -> Laoo
+        case 0xB82B0000u: // lbo -> Laoo
+        case 0x6C6F0000u: // lo -> Laoo
+        case 0xC04D0000u: // ncq -> Laoo
+        case 0xCCCD0000u: // ngt -> Laoo
+        case 0xB8EF0000u: // pho -> Laoo
+        case 0xCC520000u: // sct -> Laoo
+        case 0xC2120000u: // sqq -> Laoo
+        case 0xCA520000u: // sss -> Laoo
+        case 0x9E730000u: // tth -> Laoo
+        case 0xBA730000u: // tto -> Laoo
+            return SCRIPT_CODES[52u];
+        case 0xAD860000u: // gml -> Latf
+            return SCRIPT_CODES[53u];
+        case 0x80CC0000u: // mga -> Latg
+            return SCRIPT_CODES[54u];
+        case 0x61610000u: // aa -> Latn
+        case 0x80000000u: // aaa -> Latn
+        case 0x84000000u: // aab -> Latn
+        case 0x88000000u: // aac -> Latn
+        case 0x8C000000u: // aad -> Latn
+        case 0x90000000u: // aae -> Latn
+        case 0x98000000u: // aag -> Latn
+        case 0x9C000000u: // aah -> Latn
+        case 0xA0000000u: // aai -> Latn
+        case 0xA8000000u: // aak -> Latn
+        case 0xAC000000u: // aal -> Latn
+        case 0xB4000000u: // aan -> Latn
+        case 0xBC000000u: // aap -> Latn
+        case 0xC0000000u: // aaq -> Latn
+        case 0xC8000000u: // aas -> Latn
+        case 0xD0000000u: // aau -> Latn
+        case 0xD8000000u: // aaw -> Latn
+        case 0xDC000000u: // aax -> Latn
+        case 0xE4000000u: // aaz -> Latn
+        case 0x80200000u: // aba -> Latn
+        case 0x84200000u: // abb -> Latn
+        case 0x88200000u: // abc -> Latn
+        case 0x8C200000u: // abd -> Latn
+        case 0x90200000u: // abe -> Latn
+        case 0x94200000u: // abf -> Latn
+        case 0x98200000u: // abg -> Latn
+        case 0xA0200000u: // abi -> Latn
+        case 0xB0200000u: // abm -> Latn
+        case 0xB4200000u: // abn -> Latn
+        case 0xB8200000u: // abo -> Latn
+        case 0xBC200000u: // abp -> Latn
+        case 0xC4200000u: // abr -> Latn
+        case 0xC8200000u: // abs -> Latn
+        case 0xCC200000u: // abt -> Latn
+        case 0xD0200000u: // abu -> Latn
+        case 0xD8200000u: // abw -> Latn
+        case 0xDC200000u: // abx -> Latn
+        case 0xE0200000u: // aby -> Latn
+        case 0xE4200000u: // abz -> Latn
+        case 0x80400000u: // aca -> Latn
+        case 0x84400000u: // acb -> Latn
+        case 0x8C400000u: // acd -> Latn
+        case 0x90400000u: // ace -> Latn
+        case 0x94400000u: // acf -> Latn
+        case 0x9C400000u: // ach -> Latn
+        case 0xB4400000u: // acn -> Latn
+        case 0xBC400000u: // acp -> Latn
+        case 0xC4400000u: // acr -> Latn
+        case 0xC8400000u: // acs -> Latn
+        case 0xCC400000u: // act -> Latn
+        case 0xD0400000u: // acu -> Latn
+        case 0xD4400000u: // acv -> Latn
+        case 0xE0400000u: // acy -> Latn
+        case 0xE4400000u: // acz -> Latn
+        case 0x80600000u: // ada -> Latn
+        case 0x84600000u: // adb -> Latn
+        case 0x8C600000u: // add -> Latn
+        case 0x90600000u: // ade -> Latn
+        case 0x98600000u: // adg -> Latn
+        case 0x9C600000u: // adh -> Latn
+        case 0xA0600000u: // adi -> Latn
+        case 0xA4600000u: // adj -> Latn
+        case 0xAC600000u: // adl -> Latn
+        case 0xB4600000u: // adn -> Latn
+        case 0xB8600000u: // ado -> Latn
+        case 0xC0600000u: // adq -> Latn
+        case 0xC4600000u: // adr -> Latn
+        case 0xCC600000u: // adt -> Latn
+        case 0xD0600000u: // adu -> Latn
+        case 0xD8600000u: // adw -> Latn
+        case 0xE4600000u: // adz -> Latn
+        case 0x80800000u: // aea -> Latn
+        case 0xA8800000u: // aek -> Latn
+        case 0xAC800000u: // ael -> Latn
+        case 0xB0800000u: // aem -> Latn
+        case 0xC4800000u: // aer -> Latn
+        case 0xD0800000u: // aeu -> Latn
+        case 0xD8800000u: // aew -> Latn
+        case 0xE0800000u: // aey -> Latn
+        case 0xE4800000u: // aez -> Latn
+        case 0x61660000u: // af -> Latn
+        case 0x8CA00000u: // afd -> Latn
+        case 0x90A00000u: // afe -> Latn
+        case 0x9CA00000u: // afh -> Latn
+        case 0xA0A00000u: // afi -> Latn
+        case 0xA8A00000u: // afk -> Latn
+        case 0xB4A00000u: // afn -> Latn
+        case 0xB8A00000u: // afo -> Latn
+        case 0xBCA00000u: // afp -> Latn
+        case 0xC8A00000u: // afs -> Latn
+        case 0xD0A00000u: // afu -> Latn
+        case 0xE4A00000u: // afz -> Latn
+        case 0x80C00000u: // aga -> Latn
+        case 0x84C00000u: // agb -> Latn
+        case 0x88C00000u: // agc -> Latn
+        case 0x8CC00000u: // agd -> Latn
+        case 0x90C00000u: // age -> Latn
+        case 0x94C00000u: // agf -> Latn
+        case 0x98C00000u: // agg -> Latn
+        case 0x9CC00000u: // agh -> Latn
+        case 0xA8C00000u: // agk -> Latn
+        case 0xACC00000u: // agl -> Latn
+        case 0xB0C00000u: // agm -> Latn
+        case 0xB4C00000u: // agn -> Latn
+        case 0xB8C00000u: // ago -> Latn
+        case 0xC0C00000u: // agq -> Latn
+        case 0xC4C00000u: // agr -> Latn
+        case 0xC8C00000u: // ags -> Latn
+        case 0xCCC00000u: // agt -> Latn
+        case 0xD0C00000u: // agu -> Latn
+        case 0xD4C00000u: // agv -> Latn
+        case 0xD8C00000u: // agw -> Latn
+        case 0xE0C00000u: // agy -> Latn
+        case 0xE4C00000u: // agz -> Latn
+        case 0x80E00000u: // aha -> Latn
+        case 0x84E00000u: // ahb -> Latn
+        case 0x9CE00000u: // ahh -> Latn
+        case 0xA0E00000u: // ahi -> Latn
+        case 0xA8E00000u: // ahk -> Latn
+        case 0xACE00000u: // ahl -> Latn
+        case 0xB0E00000u: // ahm -> Latn
+        case 0xB4E00000u: // ahn -> Latn
+        case 0xBCE00000u: // ahp -> Latn
+        case 0xC8E00000u: // ahs -> Latn
+        case 0xCCE00000u: // aht -> Latn
+        case 0x81000000u: // aia -> Latn
+        case 0x89000000u: // aic -> Latn
+        case 0x8D000000u: // aid -> Latn
+        case 0x91000000u: // aie -> Latn
+        case 0x95000000u: // aif -> Latn
+        case 0x99000000u: // aig -> Latn
+        case 0xA9000000u: // aik -> Latn
+        case 0xAD000000u: // ail -> Latn
+        case 0xB1000000u: // aim -> Latn
+        case 0xBD000000u: // aip -> Latn
+        case 0xC5000000u: // air -> Latn
+        case 0xCD000000u: // ait -> Latn
+        case 0xD9000000u: // aiw -> Latn
+        case 0xDD000000u: // aix -> Latn
+        case 0xE1000000u: // aiy -> Latn
+        case 0x81200000u: // aja -> Latn
+        case 0x99200000u: // ajg -> Latn
+        case 0xA1200000u: // aji -> Latn
+        case 0xB5200000u: // ajn -> Latn
+        case 0xD9200000u: // ajw -> Latn
+        case 0xE5200000u: // ajz -> Latn
+        case 0x616B0000u: // ak -> Latn
+        case 0x85400000u: // akb -> Latn
+        case 0x89400000u: // akc -> Latn
+        case 0x8D400000u: // akd -> Latn
+        case 0x91400000u: // ake -> Latn
+        case 0x95400000u: // akf -> Latn
+        case 0x99400000u: // akg -> Latn
+        case 0x9D400000u: // akh -> Latn
+        case 0xA1400000u: // aki -> Latn
+        case 0xAD400000u: // akl -> Latn
+        case 0xB9400000u: // ako -> Latn
+        case 0xBD400000u: // akp -> Latn
+        case 0xC1400000u: // akq -> Latn
+        case 0xC5400000u: // akr -> Latn
+        case 0xC9400000u: // aks -> Latn
+        case 0xCD400000u: // akt -> Latn
+        case 0xD1400000u: // aku -> Latn
+        case 0xD9400000u: // akw -> Latn
+        case 0xE5400000u: // akz -> Latn
+        case 0x81600000u: // ala -> Latn
+        case 0x89600000u: // alc -> Latn
+        case 0x8D600000u: // ald -> Latn
+        case 0x91600000u: // ale -> Latn
+        case 0x95600000u: // alf -> Latn
+        case 0x9D600000u: // alh -> Latn
+        case 0xA1600000u: // ali -> Latn
+        case 0xA5600000u: // alj -> Latn
+        case 0xB1600000u: // alm -> Latn
+        case 0xB5600000u: // aln -> Latn
+        case 0xB9600000u: // alo -> Latn
+        case 0xBD600000u: // alp -> Latn
+        case 0xC1600000u: // alq -> Latn
+        case 0xD1600000u: // alu -> Latn
+        case 0xDD600000u: // alx -> Latn
+        case 0xE1600000u: // aly -> Latn
+        case 0xE5600000u: // alz -> Latn
+        case 0x81800000u: // ama -> Latn
+        case 0x85800000u: // amb -> Latn
+        case 0x89800000u: // amc -> Latn
+        case 0x91800000u: // ame -> Latn
+        case 0x95800000u: // amf -> Latn
+        case 0x99800000u: // amg -> Latn
+        case 0xA1800000u: // ami -> Latn
+        case 0xA5800000u: // amj -> Latn
+        case 0xA9800000u: // amk -> Latn
+        case 0xB1800000u: // amm -> Latn
+        case 0xB5800000u: // amn -> Latn
+        case 0xB9800000u: // amo -> Latn
+        case 0xBD800000u: // amp -> Latn
+        case 0xC1800000u: // amq -> Latn
+        case 0xC5800000u: // amr -> Latn
+        case 0xCD800000u: // amt -> Latn
+        case 0xD1800000u: // amu -> Latn
+        case 0xD5800000u: // amv -> Latn
+        case 0xDD800000u: // amx -> Latn
+        case 0xE1800000u: // amy -> Latn
+        case 0xE5800000u: // amz -> Latn
+        case 0x616E0000u: // an -> Latn
+        case 0x81A00000u: // ana -> Latn
+        case 0x85A00000u: // anb -> Latn
+        case 0x89A00000u: // anc -> Latn
+        case 0x8DA00000u: // and -> Latn
+        case 0x91A00000u: // ane -> Latn
+        case 0x95A00000u: // anf -> Latn
+        case 0x99A00000u: // ang -> Latn
+        case 0x9DA00000u: // anh -> Latn
+        case 0xA5A00000u: // anj -> Latn
+        case 0xA9A00000u: // ank -> Latn
+        case 0xADA00000u: // anl -> Latn
+        case 0xB1A00000u: // anm -> Latn
+        case 0xB5A00000u: // ann -> Latn
+        case 0xB9A00000u: // ano -> Latn
+        case 0xC9A00000u: // ans -> Latn
+        case 0xCDA00000u: // ant -> Latn
+        case 0xD5A00000u: // anv -> Latn
+        case 0xD9A00000u: // anw -> Latn
+        case 0xDDA00000u: // anx -> Latn
+        case 0xE1A00000u: // any -> Latn
+        case 0xE5A00000u: // anz -> Latn
+        case 0x81C00000u: // aoa -> Latn
+        case 0x85C00000u: // aob -> Latn
+        case 0x89C00000u: // aoc -> Latn
+        case 0x8DC00000u: // aod -> Latn
+        case 0x91C00000u: // aoe -> Latn
+        case 0x95C00000u: // aof -> Latn
+        case 0x99C00000u: // aog -> Latn
+        case 0xA1C00000u: // aoi -> Latn
+        case 0xA5C00000u: // aoj -> Latn
+        case 0xA9C00000u: // aok -> Latn
+        case 0xADC00000u: // aol -> Latn
+        case 0xB1C00000u: // aom -> Latn
+        case 0xB5C00000u: // aon -> Latn
+        case 0xC5C00000u: // aor -> Latn
+        case 0xC9C00000u: // aos -> Latn
+        case 0xDDC00000u: // aox -> Latn
+        case 0xE5C00000u: // aoz -> Latn
+        case 0x85E00000u: // apb -> Latn
+        case 0x91E00000u: // ape -> Latn
+        case 0x95E00000u: // apf -> Latn
+        case 0x99E00000u: // apg -> Latn
+        case 0xA1E00000u: // api -> Latn
+        case 0xA5E00000u: // apj -> Latn
+        case 0xA9E00000u: // apk -> Latn
+        case 0xADE00000u: // apl -> Latn
+        case 0xB1E00000u: // apm -> Latn
+        case 0xB5E00000u: // apn -> Latn
+        case 0xB9E00000u: // apo -> Latn
+        case 0xBDE00000u: // app -> Latn
+        case 0xC5E00000u: // apr -> Latn
+        case 0xC9E00000u: // aps -> Latn
+        case 0xCDE00000u: // apt -> Latn
+        case 0xD1E00000u: // apu -> Latn
+        case 0xD5E00000u: // apv -> Latn
+        case 0xD9E00000u: // apw -> Latn
+        case 0xDDE00000u: // apx -> Latn
+        case 0xE1E00000u: // apy -> Latn
+        case 0xE5E00000u: // apz -> Latn
+        case 0x8E000000u: // aqd -> Latn
+        case 0x9A000000u: // aqg -> Latn
+        case 0xAA000000u: // aqk -> Latn
+        case 0xB2000000u: // aqm -> Latn
+        case 0xB6000000u: // aqn -> Latn
+        case 0xC6000000u: // aqr -> Latn
+        case 0xCE000000u: // aqt -> Latn
+        case 0xE6000000u: // aqz -> Latn
+        case 0x8E200000u: // ard -> Latn
+        case 0x92200000u: // are -> Latn
+        case 0x9E200000u: // arh -> Latn
+        case 0xA2200000u: // ari -> Latn
+        case 0xA6200000u: // arj -> Latn
+        case 0xAA200000u: // ark -> Latn
+        case 0xAE200000u: // arl -> Latn
+        case 0xB6200000u: // arn -> Latn
+        case 0xBA200000u: // aro -> Latn
+        case 0xBE200000u: // arp -> Latn
+        case 0xC6200000u: // arr -> Latn
+        case 0xD2200000u: // aru -> Latn
+        case 0xDA200000u: // arw -> Latn
+        case 0xDE200000u: // arx -> Latn
+        case 0x82400000u: // asa -> Latn
+        case 0x86400000u: // asb -> Latn
+        case 0x8A400000u: // asc -> Latn
+        case 0x9A400000u: // asg -> Latn
+        case 0x9E400000u: // ash -> Latn
+        case 0xA2400000u: // asi -> Latn
+        case 0xA6400000u: // asj -> Latn
+        case 0xAE400000u: // asl -> Latn
+        case 0xB6400000u: // asn -> Latn
+        case 0xBA400000u: // aso -> Latn
+        case 0xCA400000u: // ass -> Latn
+        case 0xCE400000u: // ast -> Latn
+        case 0xD2400000u: // asu -> Latn
+        case 0xD6400000u: // asv -> Latn
+        case 0xDE400000u: // asx -> Latn
+        case 0xE2400000u: // asy -> Latn
+        case 0xE6400000u: // asz -> Latn
+        case 0x82600000u: // ata -> Latn
+        case 0x86600000u: // atb -> Latn
+        case 0x8A600000u: // atc -> Latn
+        case 0x8E600000u: // atd -> Latn
+        case 0x92600000u: // ate -> Latn
+        case 0x9A600000u: // atg -> Latn
+        case 0xA2600000u: // ati -> Latn
+        case 0xA6600000u: // atj -> Latn
+        case 0xAA600000u: // atk -> Latn
+        case 0xAE600000u: // atl -> Latn
+        case 0xB2600000u: // atm -> Latn
+        case 0xBA600000u: // ato -> Latn
+        case 0xBE600000u: // atp -> Latn
+        case 0xC2600000u: // atq -> Latn
+        case 0xC6600000u: // atr -> Latn
+        case 0xCA600000u: // ats -> Latn
+        case 0xCE600000u: // att -> Latn
+        case 0xD2600000u: // atu -> Latn
+        case 0xDA600000u: // atw -> Latn
+        case 0xDE600000u: // atx -> Latn
+        case 0xE2600000u: // aty -> Latn
+        case 0xE6600000u: // atz -> Latn
+        case 0x82800000u: // aua -> Latn
+        case 0x8A800000u: // auc -> Latn
+        case 0x8E800000u: // aud -> Latn
+        case 0x9A800000u: // aug -> Latn
+        case 0x9E800000u: // auh -> Latn
+        case 0xA2800000u: // aui -> Latn
+        case 0xAA800000u: // auk -> Latn
+        case 0xAE800000u: // aul -> Latn
+        case 0xB2800000u: // aum -> Latn
+        case 0xB6800000u: // aun -> Latn
+        case 0xBA800000u: // auo -> Latn
+        case 0xBE800000u: // aup -> Latn
+        case 0xC2800000u: // auq -> Latn
+        case 0xC6800000u: // aur -> Latn
+        case 0xCE800000u: // aut -> Latn
+        case 0xD2800000u: // auu -> Latn
+        case 0xDA800000u: // auw -> Latn
+        case 0xE2800000u: // auy -> Latn
+        case 0x86A00000u: // avb -> Latn
+        case 0xA2A00000u: // avi -> Latn
+        case 0xAAA00000u: // avk -> Latn
+        case 0xB2A00000u: // avm -> Latn
+        case 0xB6A00000u: // avn -> Latn
+        case 0xBAA00000u: // avo -> Latn
+        case 0xCAA00000u: // avs -> Latn
+        case 0xCEA00000u: // avt -> Latn
+        case 0xD2A00000u: // avu -> Latn
+        case 0xD6A00000u: // avv -> Latn
+        case 0x86C00000u: // awb -> Latn
+        case 0x8AC00000u: // awc -> Latn
+        case 0x92C00000u: // awe -> Latn
+        case 0x9AC00000u: // awg -> Latn
+        case 0x9EC00000u: // awh -> Latn
+        case 0xA2C00000u: // awi -> Latn
+        case 0xAAC00000u: // awk -> Latn
+        case 0xB2C00000u: // awm -> Latn
+        case 0xBAC00000u: // awo -> Latn
+        case 0xC6C00000u: // awr -> Latn
+        case 0xCAC00000u: // aws -> Latn
+        case 0xCEC00000u: // awt -> Latn
+        case 0xD2C00000u: // awu -> Latn
+        case 0xD6C00000u: // awv -> Latn
+        case 0xDAC00000u: // aww -> Latn
+        case 0xDEC00000u: // awx -> Latn
+        case 0xE2C00000u: // awy -> Latn
+        case 0x86E00000u: // axb -> Latn
+        case 0x92E00000u: // axe -> Latn
+        case 0x9AE00000u: // axg -> Latn
+        case 0xAAE00000u: // axk -> Latn
+        case 0xAEE00000u: // axl -> Latn
+        case 0xDEE00000u: // axx -> Latn
+        case 0x61790000u: // ay -> Latn
+        case 0x83000000u: // aya -> Latn
+        case 0x87000000u: // ayb -> Latn
+        case 0x8B000000u: // ayc -> Latn
+        case 0x8F000000u: // ayd -> Latn
+        case 0x93000000u: // aye -> Latn
+        case 0x9B000000u: // ayg -> Latn
+        case 0xA3000000u: // ayi -> Latn
+        case 0xAB000000u: // ayk -> Latn
+        case 0xBB000000u: // ayo -> Latn
+        case 0xC3000000u: // ayq -> Latn
+        case 0xCB000000u: // ays -> Latn
+        case 0xCF000000u: // ayt -> Latn
+        case 0xD3000000u: // ayu -> Latn
+        case 0xE7000000u: // ayz -> Latn
+        case 0x617A0000u: // az -> Latn
+        case 0x8F200000u: // azd -> Latn
+        case 0x9B200000u: // azg -> Latn
+        case 0xB3200000u: // azm -> Latn
+        case 0xB7200000u: // azn -> Latn
+        case 0xBB200000u: // azo -> Latn
+        case 0xCF200000u: // azt -> Latn
+        case 0xE7200000u: // azz -> Latn
+        case 0x80010000u: // baa -> Latn
+        case 0x84010000u: // bab -> Latn
+        case 0x88010000u: // bac -> Latn
+        case 0x90010000u: // bae -> Latn
+        case 0x94010000u: // baf -> Latn
+        case 0x98010000u: // bag -> Latn
+        case 0x9C010000u: // bah -> Latn
+        case 0xA4010000u: // baj -> Latn
+        case 0xB4010000u: // ban -> Latn
+        case 0xB8010000u: // bao -> Latn
+        case 0xC4010000u: // bar -> Latn
+        case 0xC8010000u: // bas -> Latn
+        case 0xD0010000u: // bau -> Latn
+        case 0xD4010000u: // bav -> Latn
+        case 0xD8010000u: // baw -> Latn
+        case 0xE0010000u: // bay -> Latn
+        case 0x80210000u: // bba -> Latn
+        case 0x84210000u: // bbb -> Latn
+        case 0x88210000u: // bbc -> Latn
+        case 0x8C210000u: // bbd -> Latn
+        case 0x90210000u: // bbe -> Latn
+        case 0x94210000u: // bbf -> Latn
+        case 0x98210000u: // bbg -> Latn
+        case 0xA0210000u: // bbi -> Latn
+        case 0xA4210000u: // bbj -> Latn
+        case 0xA8210000u: // bbk -> Latn
+        case 0xB0210000u: // bbm -> Latn
+        case 0xB4210000u: // bbn -> Latn
+        case 0xB8210000u: // bbo -> Latn
+        case 0xBC210000u: // bbp -> Latn
+        case 0xC0210000u: // bbq -> Latn
+        case 0xC4210000u: // bbr -> Latn
+        case 0xC8210000u: // bbs -> Latn
+        case 0xCC210000u: // bbt -> Latn
+        case 0xD0210000u: // bbu -> Latn
+        case 0xD4210000u: // bbv -> Latn
+        case 0xD8210000u: // bbw -> Latn
+        case 0xDC210000u: // bbx -> Latn
+        case 0xE0210000u: // bby -> Latn
+        case 0x80410000u: // bca -> Latn
+        case 0x84410000u: // bcb -> Latn
+        case 0x8C410000u: // bcd -> Latn
+        case 0x90410000u: // bce -> Latn
+        case 0x94410000u: // bcf -> Latn
+        case 0x98410000u: // bcg -> Latn
+        case 0x9C410000u: // bch -> Latn
+        case 0xA0410000u: // bci -> Latn
+        case 0xA4410000u: // bcj -> Latn
+        case 0xA8410000u: // bck -> Latn
+        case 0xB0410000u: // bcm -> Latn
+        case 0xB4410000u: // bcn -> Latn
+        case 0xB8410000u: // bco -> Latn
+        case 0xBC410000u: // bcp -> Latn
+        case 0xC4410000u: // bcr -> Latn
+        case 0xC8410000u: // bcs -> Latn
+        case 0xCC410000u: // bct -> Latn
+        case 0xD0410000u: // bcu -> Latn
+        case 0xD4410000u: // bcv -> Latn
+        case 0xD8410000u: // bcw -> Latn
+        case 0xE0410000u: // bcy -> Latn
+        case 0xE4410000u: // bcz -> Latn
+        case 0x80610000u: // bda -> Latn
+        case 0x84610000u: // bdb -> Latn
+        case 0x88610000u: // bdc -> Latn
+        case 0x8C610000u: // bdd -> Latn
+        case 0x90610000u: // bde -> Latn
+        case 0x94610000u: // bdf -> Latn
+        case 0x98610000u: // bdg -> Latn
+        case 0x9C610000u: // bdh -> Latn
+        case 0xA0610000u: // bdi -> Latn
+        case 0xA4610000u: // bdj -> Latn
+        case 0xA8610000u: // bdk -> Latn
+        case 0xAC610000u: // bdl -> Latn
+        case 0xB0610000u: // bdm -> Latn
+        case 0xB4610000u: // bdn -> Latn
+        case 0xB8610000u: // bdo -> Latn
+        case 0xBC610000u: // bdp -> Latn
+        case 0xC0610000u: // bdq -> Latn
+        case 0xC4610000u: // bdr -> Latn
+        case 0xC8610000u: // bds -> Latn
+        case 0xCC610000u: // bdt -> Latn
+        case 0xD0610000u: // bdu -> Latn
+        case 0xD8610000u: // bdw -> Latn
+        case 0xDC610000u: // bdx -> Latn
+        case 0xE0610000u: // bdy -> Latn
+        case 0x80810000u: // bea -> Latn
+        case 0x84810000u: // beb -> Latn
+        case 0x88810000u: // bec -> Latn
+        case 0x8C810000u: // bed -> Latn
+        case 0x94810000u: // bef -> Latn
+        case 0x9C810000u: // beh -> Latn
+        case 0xA0810000u: // bei -> Latn
+        case 0xA8810000u: // bek -> Latn
+        case 0xB0810000u: // bem -> Latn
+        case 0xB8810000u: // beo -> Latn
+        case 0xBC810000u: // bep -> Latn
+        case 0xC0810000u: // beq -> Latn
+        case 0xC8810000u: // bes -> Latn
+        case 0xCC810000u: // bet -> Latn
+        case 0xD0810000u: // beu -> Latn
+        case 0xD4810000u: // bev -> Latn
+        case 0xD8810000u: // bew -> Latn
+        case 0xDC810000u: // bex -> Latn
+        case 0xE0810000u: // bey -> Latn
+        case 0xE4810000u: // bez -> Latn
+        case 0x80A10000u: // bfa -> Latn
+        case 0x88A10000u: // bfc -> Latn
+        case 0x8CA10000u: // bfd -> Latn
+        case 0x90A10000u: // bfe -> Latn
+        case 0x94A10000u: // bff -> Latn
+        case 0x98A10000u: // bfg -> Latn
+        case 0x9CA10000u: // bfh -> Latn
+        case 0xA4A10000u: // bfj -> Latn
+        case 0xACA10000u: // bfl -> Latn
+        case 0xB0A10000u: // bfm -> Latn
+        case 0xB4A10000u: // bfn -> Latn
+        case 0xB8A10000u: // bfo -> Latn
+        case 0xBCA10000u: // bfp -> Latn
+        case 0xC8A10000u: // bfs -> Latn
+        case 0xDCA10000u: // bfx -> Latn
+        case 0x80C10000u: // bga -> Latn
+        case 0x84C10000u: // bgb -> Latn
+        case 0x94C10000u: // bgf -> Latn
+        case 0x98C10000u: // bgg -> Latn
+        case 0xA0C10000u: // bgi -> Latn
+        case 0xA4C10000u: // bgj -> Latn
+        case 0xB8C10000u: // bgo -> Latn
+        case 0xC4C10000u: // bgr -> Latn
+        case 0xC8C10000u: // bgs -> Latn
+        case 0xCCC10000u: // bgt -> Latn
+        case 0xD0C10000u: // bgu -> Latn
+        case 0xD4C10000u: // bgv -> Latn
+        case 0xE0C10000u: // bgy -> Latn
+        case 0xE4C10000u: // bgz -> Latn
+        case 0x88E10000u: // bhc -> Latn
+        case 0x94E10000u: // bhf -> Latn
+        case 0x98E10000u: // bhg -> Latn
+        case 0xACE10000u: // bhl -> Latn
+        case 0xBCE10000u: // bhp -> Latn
+        case 0xC0E10000u: // bhq -> Latn
+        case 0xC4E10000u: // bhr -> Latn
+        case 0xC8E10000u: // bhs -> Latn
+        case 0xD4E10000u: // bhv -> Latn
+        case 0xD8E10000u: // bhw -> Latn
+        case 0xE0E10000u: // bhy -> Latn
+        case 0xE4E10000u: // bhz -> Latn
+        case 0x62690000u: // bi -> Latn
+        case 0x81010000u: // bia -> Latn
+        case 0x85010000u: // bib -> Latn
+        case 0x8D010000u: // bid -> Latn
+        case 0x91010000u: // bie -> Latn
+        case 0x95010000u: // bif -> Latn
+        case 0x99010000u: // big -> Latn
+        case 0xA9010000u: // bik -> Latn
+        case 0xAD010000u: // bil -> Latn
+        case 0xB1010000u: // bim -> Latn
+        case 0xB5010000u: // bin -> Latn
+        case 0xB9010000u: // bio -> Latn
+        case 0xBD010000u: // bip -> Latn
+        case 0xC1010000u: // biq -> Latn
+        case 0xC5010000u: // bir -> Latn
+        case 0xCD010000u: // bit -> Latn
+        case 0xD1010000u: // biu -> Latn
+        case 0xD5010000u: // biv -> Latn
+        case 0xD9010000u: // biw -> Latn
+        case 0xE5010000u: // biz -> Latn
+        case 0x81210000u: // bja -> Latn
+        case 0x85210000u: // bjb -> Latn
+        case 0x89210000u: // bjc -> Latn
+        case 0x99210000u: // bjg -> Latn
+        case 0x9D210000u: // bjh -> Latn
+        case 0xA1210000u: // bji -> Latn
+        case 0xA9210000u: // bjk -> Latn
+        case 0xAD210000u: // bjl -> Latn
+        case 0xB5210000u: // bjn -> Latn
+        case 0xB9210000u: // bjo -> Latn
+        case 0xBD210000u: // bjp -> Latn
+        case 0xC5210000u: // bjr -> Latn
+        case 0xC9210000u: // bjs -> Latn
+        case 0xCD210000u: // bjt -> Latn
+        case 0xD1210000u: // bju -> Latn
+        case 0xD5210000u: // bjv -> Latn
+        case 0xD9210000u: // bjw -> Latn
+        case 0xDD210000u: // bjx -> Latn
+        case 0xE1210000u: // bjy -> Latn
+        case 0xE5210000u: // bjz -> Latn
+        case 0x81410000u: // bka -> Latn
+        case 0x89410000u: // bkc -> Latn
+        case 0x8D410000u: // bkd -> Latn
+        case 0x95410000u: // bkf -> Latn
+        case 0x99410000u: // bkg -> Latn
+        case 0x9D410000u: // bkh -> Latn
+        case 0xA1410000u: // bki -> Latn
+        case 0xA5410000u: // bkj -> Latn
+        case 0xAD410000u: // bkl -> Latn
+        case 0xB1410000u: // bkm -> Latn
+        case 0xB5410000u: // bkn -> Latn
+        case 0xB9410000u: // bko -> Latn
+        case 0xBD410000u: // bkp -> Latn
+        case 0xC1410000u: // bkq -> Latn
+        case 0xC5410000u: // bkr -> Latn
+        case 0xC9410000u: // bks -> Latn
+        case 0xCD410000u: // bkt -> Latn
+        case 0xD1410000u: // bku -> Latn
+        case 0xD5410000u: // bkv -> Latn
+        case 0xD9410000u: // bkw -> Latn
+        case 0xDD410000u: // bkx -> Latn
+        case 0xE1410000u: // bky -> Latn
+        case 0xE5410000u: // bkz -> Latn
+        case 0x81610000u: // bla -> Latn
+        case 0x85610000u: // blb -> Latn
+        case 0x89610000u: // blc -> Latn
+        case 0x8D610000u: // bld -> Latn
+        case 0x91610000u: // ble -> Latn
+        case 0x95610000u: // blf -> Latn
+        case 0x9D610000u: // blh -> Latn
+        case 0xA1610000u: // bli -> Latn
+        case 0xA5610000u: // blj -> Latn
+        case 0xB1610000u: // blm -> Latn
+        case 0xB5610000u: // bln -> Latn
+        case 0xB9610000u: // blo -> Latn
+        case 0xBD610000u: // blp -> Latn
+        case 0xC1610000u: // blq -> Latn
+        case 0xC5610000u: // blr -> Latn
+        case 0xC9610000u: // bls -> Latn
+        case 0xD5610000u: // blv -> Latn
+        case 0xD9610000u: // blw -> Latn
+        case 0xDD610000u: // blx -> Latn
+        case 0xE1610000u: // bly -> Latn
+        case 0xE5610000u: // blz -> Latn
+        case 0x626D0000u: // bm -> Latn
+        case 0x81810000u: // bma -> Latn
+        case 0x85810000u: // bmb -> Latn
+        case 0x89810000u: // bmc -> Latn
+        case 0x8D810000u: // bmd -> Latn
+        case 0x91810000u: // bme -> Latn
+        case 0x95810000u: // bmf -> Latn
+        case 0x99810000u: // bmg -> Latn
+        case 0x9D810000u: // bmh -> Latn
+        case 0xA1810000u: // bmi -> Latn
+        case 0xA9810000u: // bmk -> Latn
+        case 0xAD810000u: // bml -> Latn
+        case 0xB1810000u: // bmm -> Latn
+        case 0xB5810000u: // bmn -> Latn
+        case 0xB9810000u: // bmo -> Latn
+        case 0xBD810000u: // bmp -> Latn
+        case 0xC1810000u: // bmq -> Latn
+        case 0xC5810000u: // bmr -> Latn
+        case 0xC9810000u: // bms -> Latn
+        case 0xD1810000u: // bmu -> Latn
+        case 0xD5810000u: // bmv -> Latn
+        case 0xD9810000u: // bmw -> Latn
+        case 0xDD810000u: // bmx -> Latn
+        case 0xE5810000u: // bmz -> Latn
+        case 0x81A10000u: // bna -> Latn
+        case 0x85A10000u: // bnb -> Latn
+        case 0x89A10000u: // bnc -> Latn
+        case 0x8DA10000u: // bnd -> Latn
+        case 0x91A10000u: // bne -> Latn
+        case 0x95A10000u: // bnf -> Latn
+        case 0x99A10000u: // bng -> Latn
+        case 0xA1A10000u: // bni -> Latn
+        case 0xA5A10000u: // bnj -> Latn
+        case 0xA9A10000u: // bnk -> Latn
+        case 0xB1A10000u: // bnm -> Latn
+        case 0xB5A10000u: // bnn -> Latn
+        case 0xB9A10000u: // bno -> Latn
+        case 0xBDA10000u: // bnp -> Latn
+        case 0xC1A10000u: // bnq -> Latn
+        case 0xC5A10000u: // bnr -> Latn
+        case 0xD1A10000u: // bnu -> Latn
+        case 0xD5A10000u: // bnv -> Latn
+        case 0xD9A10000u: // bnw -> Latn
+        case 0xDDA10000u: // bnx -> Latn
+        case 0xE1A10000u: // bny -> Latn
+        case 0xE5A10000u: // bnz -> Latn
+        case 0x81C10000u: // boa -> Latn
+        case 0x85C10000u: // bob -> Latn
+        case 0x91C10000u: // boe -> Latn
+        case 0x95C10000u: // bof -> Latn
+        case 0x9DC10000u: // boh -> Latn
+        case 0xA5C10000u: // boj -> Latn
+        case 0xA9C10000u: // bok -> Latn
+        case 0xADC10000u: // bol -> Latn
+        case 0xB1C10000u: // bom -> Latn
+        case 0xB5C10000u: // bon -> Latn
+        case 0xB9C10000u: // boo -> Latn
+        case 0xBDC10000u: // bop -> Latn
+        case 0xC1C10000u: // boq -> Latn
+        case 0xC5C10000u: // bor -> Latn
+        case 0xCDC10000u: // bot -> Latn
+        case 0xD1C10000u: // bou -> Latn
+        case 0xD5C10000u: // bov -> Latn
+        case 0xD9C10000u: // bow -> Latn
+        case 0xDDC10000u: // box -> Latn
+        case 0xE1C10000u: // boy -> Latn
+        case 0xE5C10000u: // boz -> Latn
+        case 0x81E10000u: // bpa -> Latn
+        case 0x89E10000u: // bpc -> Latn
+        case 0x8DE10000u: // bpd -> Latn
+        case 0x91E10000u: // bpe -> Latn
+        case 0x99E10000u: // bpg -> Latn
+        case 0xA1E10000u: // bpi -> Latn
+        case 0xA5E10000u: // bpj -> Latn
+        case 0xA9E10000u: // bpk -> Latn
+        case 0xADE10000u: // bpl -> Latn
+        case 0xB1E10000u: // bpm -> Latn
+        case 0xB9E10000u: // bpo -> Latn
+        case 0xBDE10000u: // bpp -> Latn
+        case 0xC1E10000u: // bpq -> Latn
+        case 0xC5E10000u: // bpr -> Latn
+        case 0xC9E10000u: // bps -> Latn
+        case 0xCDE10000u: // bpt -> Latn
+        case 0xD1E10000u: // bpu -> Latn
+        case 0xD5E10000u: // bpv -> Latn
+        case 0xD9E10000u: // bpw -> Latn
+        case 0xE5E10000u: // bpz -> Latn
+        case 0x82010000u: // bqa -> Latn
+        case 0x86010000u: // bqb -> Latn
+        case 0x8A010000u: // bqc -> Latn
+        case 0x8E010000u: // bqd -> Latn
+        case 0x96010000u: // bqf -> Latn
+        case 0x9A010000u: // bqg -> Latn
+        case 0xA6010000u: // bqj -> Latn
+        case 0xAA010000u: // bqk -> Latn
+        case 0xAE010000u: // bql -> Latn
+        case 0xB2010000u: // bqm -> Latn
+        case 0xBA010000u: // bqo -> Latn
+        case 0xBE010000u: // bqp -> Latn
+        case 0xC2010000u: // bqq -> Latn
+        case 0xC6010000u: // bqr -> Latn
+        case 0xCA010000u: // bqs -> Latn
+        case 0xCE010000u: // bqt -> Latn
+        case 0xD2010000u: // bqu -> Latn
+        case 0xD6010000u: // bqv -> Latn
+        case 0xDA010000u: // bqw -> Latn
+        case 0xDE010000u: // bqx -> Latn
+        case 0xE6010000u: // bqz -> Latn
+        case 0x62720000u: // br -> Latn
+        case 0x8A210000u: // brc -> Latn
+        case 0x96210000u: // brf -> Latn
+        case 0x9A210000u: // brg -> Latn
+        case 0xA2210000u: // bri -> Latn
+        case 0xA6210000u: // brj -> Latn
+        case 0xAE210000u: // brl -> Latn
+        case 0xB2210000u: // brm -> Latn
+        case 0xB6210000u: // brn -> Latn
+        case 0xBE210000u: // brp -> Latn
+        case 0xC2210000u: // brq -> Latn
+        case 0xC6210000u: // brr -> Latn
+        case 0xCA210000u: // brs -> Latn
+        case 0xCE210000u: // brt -> Latn
+        case 0xD2210000u: // bru -> Latn
+        case 0xE2210000u: // bry -> Latn
+        case 0xE6210000u: // brz -> Latn
+        case 0x62730000u: // bs -> Latn
+        case 0x82410000u: // bsa -> Latn
+        case 0x86410000u: // bsb -> Latn
+        case 0x8A410000u: // bsc -> Latn
+        case 0x92410000u: // bse -> Latn
+        case 0x96410000u: // bsf -> Latn
+        case 0xA2410000u: // bsi -> Latn
+        case 0xA6410000u: // bsj -> Latn
+        case 0xAE410000u: // bsl -> Latn
+        case 0xB2410000u: // bsm -> Latn
+        case 0xB6410000u: // bsn -> Latn
+        case 0xBA410000u: // bso -> Latn
+        case 0xBE410000u: // bsp -> Latn
+        case 0xC6410000u: // bsr -> Latn
+        case 0xCA410000u: // bss -> Latn
+        case 0xD2410000u: // bsu -> Latn
+        case 0xD6410000u: // bsv -> Latn
+        case 0xDA410000u: // bsw -> Latn
+        case 0xDE410000u: // bsx -> Latn
+        case 0xE2410000u: // bsy -> Latn
+        case 0x82610000u: // bta -> Latn
+        case 0x8A610000u: // btc -> Latn
+        case 0x92610000u: // bte -> Latn
+        case 0x96610000u: // btf -> Latn
+        case 0x9A610000u: // btg -> Latn
+        case 0x9E610000u: // bth -> Latn
+        case 0xA2610000u: // bti -> Latn
+        case 0xA6610000u: // btj -> Latn
+        case 0xB6610000u: // btn -> Latn
+        case 0xBA610000u: // bto -> Latn
+        case 0xBE610000u: // btp -> Latn
+        case 0xC2610000u: // btq -> Latn
+        case 0xC6610000u: // btr -> Latn
+        case 0xCA610000u: // bts -> Latn
+        case 0xCE610000u: // btt -> Latn
+        case 0xD2610000u: // btu -> Latn
+        case 0xDA610000u: // btw -> Latn
+        case 0xDE610000u: // btx -> Latn
+        case 0xE2610000u: // bty -> Latn
+        case 0xE6610000u: // btz -> Latn
+        case 0x86810000u: // bub -> Latn
+        case 0x8A810000u: // buc -> Latn
+        case 0x8E810000u: // bud -> Latn
+        case 0x92810000u: // bue -> Latn
+        case 0x96810000u: // buf -> Latn
+        case 0x9A810000u: // bug -> Latn
+        case 0x9E810000u: // buh -> Latn
+        case 0xA2810000u: // bui -> Latn
+        case 0xA6810000u: // buj -> Latn
+        case 0xAA810000u: // buk -> Latn
+        case 0xB2810000u: // bum -> Latn
+        case 0xB6810000u: // bun -> Latn
+        case 0xBA810000u: // buo -> Latn
+        case 0xBE810000u: // bup -> Latn
+        case 0xC2810000u: // buq -> Latn
+        case 0xCA810000u: // bus -> Latn
+        case 0xCE810000u: // but -> Latn
+        case 0xD2810000u: // buu -> Latn
+        case 0xD6810000u: // buv -> Latn
+        case 0xDA810000u: // buw -> Latn
+        case 0xDE810000u: // bux -> Latn
+        case 0xE2810000u: // buy -> Latn
+        case 0xE6810000u: // buz -> Latn
+        case 0x82A10000u: // bva -> Latn
+        case 0x86A10000u: // bvb -> Latn
+        case 0x8AA10000u: // bvc -> Latn
+        case 0x8EA10000u: // bvd -> Latn
+        case 0x92A10000u: // bve -> Latn
+        case 0x96A10000u: // bvf -> Latn
+        case 0x9AA10000u: // bvg -> Latn
+        case 0x9EA10000u: // bvh -> Latn
+        case 0xA2A10000u: // bvi -> Latn
+        case 0xA6A10000u: // bvj -> Latn
+        case 0xAAA10000u: // bvk -> Latn
+        case 0xB2A10000u: // bvm -> Latn
+        case 0xB6A10000u: // bvn -> Latn
+        case 0xBAA10000u: // bvo -> Latn
+        case 0xC2A10000u: // bvq -> Latn
+        case 0xC6A10000u: // bvr -> Latn
+        case 0xCEA10000u: // bvt -> Latn
+        case 0xD2A10000u: // bvu -> Latn
+        case 0xD6A10000u: // bvv -> Latn
+        case 0xDAA10000u: // bvw -> Latn
+        case 0xDEA10000u: // bvx -> Latn
+        case 0xE2A10000u: // bvy -> Latn
+        case 0xE6A10000u: // bvz -> Latn
+        case 0x82C10000u: // bwa -> Latn
+        case 0x86C10000u: // bwb -> Latn
+        case 0x8AC10000u: // bwc -> Latn
+        case 0x8EC10000u: // bwd -> Latn
+        case 0x96C10000u: // bwf -> Latn
+        case 0x9AC10000u: // bwg -> Latn
+        case 0x9EC10000u: // bwh -> Latn
+        case 0xA2C10000u: // bwi -> Latn
+        case 0xA6C10000u: // bwj -> Latn
+        case 0xAAC10000u: // bwk -> Latn
+        case 0xAEC10000u: // bwl -> Latn
+        case 0xB2C10000u: // bwm -> Latn
+        case 0xBAC10000u: // bwo -> Latn
+        case 0xBEC10000u: // bwp -> Latn
+        case 0xC2C10000u: // bwq -> Latn
+        case 0xC6C10000u: // bwr -> Latn
+        case 0xCAC10000u: // bws -> Latn
+        case 0xCEC10000u: // bwt -> Latn
+        case 0xD2C10000u: // bwu -> Latn
+        case 0xDAC10000u: // bww -> Latn
+        case 0xDEC10000u: // bwx -> Latn
+        case 0xE2C10000u: // bwy -> Latn
+        case 0xE6C10000u: // bwz -> Latn
+        case 0x82E10000u: // bxa -> Latn
+        case 0x86E10000u: // bxb -> Latn
+        case 0x8AE10000u: // bxc -> Latn
+        case 0x96E10000u: // bxf -> Latn
+        case 0x9AE10000u: // bxg -> Latn
+        case 0x9EE10000u: // bxh -> Latn
+        case 0xA2E10000u: // bxi -> Latn
+        case 0xA6E10000u: // bxj -> Latn
+        case 0xAEE10000u: // bxl -> Latn
+        case 0xB6E10000u: // bxn -> Latn
+        case 0xBAE10000u: // bxo -> Latn
+        case 0xBEE10000u: // bxp -> Latn
+        case 0xC2E10000u: // bxq -> Latn
+        case 0xCAE10000u: // bxs -> Latn
+        case 0xD6E10000u: // bxv -> Latn
+        case 0xDAE10000u: // bxw -> Latn
+        case 0xE6E10000u: // bxz -> Latn
+        case 0x83010000u: // bya -> Latn
+        case 0x87010000u: // byb -> Latn
+        case 0x8B010000u: // byc -> Latn
+        case 0x8F010000u: // byd -> Latn
+        case 0x93010000u: // bye -> Latn
+        case 0x97010000u: // byf -> Latn
+        case 0xA3010000u: // byi -> Latn
+        case 0xA7010000u: // byj -> Latn
+        case 0xAB010000u: // byk -> Latn
+        case 0xAF010000u: // byl -> Latn
+        case 0xB3010000u: // bym -> Latn
+        case 0xBF010000u: // byp -> Latn
+        case 0xC7010000u: // byr -> Latn
+        case 0xCB010000u: // bys -> Latn
+        case 0xD7010000u: // byv -> Latn
+        case 0xDF010000u: // byx -> Latn
+        case 0xE7010000u: // byz -> Latn
+        case 0x83210000u: // bza -> Latn
+        case 0x87210000u: // bzb -> Latn
+        case 0x8B210000u: // bzc -> Latn
+        case 0x8F210000u: // bzd -> Latn
+        case 0x93210000u: // bze -> Latn
+        case 0x97210000u: // bzf -> Latn
+        case 0x9F210000u: // bzh -> Latn
+        case 0xA7210000u: // bzj -> Latn
+        case 0xAB210000u: // bzk -> Latn
+        case 0xAF210000u: // bzl -> Latn
+        case 0xB3210000u: // bzm -> Latn
+        case 0xB7210000u: // bzn -> Latn
+        case 0xBB210000u: // bzo -> Latn
+        case 0xBF210000u: // bzp -> Latn
+        case 0xC3210000u: // bzq -> Latn
+        case 0xC7210000u: // bzr -> Latn
+        case 0xCF210000u: // bzt -> Latn
+        case 0xD3210000u: // bzu -> Latn
+        case 0xD7210000u: // bzv -> Latn
+        case 0xDB210000u: // bzw -> Latn
+        case 0xDF210000u: // bzx -> Latn
+        case 0xE3210000u: // bzy -> Latn
+        case 0xE7210000u: // bzz -> Latn
+        case 0x63610000u: // ca -> Latn
+        case 0x80020000u: // caa -> Latn
+        case 0x84020000u: // cab -> Latn
+        case 0x88020000u: // cac -> Latn
+        case 0x8C020000u: // cad -> Latn
+        case 0x90020000u: // cae -> Latn
+        case 0x94020000u: // caf -> Latn
+        case 0x98020000u: // cag -> Latn
+        case 0x9C020000u: // cah -> Latn
+        case 0xA4020000u: // caj -> Latn
+        case 0xA8020000u: // cak -> Latn
+        case 0xAC020000u: // cal -> Latn
+        case 0xB0020000u: // cam -> Latn
+        case 0xB4020000u: // can -> Latn
+        case 0xB8020000u: // cao -> Latn
+        case 0xBC020000u: // cap -> Latn
+        case 0xC0020000u: // caq -> Latn
+        case 0xC4020000u: // car -> Latn
+        case 0xC8020000u: // cas -> Latn
+        case 0xD4020000u: // cav -> Latn
+        case 0xD8020000u: // caw -> Latn
+        case 0xDC020000u: // cax -> Latn
+        case 0xE0020000u: // cay -> Latn
+        case 0xE4020000u: // caz -> Latn
+        case 0x84220000u: // cbb -> Latn
+        case 0x88220000u: // cbc -> Latn
+        case 0x8C220000u: // cbd -> Latn
+        case 0x98220000u: // cbg -> Latn
+        case 0xA0220000u: // cbi -> Latn
+        case 0xA4220000u: // cbj -> Latn
+        case 0xA8220000u: // cbk -> Latn
+        case 0xAC220000u: // cbl -> Latn
+        case 0xB8220000u: // cbo -> Latn
+        case 0xC0220000u: // cbq -> Latn
+        case 0xC4220000u: // cbr -> Latn
+        case 0xC8220000u: // cbs -> Latn
+        case 0xCC220000u: // cbt -> Latn
+        case 0xD0220000u: // cbu -> Latn
+        case 0xD4220000u: // cbv -> Latn
+        case 0xD8220000u: // cbw -> Latn
+        case 0xE0220000u: // cby -> Latn
+        case 0x88420000u: // ccc -> Latn
+        case 0x8C420000u: // ccd -> Latn
+        case 0x90420000u: // cce -> Latn
+        case 0x98420000u: // ccg -> Latn
+        case 0x9C420000u: // cch -> Latn
+        case 0xA4420000u: // ccj -> Latn
+        case 0xAC420000u: // ccl -> Latn
+        case 0xB0420000u: // ccm -> Latn
+        case 0xB8420000u: // cco -> Latn
+        case 0xC4420000u: // ccr -> Latn
+        case 0x94620000u: // cdf -> Latn
+        case 0xC4620000u: // cdr -> Latn
+        case 0x80820000u: // cea -> Latn
+        case 0x84820000u: // ceb -> Latn
+        case 0x98820000u: // ceg -> Latn
+        case 0xA8820000u: // cek -> Latn
+        case 0xB4820000u: // cen -> Latn
+        case 0xCC820000u: // cet -> Latn
+        case 0xE0820000u: // cey -> Latn
+        case 0x80A20000u: // cfa -> Latn
+        case 0x8CA20000u: // cfd -> Latn
+        case 0x98A20000u: // cfg -> Latn
+        case 0xB0A20000u: // cfm -> Latn
+        case 0x80C20000u: // cga -> Latn
+        case 0x88C20000u: // cgc -> Latn
+        case 0x98C20000u: // cgg -> Latn
+        case 0x63680000u: // ch -> Latn
+        case 0x84E20000u: // chb -> Latn
+        case 0x8CE20000u: // chd -> Latn
+        case 0x94E20000u: // chf -> Latn
+        case 0x9CE20000u: // chh -> Latn
+        case 0xA4E20000u: // chj -> Latn
+        case 0xA8E20000u: // chk -> Latn
+        case 0xACE20000u: // chl -> Latn
+        case 0xB4E20000u: // chn -> Latn
+        case 0xB8E20000u: // cho -> Latn
+        case 0xBCE20000u: // chp -> Latn
+        case 0xC0E20000u: // chq -> Latn
+        case 0xCCE20000u: // cht -> Latn
+        case 0xD8E20000u: // chw -> Latn
+        case 0xE0E20000u: // chy -> Latn
+        case 0xE4E20000u: // chz -> Latn
+        case 0x81020000u: // cia -> Latn
+        case 0x85020000u: // cib -> Latn
+        case 0x89020000u: // cic -> Latn
+        case 0x91020000u: // cie -> Latn
+        case 0xB1020000u: // cim -> Latn
+        case 0xB5020000u: // cin -> Latn
+        case 0xBD020000u: // cip -> Latn
+        case 0xC5020000u: // cir -> Latn
+        case 0xD9020000u: // ciw -> Latn
+        case 0xE1020000u: // ciy -> Latn
+        case 0x91220000u: // cje -> Latn
+        case 0x9D220000u: // cjh -> Latn
+        case 0xA9220000u: // cjk -> Latn
+        case 0xB5220000u: // cjn -> Latn
+        case 0xB9220000u: // cjo -> Latn
+        case 0xBD220000u: // cjp -> Latn
+        case 0xC9220000u: // cjs -> Latn
+        case 0xD5220000u: // cjv -> Latn
+        case 0xAD420000u: // ckl -> Latn
+        case 0xB1420000u: // ckm -> Latn
+        case 0xB5420000u: // ckn -> Latn
+        case 0xB9420000u: // cko -> Latn
+        case 0xC1420000u: // ckq -> Latn
+        case 0xC5420000u: // ckr -> Latn
+        case 0xC9420000u: // cks -> Latn
+        case 0xD1420000u: // cku -> Latn
+        case 0xD5420000u: // ckv -> Latn
+        case 0xDD420000u: // ckx -> Latn
+        case 0xE1420000u: // cky -> Latn
+        case 0xE5420000u: // ckz -> Latn
+        case 0x81620000u: // cla -> Latn
+        case 0x89620000u: // clc -> Latn
+        case 0x91620000u: // cle -> Latn
+        case 0xA1620000u: // cli -> Latn
+        case 0xA5620000u: // clj -> Latn
+        case 0xA9620000u: // clk -> Latn
+        case 0xAD620000u: // cll -> Latn
+        case 0xB1620000u: // clm -> Latn
+        case 0xB9620000u: // clo -> Latn
+        case 0xCD620000u: // clt -> Latn
+        case 0xD1620000u: // clu -> Latn
+        case 0xE1620000u: // cly -> Latn
+        case 0x81820000u: // cma -> Latn
+        case 0x91820000u: // cme -> Latn
+        case 0xA1820000u: // cmi -> Latn
+        case 0xAD820000u: // cml -> Latn
+        case 0xB9820000u: // cmo -> Latn
+        case 0xC5820000u: // cmr -> Latn
+        case 0xC9820000u: // cms -> Latn
+        case 0xCD820000u: // cmt -> Latn
+        case 0x85A20000u: // cnb -> Latn
+        case 0x89A20000u: // cnc -> Latn
+        case 0x99A20000u: // cng -> Latn
+        case 0x9DA20000u: // cnh -> Latn
+        case 0xA1A20000u: // cni -> Latn
+        case 0xA9A20000u: // cnk -> Latn
+        case 0xADA20000u: // cnl -> Latn
+        case 0xC1A20000u: // cnq -> Latn
+        case 0xC9A20000u: // cns -> Latn
+        case 0xCDA20000u: // cnt -> Latn
+        case 0xD9A20000u: // cnw -> Latn
+        case 0xDDA20000u: // cnx -> Latn
+        case 0x636F0000u: // co -> Latn
+        case 0x81C20000u: // coa -> Latn
+        case 0x85C20000u: // cob -> Latn
+        case 0x89C20000u: // coc -> Latn
+        case 0x8DC20000u: // cod -> Latn
+        case 0x91C20000u: // coe -> Latn
+        case 0x95C20000u: // cof -> Latn
+        case 0x9DC20000u: // coh -> Latn
+        case 0xA5C20000u: // coj -> Latn
+        case 0xA9C20000u: // cok -> Latn
+        case 0xADC20000u: // col -> Latn
+        case 0xB1C20000u: // com -> Latn
+        case 0xB9C20000u: // coo -> Latn
+        case 0xC1C20000u: // coq -> Latn
+        case 0xCDC20000u: // cot -> Latn
+        case 0xD1C20000u: // cou -> Latn
+        case 0xDDC20000u: // cox -> Latn
+        case 0xE5C20000u: // coz -> Latn
+        case 0x81E20000u: // cpa -> Latn
+        case 0x85E20000u: // cpb -> Latn
+        case 0x89E20000u: // cpc -> Latn
+        case 0xA1E20000u: // cpi -> Latn
+        case 0xB5E20000u: // cpn -> Latn
+        case 0xB9E20000u: // cpo -> Latn
+        case 0xC9E20000u: // cps -> Latn
+        case 0xD1E20000u: // cpu -> Latn
+        case 0xDDE20000u: // cpx -> Latn
+        case 0xE1E20000u: // cpy -> Latn
+        case 0x8E020000u: // cqd -> Latn
+        case 0x82220000u: // cra -> Latn
+        case 0x86220000u: // crb -> Latn
+        case 0x8A220000u: // crc -> Latn
+        case 0x8E220000u: // crd -> Latn
+        case 0x96220000u: // crf -> Latn
+        case 0x9A220000u: // crg -> Latn
+        case 0xA2220000u: // cri -> Latn
+        case 0xB6220000u: // crn -> Latn
+        case 0xBA220000u: // cro -> Latn
+        case 0xC2220000u: // crq -> Latn
+        case 0xCA220000u: // crs -> Latn
+        case 0xCE220000u: // crt -> Latn
+        case 0xD6220000u: // crv -> Latn
+        case 0xDA220000u: // crw -> Latn
+        case 0xDE220000u: // crx -> Latn
+        case 0xE2220000u: // cry -> Latn
+        case 0xE6220000u: // crz -> Latn
+        case 0x63730000u: // cs -> Latn
+        case 0x82420000u: // csa -> Latn
+        case 0x86420000u: // csb -> Latn
+        case 0xA6420000u: // csj -> Latn
+        case 0xAA420000u: // csk -> Latn
+        case 0xB2420000u: // csm -> Latn
+        case 0xBA420000u: // cso -> Latn
+        case 0xCA420000u: // css -> Latn
+        case 0xCE420000u: // cst -> Latn
+        case 0xD6420000u: // csv -> Latn
+        case 0xE2420000u: // csy -> Latn
+        case 0xE6420000u: // csz -> Latn
+        case 0x82620000u: // cta -> Latn
+        case 0x8A620000u: // ctc -> Latn
+        case 0x92620000u: // cte -> Latn
+        case 0x9E620000u: // cth -> Latn
+        case 0xAE620000u: // ctl -> Latn
+        case 0xB2620000u: // ctm -> Latn
+        case 0xBA620000u: // cto -> Latn
+        case 0xBE620000u: // ctp -> Latn
+        case 0xCA620000u: // cts -> Latn
+        case 0xD2620000u: // ctu -> Latn
+        case 0xE6620000u: // ctz -> Latn
+        case 0x82820000u: // cua -> Latn
+        case 0x86820000u: // cub -> Latn
+        case 0x8A820000u: // cuc -> Latn
+        case 0x9E820000u: // cuh -> Latn
+        case 0xA2820000u: // cui -> Latn
+        case 0xA6820000u: // cuj -> Latn
+        case 0xAA820000u: // cuk -> Latn
+        case 0xAE820000u: // cul -> Latn
+        case 0xBA820000u: // cuo -> Latn
+        case 0xBE820000u: // cup -> Latn
+        case 0xCE820000u: // cut -> Latn
+        case 0xD6820000u: // cuv -> Latn
+        case 0xDE820000u: // cux -> Latn
+        case 0xE2820000u: // cuy -> Latn
+        case 0x9AA20000u: // cvg -> Latn
+        case 0xB6A20000u: // cvn -> Latn
+        case 0x82C20000u: // cwa -> Latn
+        case 0x86C20000u: // cwb -> Latn
+        case 0x92C20000u: // cwe -> Latn
+        case 0x9AC20000u: // cwg -> Latn
+        case 0xCEC20000u: // cwt -> Latn
+        case 0x9EE20000u: // cxh -> Latn
+        case 0x63790000u: // cy -> Latn
+        case 0x83020000u: // cya -> Latn
+        case 0x87020000u: // cyb -> Latn
+        case 0xBB020000u: // cyo -> Latn
+        case 0xB7220000u: // czn -> Latn
+        case 0xCF220000u: // czt -> Latn
+        case 0x64610000u: // da -> Latn
+        case 0x80030000u: // daa -> Latn
+        case 0x88030000u: // dac -> Latn
+        case 0x8C030000u: // dad -> Latn
+        case 0x90030000u: // dae -> Latn
+        case 0x98030000u: // dag -> Latn
+        case 0x9C030000u: // dah -> Latn
+        case 0xA0030000u: // dai -> Latn
+        case 0xA4030000u: // daj -> Latn
+        case 0xA8030000u: // dak -> Latn
+        case 0xAC030000u: // dal -> Latn
+        case 0xB0030000u: // dam -> Latn
+        case 0xB8030000u: // dao -> Latn
+        case 0xC8030000u: // das -> Latn
+        case 0xD0030000u: // dau -> Latn
+        case 0xD4030000u: // dav -> Latn
+        case 0xD8030000u: // daw -> Latn
+        case 0xDC030000u: // dax -> Latn
+        case 0xE4030000u: // daz -> Latn
+        case 0x80230000u: // dba -> Latn
+        case 0x84230000u: // dbb -> Latn
+        case 0x8C230000u: // dbd -> Latn
+        case 0x90230000u: // dbe -> Latn
+        case 0x94230000u: // dbf -> Latn
+        case 0x98230000u: // dbg -> Latn
+        case 0xA0230000u: // dbi -> Latn
+        case 0xA4230000u: // dbj -> Latn
+        case 0xAC230000u: // dbl -> Latn
+        case 0xB0230000u: // dbm -> Latn
+        case 0xB4230000u: // dbn -> Latn
+        case 0xB8230000u: // dbo -> Latn
+        case 0xBC230000u: // dbp -> Latn
+        case 0xC0230000u: // dbq -> Latn
+        case 0xCC230000u: // dbt -> Latn
+        case 0xD0230000u: // dbu -> Latn
+        case 0xD4230000u: // dbv -> Latn
+        case 0xD8230000u: // dbw -> Latn
+        case 0xE0230000u: // dby -> Latn
+        case 0xC4430000u: // dcr -> Latn
+        case 0x80630000u: // dda -> Latn
+        case 0x8C630000u: // ddd -> Latn
+        case 0x90630000u: // dde -> Latn
+        case 0x98630000u: // ddg -> Latn
+        case 0xA0630000u: // ddi -> Latn
+        case 0xA4630000u: // ddj -> Latn
+        case 0xB4630000u: // ddn -> Latn
+        case 0xC4630000u: // ddr -> Latn
+        case 0xC8630000u: // dds -> Latn
+        case 0xD8630000u: // ddw -> Latn
+        case 0x64650000u: // de -> Latn
+        case 0x88830000u: // dec -> Latn
+        case 0x8C830000u: // ded -> Latn
+        case 0x90830000u: // dee -> Latn
+        case 0x98830000u: // deg -> Latn
+        case 0xA0830000u: // dei -> Latn
+        case 0xA8830000u: // dek -> Latn
+        case 0xAC830000u: // del -> Latn
+        case 0xB0830000u: // dem -> Latn
+        case 0xB4830000u: // den -> Latn
+        case 0xC0830000u: // deq -> Latn
+        case 0xC8830000u: // des -> Latn
+        case 0xD4830000u: // dev -> Latn
+        case 0xE4830000u: // dez -> Latn
+        case 0x80C30000u: // dga -> Latn
+        case 0x84C30000u: // dgb -> Latn
+        case 0x88C30000u: // dgc -> Latn
+        case 0x8CC30000u: // dgd -> Latn
+        case 0x90C30000u: // dge -> Latn
+        case 0x98C30000u: // dgg -> Latn
+        case 0x9CC30000u: // dgh -> Latn
+        case 0xA0C30000u: // dgi -> Latn
+        case 0xA8C30000u: // dgk -> Latn
+        case 0xB4C30000u: // dgn -> Latn
+        case 0xC4C30000u: // dgr -> Latn
+        case 0xC8C30000u: // dgs -> Latn
+        case 0xCCC30000u: // dgt -> Latn
+        case 0xD8C30000u: // dgw -> Latn
+        case 0xDCC30000u: // dgx -> Latn
+        case 0xE4C30000u: // dgz -> Latn
+        case 0x98E30000u: // dhg -> Latn
+        case 0xACE30000u: // dhl -> Latn
+        case 0xB0E30000u: // dhm -> Latn
+        case 0xC4E30000u: // dhr -> Latn
+        case 0xC8E30000u: // dhs -> Latn
+        case 0xD0E30000u: // dhu -> Latn
+        case 0xD4E30000u: // dhv -> Latn
+        case 0xDCE30000u: // dhx -> Latn
+        case 0x81030000u: // dia -> Latn
+        case 0x85030000u: // dib -> Latn
+        case 0x89030000u: // dic -> Latn
+        case 0x8D030000u: // did -> Latn
+        case 0x95030000u: // dif -> Latn
+        case 0x99030000u: // dig -> Latn
+        case 0x9D030000u: // dih -> Latn
+        case 0xA1030000u: // dii -> Latn
+        case 0xA5030000u: // dij -> Latn
+        case 0xAD030000u: // dil -> Latn
+        case 0xB5030000u: // din -> Latn
+        case 0xB9030000u: // dio -> Latn
+        case 0xBD030000u: // dip -> Latn
+        case 0xC5030000u: // dir -> Latn
+        case 0xC9030000u: // dis -> Latn
+        case 0xD1030000u: // diu -> Latn
+        case 0xD9030000u: // diw -> Latn
+        case 0xDD030000u: // dix -> Latn
+        case 0xE1030000u: // diy -> Latn
+        case 0xE5030000u: // diz -> Latn
+        case 0x81230000u: // dja -> Latn
+        case 0x85230000u: // djb -> Latn
+        case 0x89230000u: // djc -> Latn
+        case 0x8D230000u: // djd -> Latn
+        case 0x91230000u: // dje -> Latn
+        case 0x95230000u: // djf -> Latn
+        case 0xA1230000u: // dji -> Latn
+        case 0xA5230000u: // djj -> Latn
+        case 0xA9230000u: // djk -> Latn
+        case 0xB1230000u: // djm -> Latn
+        case 0xB5230000u: // djn -> Latn
+        case 0xB9230000u: // djo -> Latn
+        case 0xC5230000u: // djr -> Latn
+        case 0xD1230000u: // dju -> Latn
+        case 0xD9230000u: // djw -> Latn
+        case 0x99430000u: // dkg -> Latn
+        case 0xA9430000u: // dkk -> Latn
+        case 0xC5430000u: // dkr -> Latn
+        case 0xC9430000u: // dks -> Latn
+        case 0xDD430000u: // dkx -> Latn
+        case 0xB1630000u: // dlm -> Latn
+        case 0xB5630000u: // dln -> Latn
+        case 0x81830000u: // dma -> Latn
+        case 0x85830000u: // dmb -> Latn
+        case 0x89830000u: // dmc -> Latn
+        case 0x8D830000u: // dmd -> Latn
+        case 0x91830000u: // dme -> Latn
+        case 0x99830000u: // dmg -> Latn
+        case 0xB1830000u: // dmm -> Latn
+        case 0xB9830000u: // dmo -> Latn
+        case 0xC5830000u: // dmr -> Latn
+        case 0xC9830000u: // dms -> Latn
+        case 0xD1830000u: // dmu -> Latn
+        case 0xD5830000u: // dmv -> Latn
+        case 0xD9830000u: // dmw -> Latn
+        case 0xDD830000u: // dmx -> Latn
+        case 0xE1830000u: // dmy -> Latn
+        case 0x81A30000u: // dna -> Latn
+        case 0x8DA30000u: // dnd -> Latn
+        case 0x91A30000u: // dne -> Latn
+        case 0xA1A30000u: // dni -> Latn
+        case 0xA5A30000u: // dnj -> Latn
+        case 0xA9A30000u: // dnk -> Latn
+        case 0xB5A30000u: // dnn -> Latn
+        case 0xB9A30000u: // dno -> Latn
+        case 0xC5A30000u: // dnr -> Latn
+        case 0xCDA30000u: // dnt -> Latn
+        case 0xD9A30000u: // dnw -> Latn
+        case 0xE1A30000u: // dny -> Latn
+        case 0x81C30000u: // doa -> Latn
+        case 0x85C30000u: // dob -> Latn
+        case 0x89C30000u: // doc -> Latn
+        case 0x91C30000u: // doe -> Latn
+        case 0x95C30000u: // dof -> Latn
+        case 0x9DC30000u: // doh -> Latn
+        case 0xA9C30000u: // dok -> Latn
+        case 0xADC30000u: // dol -> Latn
+        case 0xB5C30000u: // don -> Latn
+        case 0xB9C30000u: // doo -> Latn
+        case 0xBDC30000u: // dop -> Latn
+        case 0xC5C30000u: // dor -> Latn
+        case 0xC9C30000u: // dos -> Latn
+        case 0xCDC30000u: // dot -> Latn
+        case 0xD5C30000u: // dov -> Latn
+        case 0xD9C30000u: // dow -> Latn
+        case 0xE1C30000u: // doy -> Latn
+        case 0xBDE30000u: // dpp -> Latn
+        case 0x8A230000u: // drc -> Latn
+        case 0x9A230000u: // drg -> Latn
+        case 0xA2230000u: // dri -> Latn
+        case 0xAE230000u: // drl -> Latn
+        case 0xB6230000u: // drn -> Latn
+        case 0xBA230000u: // dro -> Latn
+        case 0xCE230000u: // drt -> Latn
+        case 0xD2230000u: // dru -> Latn
+        case 0x86430000u: // dsb -> Latn
+        case 0x9E430000u: // dsh -> Latn
+        case 0xA2430000u: // dsi -> Latn
+        case 0xAA430000u: // dsk -> Latn
+        case 0xB6430000u: // dsn -> Latn
+        case 0xC2430000u: // dsq -> Latn
+        case 0x82630000u: // dta -> Latn
+        case 0x86630000u: // dtb -> Latn
+        case 0x8E630000u: // dtd -> Latn
+        case 0x9E630000u: // dth -> Latn
+        case 0xA2630000u: // dti -> Latn
+        case 0xAA630000u: // dtk -> Latn
+        case 0xB2630000u: // dtm -> Latn
+        case 0xBA630000u: // dto -> Latn
+        case 0xBE630000u: // dtp -> Latn
+        case 0xC6630000u: // dtr -> Latn
+        case 0xCA630000u: // dts -> Latn
+        case 0xCE630000u: // dtt -> Latn
+        case 0xD2630000u: // dtu -> Latn
+        case 0x82830000u: // dua -> Latn
+        case 0x8A830000u: // duc -> Latn
+        case 0x92830000u: // due -> Latn
+        case 0x96830000u: // duf -> Latn
+        case 0x9A830000u: // dug -> Latn
+        case 0xA2830000u: // dui -> Latn
+        case 0xAA830000u: // duk -> Latn
+        case 0xAE830000u: // dul -> Latn
+        case 0xB2830000u: // dum -> Latn
+        case 0xB6830000u: // dun -> Latn
+        case 0xBA830000u: // duo -> Latn
+        case 0xBE830000u: // dup -> Latn
+        case 0xC2830000u: // duq -> Latn
+        case 0xC6830000u: // dur -> Latn
+        case 0xD2830000u: // duu -> Latn
+        case 0xD6830000u: // duv -> Latn
+        case 0xDA830000u: // duw -> Latn
+        case 0xDE830000u: // dux -> Latn
+        case 0xE2830000u: // duy -> Latn
+        case 0xE6830000u: // duz -> Latn
+        case 0x82A30000u: // dva -> Latn
+        case 0x82C30000u: // dwa -> Latn
+        case 0xC6C30000u: // dwr -> Latn
+        case 0xCAC30000u: // dws -> Latn
+        case 0xD2C30000u: // dwu -> Latn
+        case 0xDAC30000u: // dww -> Latn
+        case 0xE2C30000u: // dwy -> Latn
+        case 0x83030000u: // dya -> Latn
+        case 0x87030000u: // dyb -> Latn
+        case 0x8F030000u: // dyd -> Latn
+        case 0x9B030000u: // dyg -> Latn
+        case 0xA3030000u: // dyi -> Latn
+        case 0xB3030000u: // dym -> Latn
+        case 0xB7030000u: // dyn -> Latn
+        case 0xBB030000u: // dyo -> Latn
+        case 0xC7030000u: // dyr -> Latn
+        case 0xD3030000u: // dyu -> Latn
+        case 0xE3030000u: // dyy -> Latn
+        case 0x83230000u: // dza -> Latn
+        case 0x8F230000u: // dzd -> Latn
+        case 0x93230000u: // dze -> Latn
+        case 0x9B230000u: // dzg -> Latn
+        case 0xB7230000u: // dzn -> Latn
+        case 0x80040000u: // eaa -> Latn
+        case 0x88240000u: // ebc -> Latn
+        case 0x98240000u: // ebg -> Latn
+        case 0xA8240000u: // ebk -> Latn
+        case 0xB8240000u: // ebo -> Latn
+        case 0xC4240000u: // ebr -> Latn
+        case 0xD0240000u: // ebu -> Latn
+        case 0x65650000u: // ee -> Latn
+        case 0x80A40000u: // efa -> Latn
+        case 0x90A40000u: // efe -> Latn
+        case 0xA0A40000u: // efi -> Latn
+        case 0x80C40000u: // ega -> Latn
+        case 0xACC40000u: // egl -> Latn
+        case 0xB0C40000u: // egm -> Latn
+        case 0xB8C40000u: // ego -> Latn
+        case 0xD0E40000u: // ehu -> Latn
+        case 0xBD040000u: // eip -> Latn
+        case 0xCD040000u: // eit -> Latn
+        case 0xD5040000u: // eiv -> Latn
+        case 0x81240000u: // eja -> Latn
+        case 0x81440000u: // eka -> Latn
+        case 0x91440000u: // eke -> Latn
+        case 0x99440000u: // ekg -> Latn
+        case 0xA1440000u: // eki -> Latn
+        case 0xAD440000u: // ekl -> Latn
+        case 0xB1440000u: // ekm -> Latn
+        case 0xB9440000u: // eko -> Latn
+        case 0xBD440000u: // ekp -> Latn
+        case 0xC5440000u: // ekr -> Latn
+        case 0x91640000u: // ele -> Latn
+        case 0xA9640000u: // elk -> Latn
+        case 0xB1640000u: // elm -> Latn
+        case 0xB9640000u: // elo -> Latn
+        case 0xD1640000u: // elu -> Latn
+        case 0x81840000u: // ema -> Latn
+        case 0x85840000u: // emb -> Latn
+        case 0x91840000u: // eme -> Latn
+        case 0xA1840000u: // emi -> Latn
+        case 0xB1840000u: // emm -> Latn
+        case 0xB5840000u: // emn -> Latn
+        case 0xBD840000u: // emp -> Latn
+        case 0xC9840000u: // ems -> Latn
+        case 0xD9840000u: // emw -> Latn
+        case 0xDD840000u: // emx -> Latn
+        case 0xE5840000u: // emz -> Latn
+        case 0x656E0000u: // en -> Latn
+        case 0x81A40000u: // ena -> Latn
+        case 0x85A40000u: // enb -> Latn
+        case 0x89A40000u: // enc -> Latn
+        case 0x8DA40000u: // end -> Latn
+        case 0xADA40000u: // enl -> Latn
+        case 0xB1A40000u: // enm -> Latn
+        case 0xB5A40000u: // enn -> Latn
+        case 0xB9A40000u: // eno -> Latn
+        case 0xC1A40000u: // enq -> Latn
+        case 0xC5A40000u: // enr -> Latn
+        case 0xD5A40000u: // env -> Latn
+        case 0xD9A40000u: // enw -> Latn
+        case 0xDDA40000u: // enx -> Latn
+        case 0x656F0000u: // eo -> Latn
+        case 0xCDC40000u: // eot -> Latn
+        case 0xA1E40000u: // epi -> Latn
+        case 0x9A240000u: // erg -> Latn
+        case 0x9E240000u: // erh -> Latn
+        case 0xA2240000u: // eri -> Latn
+        case 0xAA240000u: // erk -> Latn
+        case 0xC6240000u: // err -> Latn
+        case 0xCA240000u: // ers -> Latn
+        case 0xCE240000u: // ert -> Latn
+        case 0xDA240000u: // erw -> Latn
+        case 0x65730000u: // es -> Latn
+        case 0x92440000u: // ese -> Latn
+        case 0xA2440000u: // esi -> Latn
+        case 0xB2440000u: // esm -> Latn
+        case 0xCA440000u: // ess -> Latn
+        case 0xD2440000u: // esu -> Latn
+        case 0xE2440000u: // esy -> Latn
+        case 0x65740000u: // et -> Latn
+        case 0x86640000u: // etb -> Latn
+        case 0xB6640000u: // etn -> Latn
+        case 0xBA640000u: // eto -> Latn
+        case 0xC6640000u: // etr -> Latn
+        case 0xCA640000u: // ets -> Latn
+        case 0xD2640000u: // etu -> Latn
+        case 0xDE640000u: // etx -> Latn
+        case 0xE6640000u: // etz -> Latn
+        case 0x65750000u: // eu -> Latn
+        case 0x8E840000u: // eud -> Latn
+        case 0x9EA40000u: // evh -> Latn
+        case 0xBAC40000u: // ewo -> Latn
+        case 0xCEE40000u: // ext -> Latn
+        case 0x83040000u: // eya -> Latn
+        case 0xBB040000u: // eyo -> Latn
+        case 0x83240000u: // eza -> Latn
+        case 0x93240000u: // eze -> Latn
+        case 0x80050000u: // faa -> Latn
+        case 0x84050000u: // fab -> Latn
+        case 0x8C050000u: // fad -> Latn
+        case 0x94050000u: // faf -> Latn
+        case 0x98050000u: // fag -> Latn
+        case 0x9C050000u: // fah -> Latn
+        case 0xA0050000u: // fai -> Latn
+        case 0xA4050000u: // faj -> Latn
+        case 0xA8050000u: // fak -> Latn
+        case 0xAC050000u: // fal -> Latn
+        case 0xB0050000u: // fam -> Latn
+        case 0xB4050000u: // fan -> Latn
+        case 0xBC050000u: // fap -> Latn
+        case 0xC4050000u: // far -> Latn
+        case 0xD0050000u: // fau -> Latn
+        case 0xDC050000u: // fax -> Latn
+        case 0xAC250000u: // fbl -> Latn
+        case 0xC4850000u: // fer -> Latn
+        case 0x66660000u: // ff -> Latn
+        case 0xA0A50000u: // ffi -> Latn
+        case 0xB0A50000u: // ffm -> Latn
+        case 0xC4C50000u: // fgr -> Latn
+        case 0x66690000u: // fi -> Latn
+        case 0x91050000u: // fie -> Latn
+        case 0x95050000u: // fif -> Latn
+        case 0xAD050000u: // fil -> Latn
+        case 0xBD050000u: // fip -> Latn
+        case 0xC5050000u: // fir -> Latn
+        case 0xCD050000u: // fit -> Latn
+        case 0xD9050000u: // fiw -> Latn
+        case 0x666A0000u: // fj -> Latn
+        case 0xA9450000u: // fkk -> Latn
+        case 0xD5450000u: // fkv -> Latn
+        case 0x81650000u: // fla -> Latn
+        case 0x9D650000u: // flh -> Latn
+        case 0xA1650000u: // fli -> Latn
+        case 0xAD650000u: // fll -> Latn
+        case 0xB5650000u: // fln -> Latn
+        case 0xC5650000u: // flr -> Latn
+        case 0xE1650000u: // fly -> Latn
+        case 0xBD850000u: // fmp -> Latn
+        case 0x85A50000u: // fnb -> Latn
+        case 0x99A50000u: // fng -> Latn
+        case 0xA1A50000u: // fni -> Latn
+        case 0x666F0000u: // fo -> Latn
+        case 0x8DC50000u: // fod -> Latn
+        case 0xA1C50000u: // foi -> Latn
+        case 0xB1C50000u: // fom -> Latn
+        case 0xB5C50000u: // fon -> Latn
+        case 0xC5C50000u: // for -> Latn
+        case 0xC9C50000u: // fos -> Latn
+        case 0x91E50000u: // fpe -> Latn
+        case 0xCA050000u: // fqs -> Latn
+        case 0x66720000u: // fr -> Latn
+        case 0x8A250000u: // frc -> Latn
+        case 0x8E250000u: // frd -> Latn
+        case 0xAA250000u: // frk -> Latn
+        case 0xB2250000u: // frm -> Latn
+        case 0xBA250000u: // fro -> Latn
+        case 0xBE250000u: // frp -> Latn
+        case 0xC2250000u: // frq -> Latn
+        case 0xC6250000u: // frr -> Latn
+        case 0xCA250000u: // frs -> Latn
+        case 0xCE250000u: // frt -> Latn
+        case 0x8E850000u: // fud -> Latn
+        case 0x92850000u: // fue -> Latn
+        case 0x96850000u: // fuf -> Latn
+        case 0x9E850000u: // fuh -> Latn
+        case 0xA2850000u: // fui -> Latn
+        case 0xB2850000u: // fum -> Latn
+        case 0xB6850000u: // fun -> Latn
+        case 0xC2850000u: // fuq -> Latn
+        case 0xC6850000u: // fur -> Latn
+        case 0xCE850000u: // fut -> Latn
+        case 0xD2850000u: // fuu -> Latn
+        case 0xD6850000u: // fuv -> Latn
+        case 0xE2850000u: // fuy -> Latn
+        case 0xC6A50000u: // fvr -> Latn
+        case 0x82C50000u: // fwa -> Latn
+        case 0x92C50000u: // fwe -> Latn
+        case 0x66790000u: // fy -> Latn
+        case 0x67610000u: // ga -> Latn
+        case 0x80060000u: // gaa -> Latn
+        case 0x84060000u: // gab -> Latn
+        case 0x88060000u: // gac -> Latn
+        case 0x8C060000u: // gad -> Latn
+        case 0x90060000u: // gae -> Latn
+        case 0x94060000u: // gaf -> Latn
+        case 0x98060000u: // gag -> Latn
+        case 0x9C060000u: // gah -> Latn
+        case 0xA0060000u: // gai -> Latn
+        case 0xA4060000u: // gaj -> Latn
+        case 0xA8060000u: // gak -> Latn
+        case 0xAC060000u: // gal -> Latn
+        case 0xB0060000u: // gam -> Latn
+        case 0xB8060000u: // gao -> Latn
+        case 0xBC060000u: // gap -> Latn
+        case 0xC4060000u: // gar -> Latn
+        case 0xCC060000u: // gat -> Latn
+        case 0xD8060000u: // gaw -> Latn
+        case 0xDC060000u: // gax -> Latn
+        case 0xE0060000u: // gay -> Latn
+        case 0x80260000u: // gba -> Latn
+        case 0x84260000u: // gbb -> Latn
+        case 0x8C260000u: // gbd -> Latn
+        case 0x90260000u: // gbe -> Latn
+        case 0x94260000u: // gbf -> Latn
+        case 0x98260000u: // gbg -> Latn
+        case 0x9C260000u: // gbh -> Latn
+        case 0xA0260000u: // gbi -> Latn
+        case 0xB4260000u: // gbn -> Latn
+        case 0xBC260000u: // gbp -> Latn
+        case 0xC0260000u: // gbq -> Latn
+        case 0xC4260000u: // gbr -> Latn
+        case 0xC8260000u: // gbs -> Latn
+        case 0xD0260000u: // gbu -> Latn
+        case 0xD4260000u: // gbv -> Latn
+        case 0xD8260000u: // gbw -> Latn
+        case 0xDC260000u: // gbx -> Latn
+        case 0xE0260000u: // gby -> Latn
+        case 0x88460000u: // gcc -> Latn
+        case 0x8C460000u: // gcd -> Latn
+        case 0x94460000u: // gcf -> Latn
+        case 0xAC460000u: // gcl -> Latn
+        case 0xB4460000u: // gcn -> Latn
+        case 0xC4460000u: // gcr -> Latn
+        case 0xCC460000u: // gct -> Latn
+        case 0x67640000u: // gd -> Latn
+        case 0x88660000u: // gdc -> Latn
+        case 0x8C660000u: // gdd -> Latn
+        case 0x90660000u: // gde -> Latn
+        case 0x94660000u: // gdf -> Latn
+        case 0x98660000u: // gdg -> Latn
+        case 0x9C660000u: // gdh -> Latn
+        case 0xA0660000u: // gdi -> Latn
+        case 0xA4660000u: // gdj -> Latn
+        case 0xA8660000u: // gdk -> Latn
+        case 0xAC660000u: // gdl -> Latn
+        case 0xB0660000u: // gdm -> Latn
+        case 0xB4660000u: // gdn -> Latn
+        case 0xC0660000u: // gdq -> Latn
+        case 0xC4660000u: // gdr -> Latn
+        case 0xCC660000u: // gdt -> Latn
+        case 0xD0660000u: // gdu -> Latn
+        case 0x80860000u: // gea -> Latn
+        case 0x84860000u: // geb -> Latn
+        case 0x88860000u: // gec -> Latn
+        case 0x8C860000u: // ged -> Latn
+        case 0x94860000u: // gef -> Latn
+        case 0x98860000u: // geg -> Latn
+        case 0x9C860000u: // geh -> Latn
+        case 0xA0860000u: // gei -> Latn
+        case 0xA4860000u: // gej -> Latn
+        case 0xA8860000u: // gek -> Latn
+        case 0xAC860000u: // gel -> Latn
+        case 0xC0860000u: // geq -> Latn
+        case 0xC8860000u: // ges -> Latn
+        case 0xD4860000u: // gev -> Latn
+        case 0xD8860000u: // gew -> Latn
+        case 0xDC860000u: // gex -> Latn
+        case 0xE0860000u: // gey -> Latn
+        case 0xA8A60000u: // gfk -> Latn
+        case 0x80C60000u: // gga -> Latn
+        case 0x84C60000u: // ggb -> Latn
+        case 0x8CC60000u: // ggd -> Latn
+        case 0x90C60000u: // gge -> Latn
+        case 0xA8C60000u: // ggk -> Latn
+        case 0xACC60000u: // ggl -> Latn
+        case 0xCCC60000u: // ggt -> Latn
+        case 0xD0C60000u: // ggu -> Latn
+        case 0xD8C60000u: // ggw -> Latn
+        case 0x88E60000u: // ghc -> Latn
+        case 0xA8E60000u: // ghk -> Latn
+        case 0xB4E60000u: // ghn -> Latn
+        case 0xC8E60000u: // ghs -> Latn
+        case 0x81060000u: // gia -> Latn
+        case 0x85060000u: // gib -> Latn
+        case 0x89060000u: // gic -> Latn
+        case 0x8D060000u: // gid -> Latn
+        case 0x91060000u: // gie -> Latn
+        case 0x9D060000u: // gih -> Latn
+        case 0xAD060000u: // gil -> Latn
+        case 0xB1060000u: // gim -> Latn
+        case 0xBD060000u: // gip -> Latn
+        case 0xC1060000u: // giq -> Latn
+        case 0xC5060000u: // gir -> Latn
+        case 0xC9060000u: // gis -> Latn
+        case 0xCD060000u: // git -> Latn
+        case 0xDD060000u: // gix -> Latn
+        case 0xE1060000u: // giy -> Latn
+        case 0xE5060000u: // giz -> Latn
+        case 0xB1260000u: // gjm -> Latn
+        case 0xB5260000u: // gjn -> Latn
+        case 0xC5260000u: // gjr -> Latn
+        case 0x81460000u: // gka -> Latn
+        case 0x8D460000u: // gkd -> Latn
+        case 0x91460000u: // gke -> Latn
+        case 0xB5460000u: // gkn -> Latn
+        case 0xB9460000u: // gko -> Latn
+        case 0xBD460000u: // gkp -> Latn
+        case 0xD1460000u: // gku -> Latn
+        case 0x676C0000u: // gl -> Latn
+        case 0x85660000u: // glb -> Latn
+        case 0x89660000u: // glc -> Latn
+        case 0xA5660000u: // glj -> Latn
+        case 0xAD660000u: // gll -> Latn
+        case 0xB9660000u: // glo -> Latn
+        case 0xC5660000u: // glr -> Latn
+        case 0xD1660000u: // glu -> Latn
+        case 0xD9660000u: // glw -> Latn
+        case 0x81860000u: // gma -> Latn
+        case 0x85860000u: // gmb -> Latn
+        case 0x8D860000u: // gmd -> Latn
+        case 0x99860000u: // gmg -> Latn
+        case 0x9D860000u: // gmh -> Latn
+        case 0xB1860000u: // gmm -> Latn
+        case 0xB5860000u: // gmn -> Latn
+        case 0xC5860000u: // gmr -> Latn
+        case 0xD1860000u: // gmu -> Latn
+        case 0xDD860000u: // gmx -> Latn
+        case 0xE5860000u: // gmz -> Latn
+        case 0x676E0000u: // gn -> Latn
+        case 0x81A60000u: // gna -> Latn
+        case 0x85A60000u: // gnb -> Latn
+        case 0x89A60000u: // gnc -> Latn
+        case 0x8DA60000u: // gnd -> Latn
+        case 0x91A60000u: // gne -> Latn
+        case 0x99A60000u: // gng -> Latn
+        case 0x9DA60000u: // gnh -> Latn
+        case 0xA1A60000u: // gni -> Latn
+        case 0xA5A60000u: // gnj -> Latn
+        case 0xA9A60000u: // gnk -> Latn
+        case 0xADA60000u: // gnl -> Latn
+        case 0xB1A60000u: // gnm -> Latn
+        case 0xB5A60000u: // gnn -> Latn
+        case 0xC1A60000u: // gnq -> Latn
+        case 0xC5A60000u: // gnr -> Latn
+        case 0xCDA60000u: // gnt -> Latn
+        case 0xD1A60000u: // gnu -> Latn
+        case 0xD9A60000u: // gnw -> Latn
+        case 0xE5A60000u: // gnz -> Latn
+        case 0x81C60000u: // goa -> Latn
+        case 0x85C60000u: // gob -> Latn
+        case 0x89C60000u: // goc -> Latn
+        case 0x8DC60000u: // god -> Latn
+        case 0x99C60000u: // gog -> Latn
+        case 0x9DC60000u: // goh -> Latn
+        case 0xA1C60000u: // goi -> Latn
+        case 0xADC60000u: // gol -> Latn
+        case 0xB9C60000u: // goo -> Latn
+        case 0xBDC60000u: // gop -> Latn
+        case 0xC1C60000u: // goq -> Latn
+        case 0xC5C60000u: // gor -> Latn
+        case 0xC9C60000u: // gos -> Latn
+        case 0xD1C60000u: // gou -> Latn
+        case 0xD5C60000u: // gov -> Latn
+        case 0xD9C60000u: // gow -> Latn
+        case 0xDDC60000u: // gox -> Latn
+        case 0xE1C60000u: // goy -> Latn
+        case 0x81E60000u: // gpa -> Latn
+        case 0x91E60000u: // gpe -> Latn
+        case 0xB5E60000u: // gpn -> Latn
+        case 0x82060000u: // gqa -> Latn
+        case 0xB6060000u: // gqn -> Latn
+        case 0xC6060000u: // gqr -> Latn
+        case 0x86260000u: // grb -> Latn
+        case 0x8E260000u: // grd -> Latn
+        case 0x9A260000u: // grg -> Latn
+        case 0x9E260000u: // grh -> Latn
+        case 0xA2260000u: // gri -> Latn
+        case 0xA6260000u: // grj -> Latn
+        case 0xB2260000u: // grm -> Latn
+        case 0xC2260000u: // grq -> Latn
+        case 0xCA260000u: // grs -> Latn
+        case 0xD6260000u: // grv -> Latn
+        case 0xDA260000u: // grw -> Latn
+        case 0xDE260000u: // grx -> Latn
+        case 0xE2260000u: // gry -> Latn
+        case 0xE6260000u: // grz -> Latn
+        case 0xAE460000u: // gsl -> Latn
+        case 0xB6460000u: // gsn -> Latn
+        case 0xBA460000u: // gso -> Latn
+        case 0xBE460000u: // gsp -> Latn
+        case 0xDA460000u: // gsw -> Latn
+        case 0x82660000u: // gta -> Latn
+        case 0xD2660000u: // gtu -> Latn
+        case 0x82860000u: // gua -> Latn
+        case 0x86860000u: // gub -> Latn
+        case 0x8A860000u: // guc -> Latn
+        case 0x8E860000u: // gud -> Latn
+        case 0x92860000u: // gue -> Latn
+        case 0x96860000u: // guf -> Latn
+        case 0x9E860000u: // guh -> Latn
+        case 0xA2860000u: // gui -> Latn
+        case 0xAA860000u: // guk -> Latn
+        case 0xAE860000u: // gul -> Latn
+        case 0xB2860000u: // gum -> Latn
+        case 0xB6860000u: // gun -> Latn
+        case 0xBA860000u: // guo -> Latn
+        case 0xBE860000u: // gup -> Latn
+        case 0xC2860000u: // guq -> Latn
+        case 0xC6860000u: // gur -> Latn
+        case 0xCE860000u: // gut -> Latn
+        case 0xD2860000u: // guu -> Latn
+        case 0xDA860000u: // guw -> Latn
+        case 0xDE860000u: // gux -> Latn
+        case 0xE6860000u: // guz -> Latn
+        case 0x67760000u: // gv -> Latn
+        case 0x82A60000u: // gva -> Latn
+        case 0x8AA60000u: // gvc -> Latn
+        case 0x92A60000u: // gve -> Latn
+        case 0x96A60000u: // gvf -> Latn
+        case 0xA6A60000u: // gvj -> Latn
+        case 0xAEA60000u: // gvl -> Latn
+        case 0xB2A60000u: // gvm -> Latn
+        case 0xB6A60000u: // gvn -> Latn
+        case 0xBAA60000u: // gvo -> Latn
+        case 0xBEA60000u: // gvp -> Latn
+        case 0xCAA60000u: // gvs -> Latn
+        case 0xE2A60000u: // gvy -> Latn
+        case 0x82C60000u: // gwa -> Latn
+        case 0x86C60000u: // gwb -> Latn
+        case 0x8EC60000u: // gwd -> Latn
+        case 0x92C60000u: // gwe -> Latn
+        case 0x9AC60000u: // gwg -> Latn
+        case 0xA2C60000u: // gwi -> Latn
+        case 0xA6C60000u: // gwj -> Latn
+        case 0xB2C60000u: // gwm -> Latn
+        case 0xB6C60000u: // gwn -> Latn
+        case 0xC6C60000u: // gwr -> Latn
+        case 0xD2C60000u: // gwu -> Latn
+        case 0xDAC60000u: // gww -> Latn
+        case 0xDEC60000u: // gwx -> Latn
+        case 0xDEE60000u: // gxx -> Latn
+        case 0x87060000u: // gyb -> Latn
+        case 0x8F060000u: // gyd -> Latn
+        case 0x93060000u: // gye -> Latn
+        case 0x97060000u: // gyf -> Latn
+        case 0x9B060000u: // gyg -> Latn
+        case 0xA3060000u: // gyi -> Latn
+        case 0xAF060000u: // gyl -> Latn
+        case 0xB3060000u: // gym -> Latn
+        case 0xB7060000u: // gyn -> Latn
+        case 0xC7060000u: // gyr -> Latn
+        case 0xE3060000u: // gyy -> Latn
+        case 0xE7060000u: // gyz -> Latn
+        case 0x83260000u: // gza -> Latn
+        case 0xB7260000u: // gzn -> Latn
+        case 0x68610000u: // ha -> Latn
+        case 0x80070000u: // haa -> Latn
+        case 0x8C070000u: // had -> Latn
+        case 0x90070000u: // hae -> Latn
+        case 0x98070000u: // hag -> Latn
+        case 0x9C070000u: // hah -> Latn
+        case 0xA0070000u: // hai -> Latn
+        case 0xA4070000u: // haj -> Latn
+        case 0xAC070000u: // hal -> Latn
+        case 0xB0070000u: // ham -> Latn
+        case 0xB4070000u: // han -> Latn
+        case 0xB8070000u: // hao -> Latn
+        case 0xBC070000u: // hap -> Latn
+        case 0xC0070000u: // haq -> Latn
+        case 0xC8070000u: // has -> Latn
+        case 0xD4070000u: // hav -> Latn
+        case 0xD8070000u: // haw -> Latn
+        case 0xDC070000u: // hax -> Latn
+        case 0xE0070000u: // hay -> Latn
+        case 0x80270000u: // hba -> Latn
+        case 0x84270000u: // hbb -> Latn
+        case 0xB4270000u: // hbn -> Latn
+        case 0xD0270000u: // hbu -> Latn
+        case 0x9C470000u: // hch -> Latn
+        case 0x8C870000u: // hed -> Latn
+        case 0x98870000u: // heg -> Latn
+        case 0x9C870000u: // heh -> Latn
+        case 0xA0870000u: // hei -> Latn
+        case 0xB0870000u: // hem -> Latn
+        case 0xB0C70000u: // hgm -> Latn
+        case 0xD8C70000u: // hgw -> Latn
+        case 0xA0E70000u: // hhi -> Latn
+        case 0xC4E70000u: // hhr -> Latn
+        case 0xE0E70000u: // hhy -> Latn
+        case 0x81070000u: // hia -> Latn
+        case 0x85070000u: // hib -> Latn
+        case 0x8D070000u: // hid -> Latn
+        case 0x99070000u: // hig -> Latn
+        case 0x9D070000u: // hih -> Latn
+        case 0xA5070000u: // hij -> Latn
+        case 0xA9070000u: // hik -> Latn
+        case 0xAD070000u: // hil -> Latn
+        case 0xB9070000u: // hio -> Latn
+        case 0xC5070000u: // hir -> Latn
+        case 0xD9070000u: // hiw -> Latn
+        case 0xDD070000u: // hix -> Latn
+        case 0xA1270000u: // hji -> Latn
+        case 0x81470000u: // hka -> Latn
+        case 0x91470000u: // hke -> Latn
+        case 0xA9470000u: // hkk -> Latn
+        case 0x81670000u: // hla -> Latn
+        case 0x8D670000u: // hld -> Latn
+        case 0xCD670000u: // hlt -> Latn
+        case 0x81870000u: // hma -> Latn
+        case 0x85870000u: // hmb -> Latn
+        case 0x95870000u: // hmf -> Latn
+        case 0xB1870000u: // hmm -> Latn
+        case 0xB5870000u: // hmn -> Latn
+        case 0xBD870000u: // hmp -> Latn
+        case 0xC5870000u: // hmr -> Latn
+        case 0xC9870000u: // hms -> Latn
+        case 0xCD870000u: // hmt -> Latn
+        case 0xD1870000u: // hmu -> Latn
+        case 0xD5870000u: // hmv -> Latn
+        case 0xD9870000u: // hmw -> Latn
+        case 0xE1870000u: // hmy -> Latn
+        case 0xE5870000u: // hmz -> Latn
+        case 0x81A70000u: // hna -> Latn
+        case 0x99A70000u: // hng -> Latn
+        case 0x9DA70000u: // hnh -> Latn
+        case 0xA1A70000u: // hni -> Latn
+        case 0xB5A70000u: // hnn -> Latn
+        case 0xC9A70000u: // hns -> Latn
+        case 0x686F0000u: // ho -> Latn
+        case 0x81C70000u: // hoa -> Latn
+        case 0x85C70000u: // hob -> Latn
+        case 0x8DC70000u: // hod -> Latn
+        case 0x91C70000u: // hoe -> Latn
+        case 0xA1C70000u: // hoi -> Latn
+        case 0xADC70000u: // hol -> Latn
+        case 0xB1C70000u: // hom -> Latn
+        case 0xB9C70000u: // hoo -> Latn
+        case 0xBDC70000u: // hop -> Latn
+        case 0xC5C70000u: // hor -> Latn
+        case 0xCDC70000u: // hot -> Latn
+        case 0xD5C70000u: // hov -> Latn
+        case 0x68720000u: // hr -> Latn
+        case 0x82270000u: // hra -> Latn
+        case 0x8A270000u: // hrc -> Latn
+        case 0x92270000u: // hre -> Latn
+        case 0xAA270000u: // hrk -> Latn
+        case 0xB2270000u: // hrm -> Latn
+        case 0xBA270000u: // hro -> Latn
+        case 0xBE270000u: // hrp -> Latn
+        case 0xD2270000u: // hru -> Latn
+        case 0xDA270000u: // hrw -> Latn
+        case 0xDE270000u: // hrx -> Latn
+        case 0x86470000u: // hsb -> Latn
+        case 0x68740000u: // ht -> Latn
+        case 0xA2670000u: // hti -> Latn
+        case 0xBA670000u: // hto -> Latn
+        case 0xCA670000u: // hts -> Latn
+        case 0xD2670000u: // htu -> Latn
+        case 0x68750000u: // hu -> Latn
+        case 0x86870000u: // hub -> Latn
+        case 0x8A870000u: // huc -> Latn
+        case 0x8E870000u: // hud -> Latn
+        case 0x92870000u: // hue -> Latn
+        case 0x96870000u: // huf -> Latn
+        case 0x9A870000u: // hug -> Latn
+        case 0x9E870000u: // huh -> Latn
+        case 0xA2870000u: // hui -> Latn
+        case 0xAA870000u: // huk -> Latn
+        case 0xAE870000u: // hul -> Latn
+        case 0xB2870000u: // hum -> Latn
+        case 0xBE870000u: // hup -> Latn
+        case 0xC6870000u: // hur -> Latn
+        case 0xCA870000u: // hus -> Latn
+        case 0xD2870000u: // huu -> Latn
+        case 0xD6870000u: // huv -> Latn
+        case 0xDA870000u: // huw -> Latn
+        case 0xDE870000u: // hux -> Latn
+        case 0x8AA70000u: // hvc -> Latn
+        case 0x92A70000u: // hve -> Latn
+        case 0xAAA70000u: // hvk -> Latn
+        case 0xB6A70000u: // hvn -> Latn
+        case 0xD6A70000u: // hvv -> Latn
+        case 0x82C70000u: // hwa -> Latn
+        case 0x8AC70000u: // hwc -> Latn
+        case 0xBAC70000u: // hwo -> Latn
+        case 0x83070000u: // hya -> Latn
+        case 0x687A0000u: // hz -> Latn
+        case 0x69610000u: // ia -> Latn
+        case 0xA0080000u: // iai -> Latn
+        case 0xB4080000u: // ian -> Latn
+        case 0xC4080000u: // iar -> Latn
+        case 0x80280000u: // iba -> Latn
+        case 0x84280000u: // ibb -> Latn
+        case 0x8C280000u: // ibd -> Latn
+        case 0x90280000u: // ibe -> Latn
+        case 0x98280000u: // ibg -> Latn
+        case 0x9C280000u: // ibh -> Latn
+        case 0xAC280000u: // ibl -> Latn
+        case 0xB0280000u: // ibm -> Latn
+        case 0xB4280000u: // ibn -> Latn
+        case 0xC4280000u: // ibr -> Latn
+        case 0xD0280000u: // ibu -> Latn
+        case 0xE0280000u: // iby -> Latn
+        case 0x80480000u: // ica -> Latn
+        case 0x9C480000u: // ich -> Latn
+        case 0xC4480000u: // icr -> Latn
+        case 0x69640000u: // id -> Latn
+        case 0x80680000u: // ida -> Latn
+        case 0x84680000u: // idb -> Latn
+        case 0x88680000u: // idc -> Latn
+        case 0x8C680000u: // idd -> Latn
+        case 0x90680000u: // ide -> Latn
+        case 0xA0680000u: // idi -> Latn
+        case 0xC4680000u: // idr -> Latn
+        case 0xC8680000u: // ids -> Latn
+        case 0xCC680000u: // idt -> Latn
+        case 0xD0680000u: // idu -> Latn
+        case 0x69650000u: // ie -> Latn
+        case 0x80A80000u: // ifa -> Latn
+        case 0x84A80000u: // ifb -> Latn
+        case 0x90A80000u: // ife -> Latn
+        case 0x94A80000u: // iff -> Latn
+        case 0xA8A80000u: // ifk -> Latn
+        case 0xB0A80000u: // ifm -> Latn
+        case 0xD0A80000u: // ifu -> Latn
+        case 0xE0A80000u: // ify -> Latn
+        case 0x69670000u: // ig -> Latn
+        case 0x84C80000u: // igb -> Latn
+        case 0x90C80000u: // ige -> Latn
+        case 0x98C80000u: // igg -> Latn
+        case 0xACC80000u: // igl -> Latn
+        case 0xB0C80000u: // igm -> Latn
+        case 0xB4C80000u: // ign -> Latn
+        case 0xB8C80000u: // igo -> Latn
+        case 0xC8C80000u: // igs -> Latn
+        case 0xD8C80000u: // igw -> Latn
+        case 0x84E80000u: // ihb -> Latn
+        case 0xA0E80000u: // ihi -> Latn
+        case 0xBCE80000u: // ihp -> Latn
+        case 0xD8E80000u: // ihw -> Latn
+        case 0xB5080000u: // iin -> Latn
+        case 0x89280000u: // ijc -> Latn
+        case 0x91280000u: // ije -> Latn
+        case 0xA5280000u: // ijj -> Latn
+        case 0xB5280000u: // ijn -> Latn
+        case 0xC9280000u: // ijs -> Latn
+        case 0x696B0000u: // ik -> Latn
+        case 0x9D480000u: // ikh -> Latn
+        case 0xA1480000u: // iki -> Latn
+        case 0xA9480000u: // ikk -> Latn
+        case 0xAD480000u: // ikl -> Latn
+        case 0xB9480000u: // iko -> Latn
+        case 0xBD480000u: // ikp -> Latn
+        case 0xC5480000u: // ikr -> Latn
+        case 0xCD480000u: // ikt -> Latn
+        case 0xD5480000u: // ikv -> Latn
+        case 0xD9480000u: // ikw -> Latn
+        case 0xDD480000u: // ikx -> Latn
+        case 0xE5480000u: // ikz -> Latn
+        case 0x81680000u: // ila -> Latn
+        case 0x85680000u: // ilb -> Latn
+        case 0x99680000u: // ilg -> Latn
+        case 0xA1680000u: // ili -> Latn
+        case 0xA9680000u: // ilk -> Latn
+        case 0xB1680000u: // ilm -> Latn
+        case 0xB9680000u: // ilo -> Latn
+        case 0xBD680000u: // ilp -> Latn
+        case 0xD1680000u: // ilu -> Latn
+        case 0xD5680000u: // ilv -> Latn
+        case 0xA1880000u: // imi -> Latn
+        case 0xAD880000u: // iml -> Latn
+        case 0xB5880000u: // imn -> Latn
+        case 0xB9880000u: // imo -> Latn
+        case 0xC5880000u: // imr -> Latn
+        case 0xC9880000u: // ims -> Latn
+        case 0xCD880000u: // imt -> Latn
+        case 0x696E0000u: // in -> Latn
+        case 0x85A80000u: // inb -> Latn
+        case 0x99A80000u: // ing -> Latn
+        case 0xA5A80000u: // inj -> Latn
+        case 0xB5A80000u: // inn -> Latn
+        case 0xB9A80000u: // ino -> Latn
+        case 0xBDA80000u: // inp -> Latn
+        case 0x696F0000u: // io -> Latn
+        case 0xD1C80000u: // iou -> Latn
+        case 0xD9C80000u: // iow -> Latn
+        case 0xA1E80000u: // ipi -> Latn
+        case 0xB9E80000u: // ipo -> Latn
+        case 0xD2080000u: // iqu -> Latn
+        case 0xDA080000u: // iqw -> Latn
+        case 0x92280000u: // ire -> Latn
+        case 0x9E280000u: // irh -> Latn
+        case 0xA2280000u: // iri -> Latn
+        case 0xAA280000u: // irk -> Latn
+        case 0xB6280000u: // irn -> Latn
+        case 0xDE280000u: // irx -> Latn
+        case 0xE2280000u: // iry -> Latn
+        case 0x69730000u: // is -> Latn
+        case 0x82480000u: // isa -> Latn
+        case 0x8A480000u: // isc -> Latn
+        case 0x8E480000u: // isd -> Latn
+        case 0x9E480000u: // ish -> Latn
+        case 0xA2480000u: // isi -> Latn
+        case 0xB2480000u: // ism -> Latn
+        case 0xB6480000u: // isn -> Latn
+        case 0xBA480000u: // iso -> Latn
+        case 0xCE480000u: // ist -> Latn
+        case 0xD2480000u: // isu -> Latn
+        case 0x69740000u: // it -> Latn
+        case 0x86680000u: // itb -> Latn
+        case 0x8E680000u: // itd -> Latn
+        case 0x92680000u: // ite -> Latn
+        case 0xA2680000u: // iti -> Latn
+        case 0xB2680000u: // itm -> Latn
+        case 0xBA680000u: // ito -> Latn
+        case 0xC6680000u: // itr -> Latn
+        case 0xCA680000u: // its -> Latn
+        case 0xCE680000u: // itt -> Latn
+        case 0xD6680000u: // itv -> Latn
+        case 0xDA680000u: // itw -> Latn
+        case 0xDE680000u: // itx -> Latn
+        case 0xE2680000u: // ity -> Latn
+        case 0xE6680000u: // itz -> Latn
+        case 0xB2880000u: // ium -> Latn
+        case 0x86A80000u: // ivb -> Latn
+        case 0xD6A80000u: // ivv -> Latn
+        case 0xAAC80000u: // iwk -> Latn
+        case 0xB2C80000u: // iwm -> Latn
+        case 0xBAC80000u: // iwo -> Latn
+        case 0xCAC80000u: // iws -> Latn
+        case 0x8AE80000u: // ixc -> Latn
+        case 0xAEE80000u: // ixl -> Latn
+        case 0x83080000u: // iya -> Latn
+        case 0xBB080000u: // iyo -> Latn
+        case 0xDF080000u: // iyx -> Latn
+        case 0x9F280000u: // izh -> Latn
+        case 0xB3280000u: // izm -> Latn
+        case 0xC7280000u: // izr -> Latn
+        case 0xE7280000u: // izz -> Latn
+        case 0x80090000u: // jaa -> Latn
+        case 0x84090000u: // jab -> Latn
+        case 0x88090000u: // jac -> Latn
+        case 0x90090000u: // jae -> Latn
+        case 0x94090000u: // jaf -> Latn
+        case 0x9C090000u: // jah -> Latn
+        case 0xA4090000u: // jaj -> Latn
+        case 0xA8090000u: // jak -> Latn
+        case 0xAC090000u: // jal -> Latn
+        case 0xB0090000u: // jam -> Latn
+        case 0xB4090000u: // jan -> Latn
+        case 0xB8090000u: // jao -> Latn
+        case 0xC0090000u: // jaq -> Latn
+        case 0xC8090000u: // jas -> Latn
+        case 0xD0090000u: // jau -> Latn
+        case 0xDC090000u: // jax -> Latn
+        case 0xE0090000u: // jay -> Latn
+        case 0xE4090000u: // jaz -> Latn
+        case 0xA0290000u: // jbi -> Latn
+        case 0xA4290000u: // jbj -> Latn
+        case 0xA8290000u: // jbk -> Latn
+        case 0xB0290000u: // jbm -> Latn
+        case 0xB8290000u: // jbo -> Latn
+        case 0xC4290000u: // jbr -> Latn
+        case 0xCC290000u: // jbt -> Latn
+        case 0xD0290000u: // jbu -> Latn
+        case 0xD8290000u: // jbw -> Latn
+        case 0x84890000u: // jeb -> Latn
+        case 0x9C890000u: // jeh -> Latn
+        case 0xA0890000u: // jei -> Latn
+        case 0xA8890000u: // jek -> Latn
+        case 0xAC890000u: // jel -> Latn
+        case 0xB4890000u: // jen -> Latn
+        case 0xC4890000u: // jer -> Latn
+        case 0xCC890000u: // jet -> Latn
+        case 0xD0890000u: // jeu -> Latn
+        case 0x84C90000u: // jgb -> Latn
+        case 0xA8C90000u: // jgk -> Latn
+        case 0xB8C90000u: // jgo -> Latn
+        case 0xA0E90000u: // jhi -> Latn
+        case 0x81090000u: // jia -> Latn
+        case 0x85090000u: // jib -> Latn
+        case 0x89090000u: // jic -> Latn
+        case 0x8D090000u: // jid -> Latn
+        case 0x91090000u: // jie -> Latn
+        case 0x99090000u: // jig -> Latn
+        case 0xAD090000u: // jil -> Latn
+        case 0xB1090000u: // jim -> Latn
+        case 0xCD090000u: // jit -> Latn
+        case 0xD1090000u: // jiu -> Latn
+        case 0xD5090000u: // jiv -> Latn
+        case 0xE1090000u: // jiy -> Latn
+        case 0xC5290000u: // jjr -> Latn
+        case 0x81490000u: // jka -> Latn
+        case 0xB9490000u: // jko -> Latn
+        case 0xD1490000u: // jku -> Latn
+        case 0x91690000u: // jle -> Latn
+        case 0x81890000u: // jma -> Latn
+        case 0x85890000u: // jmb -> Latn
+        case 0x89890000u: // jmc -> Latn
+        case 0x8D890000u: // jmd -> Latn
+        case 0xA1890000u: // jmi -> Latn
+        case 0xB5890000u: // jmn -> Latn
+        case 0xC5890000u: // jmr -> Latn
+        case 0xC9890000u: // jms -> Latn
+        case 0xD9890000u: // jmw -> Latn
+        case 0xDD890000u: // jmx -> Latn
+        case 0x99A90000u: // jng -> Latn
+        case 0xA1A90000u: // jni -> Latn
+        case 0xA5A90000u: // jnj -> Latn
+        case 0x85C90000u: // job -> Latn
+        case 0x8DC90000u: // jod -> Latn
+        case 0xC5C90000u: // jor -> Latn
+        case 0xD9C90000u: // jow -> Latn
+        case 0xC6090000u: // jqr -> Latn
+        case 0x82290000u: // jra -> Latn
+        case 0xC6290000u: // jrr -> Latn
+        case 0xCE290000u: // jrt -> Latn
+        case 0xD2290000u: // jru -> Latn
+        case 0x82890000u: // jua -> Latn
+        case 0x86890000u: // jub -> Latn
+        case 0x8E890000u: // jud -> Latn
+        case 0x9E890000u: // juh -> Latn
+        case 0xA2890000u: // jui -> Latn
+        case 0xAA890000u: // juk -> Latn
+        case 0xB2890000u: // jum -> Latn
+        case 0xBA890000u: // juo -> Latn
+        case 0xBE890000u: // jup -> Latn
+        case 0xC6890000u: // jur -> Latn
+        case 0xCE890000u: // jut -> Latn
+        case 0xD2890000u: // juu -> Latn
+        case 0xDA890000u: // juw -> Latn
+        case 0x6A760000u: // jv -> Latn
+        case 0x8EA90000u: // jvd -> Latn
+        case 0xB6A90000u: // jvn -> Latn
+        case 0x6A770000u: // jw -> Latn
+        case 0xA2C90000u: // jwi -> Latn
+        case 0xE3090000u: // jyy -> Latn
+        case 0x840A0000u: // kab -> Latn
+        case 0x880A0000u: // kac -> Latn
+        case 0x8C0A0000u: // kad -> Latn
+        case 0x980A0000u: // kag -> Latn
+        case 0x9C0A0000u: // kah -> Latn
+        case 0xA00A0000u: // kai -> Latn
+        case 0xA40A0000u: // kaj -> Latn
+        case 0xA80A0000u: // kak -> Latn
+        case 0xB00A0000u: // kam -> Latn
+        case 0xB80A0000u: // kao -> Latn
+        case 0xC00A0000u: // kaq -> Latn
+        case 0xD40A0000u: // kav -> Latn
+        case 0xDC0A0000u: // kax -> Latn
+        case 0xE00A0000u: // kay -> Latn
+        case 0x802A0000u: // kba -> Latn
+        case 0x842A0000u: // kbb -> Latn
+        case 0x882A0000u: // kbc -> Latn
+        case 0x902A0000u: // kbe -> Latn
+        case 0x9C2A0000u: // kbh -> Latn
+        case 0xA02A0000u: // kbi -> Latn
+        case 0xA42A0000u: // kbj -> Latn
+        case 0xA82A0000u: // kbk -> Latn
+        case 0xAC2A0000u: // kbl -> Latn
+        case 0xB02A0000u: // kbm -> Latn
+        case 0xB42A0000u: // kbn -> Latn
+        case 0xB82A0000u: // kbo -> Latn
+        case 0xBC2A0000u: // kbp -> Latn
+        case 0xC02A0000u: // kbq -> Latn
+        case 0xC42A0000u: // kbr -> Latn
+        case 0xC82A0000u: // kbs -> Latn
+        case 0xCC2A0000u: // kbt -> Latn
+        case 0xD42A0000u: // kbv -> Latn
+        case 0xD82A0000u: // kbw -> Latn
+        case 0xDC2A0000u: // kbx -> Latn
+        case 0xE42A0000u: // kbz -> Latn
+        case 0x844A0000u: // kcb -> Latn
+        case 0x884A0000u: // kcc -> Latn
+        case 0x8C4A0000u: // kcd -> Latn
+        case 0x904A0000u: // kce -> Latn
+        case 0x944A0000u: // kcf -> Latn
+        case 0x984A0000u: // kcg -> Latn
+        case 0x9C4A0000u: // kch -> Latn
+        case 0xA04A0000u: // kci -> Latn
+        case 0xA44A0000u: // kcj -> Latn
+        case 0xA84A0000u: // kck -> Latn
+        case 0xAC4A0000u: // kcl -> Latn
+        case 0xB04A0000u: // kcm -> Latn
+        case 0xB44A0000u: // kcn -> Latn
+        case 0xB84A0000u: // kco -> Latn
+        case 0xBC4A0000u: // kcp -> Latn
+        case 0xC04A0000u: // kcq -> Latn
+        case 0xC84A0000u: // kcs -> Latn
+        case 0xCC4A0000u: // kct -> Latn
+        case 0xD04A0000u: // kcu -> Latn
+        case 0xD44A0000u: // kcv -> Latn
+        case 0xD84A0000u: // kcw -> Latn
+        case 0xE44A0000u: // kcz -> Latn
+        case 0x806A0000u: // kda -> Latn
+        case 0x886A0000u: // kdc -> Latn
+        case 0x8C6A0000u: // kdd -> Latn
+        case 0x906A0000u: // kde -> Latn
+        case 0x946A0000u: // kdf -> Latn
+        case 0x986A0000u: // kdg -> Latn
+        case 0x9C6A0000u: // kdh -> Latn
+        case 0xA06A0000u: // kdi -> Latn
+        case 0xA46A0000u: // kdj -> Latn
+        case 0xA86A0000u: // kdk -> Latn
+        case 0xAC6A0000u: // kdl -> Latn
+        case 0xB06A0000u: // kdm -> Latn
+        case 0xB46A0000u: // kdn -> Latn
+        case 0xBC6A0000u: // kdp -> Latn
+        case 0xC46A0000u: // kdr -> Latn
+        case 0xD86A0000u: // kdw -> Latn
+        case 0xDC6A0000u: // kdx -> Latn
+        case 0xE06A0000u: // kdy -> Latn
+        case 0xE46A0000u: // kdz -> Latn
+        case 0x808A0000u: // kea -> Latn
+        case 0x848A0000u: // keb -> Latn
+        case 0x888A0000u: // kec -> Latn
+        case 0x8C8A0000u: // ked -> Latn
+        case 0x908A0000u: // kee -> Latn
+        case 0x948A0000u: // kef -> Latn
+        case 0x988A0000u: // keg -> Latn
+        case 0x9C8A0000u: // keh -> Latn
+        case 0xA08A0000u: // kei -> Latn
+        case 0xA88A0000u: // kek -> Latn
+        case 0xAC8A0000u: // kel -> Latn
+        case 0xB08A0000u: // kem -> Latn
+        case 0xB48A0000u: // ken -> Latn
+        case 0xB88A0000u: // keo -> Latn
+        case 0xC48A0000u: // ker -> Latn
+        case 0xC88A0000u: // kes -> Latn
+        case 0xD08A0000u: // keu -> Latn
+        case 0xD88A0000u: // kew -> Latn
+        case 0xE48A0000u: // kez -> Latn
+        case 0x94AA0000u: // kff -> Latn
+        case 0xACAA0000u: // kfl -> Latn
+        case 0xB4AA0000u: // kfn -> Latn
+        case 0xB8AA0000u: // kfo -> Latn
+        case 0xD4AA0000u: // kfv -> Latn
+        case 0xD8AA0000u: // kfw -> Latn
+        case 0xE4AA0000u: // kfz -> Latn
+        case 0x6B670000u: // kg -> Latn
+        case 0x80CA0000u: // kga -> Latn
+        case 0x84CA0000u: // kgb -> Latn
+        case 0x90CA0000u: // kge -> Latn
+        case 0x94CA0000u: // kgf -> Latn
+        case 0xA8CA0000u: // kgk -> Latn
+        case 0xACCA0000u: // kgl -> Latn
+        case 0xB8CA0000u: // kgo -> Latn
+        case 0xBCCA0000u: // kgp -> Latn
+        case 0xC0CA0000u: // kgq -> Latn
+        case 0xC4CA0000u: // kgr -> Latn
+        case 0xC8CA0000u: // kgs -> Latn
+        case 0xCCCA0000u: // kgt -> Latn
+        case 0xD0CA0000u: // kgu -> Latn
+        case 0xD4CA0000u: // kgv -> Latn
+        case 0xD8CA0000u: // kgw -> Latn
+        case 0xDCCA0000u: // kgx -> Latn
+        case 0x80EA0000u: // kha -> Latn
+        case 0x88EA0000u: // khc -> Latn
+        case 0x8CEA0000u: // khd -> Latn
+        case 0x90EA0000u: // khe -> Latn
+        case 0x9CEA0000u: // khh -> Latn
+        case 0xA4EA0000u: // khj -> Latn
+        case 0xACEA0000u: // khl -> Latn
+        case 0xBCEA0000u: // khp -> Latn
+        case 0xC0EA0000u: // khq -> Latn
+        case 0xC4EA0000u: // khr -> Latn
+        case 0xC8EA0000u: // khs -> Latn
+        case 0xD0EA0000u: // khu -> Latn
+        case 0xDCEA0000u: // khx -> Latn
+        case 0xE0EA0000u: // khy -> Latn
+        case 0xE4EA0000u: // khz -> Latn
+        case 0x6B690000u: // ki -> Latn
+        case 0x810A0000u: // kia -> Latn
+        case 0x850A0000u: // kib -> Latn
+        case 0x890A0000u: // kic -> Latn
+        case 0x8D0A0000u: // kid -> Latn
+        case 0x910A0000u: // kie -> Latn
+        case 0x990A0000u: // kig -> Latn
+        case 0x9D0A0000u: // kih -> Latn
+        case 0xA50A0000u: // kij -> Latn
+        case 0xAD0A0000u: // kil -> Latn
+        case 0xB90A0000u: // kio -> Latn
+        case 0xC10A0000u: // kiq -> Latn
+        case 0xC90A0000u: // kis -> Latn
+        case 0xCD0A0000u: // kit -> Latn
+        case 0xD10A0000u: // kiu -> Latn
+        case 0xD50A0000u: // kiv -> Latn
+        case 0xD90A0000u: // kiw -> Latn
+        case 0xDD0A0000u: // kix -> Latn
+        case 0xE10A0000u: // kiy -> Latn
+        case 0xE50A0000u: // kiz -> Latn
+        case 0x6B6A0000u: // kj -> Latn
+        case 0x812A0000u: // kja -> Latn
+        case 0x852A0000u: // kjb -> Latn
+        case 0x892A0000u: // kjc -> Latn
+        case 0x8D2A0000u: // kjd -> Latn
+        case 0x912A0000u: // kje -> Latn
+        case 0xA12A0000u: // kji -> Latn
+        case 0xA52A0000u: // kjj -> Latn
+        case 0xA92A0000u: // kjk -> Latn
+        case 0xB12A0000u: // kjm -> Latn
+        case 0xB52A0000u: // kjn -> Latn
+        case 0xC12A0000u: // kjq -> Latn
+        case 0xC52A0000u: // kjr -> Latn
+        case 0xC92A0000u: // kjs -> Latn
+        case 0xD12A0000u: // kju -> Latn
+        case 0xDD2A0000u: // kjx -> Latn
+        case 0xE12A0000u: // kjy -> Latn
+        case 0x814A0000u: // kka -> Latn
+        case 0x854A0000u: // kkb -> Latn
+        case 0x894A0000u: // kkc -> Latn
+        case 0x8D4A0000u: // kkd -> Latn
+        case 0x914A0000u: // kke -> Latn
+        case 0x994A0000u: // kkg -> Latn
+        case 0xA14A0000u: // kki -> Latn
+        case 0xA54A0000u: // kkj -> Latn
+        case 0xA94A0000u: // kkk -> Latn
+        case 0xAD4A0000u: // kkl -> Latn
+        case 0xB14A0000u: // kkm -> Latn
+        case 0xB94A0000u: // kko -> Latn
+        case 0xBD4A0000u: // kkp -> Latn
+        case 0xC14A0000u: // kkq -> Latn
+        case 0xC54A0000u: // kkr -> Latn
+        case 0xC94A0000u: // kks -> Latn
+        case 0xD14A0000u: // kku -> Latn
+        case 0xD54A0000u: // kkv -> Latn
+        case 0xD94A0000u: // kkw -> Latn
+        case 0xDD4A0000u: // kkx -> Latn
+        case 0xE14A0000u: // kky -> Latn
+        case 0xE54A0000u: // kkz -> Latn
+        case 0x6B6C0000u: // kl -> Latn
+        case 0x816A0000u: // kla -> Latn
+        case 0x856A0000u: // klb -> Latn
+        case 0x896A0000u: // klc -> Latn
+        case 0x8D6A0000u: // kld -> Latn
+        case 0x956A0000u: // klf -> Latn
+        case 0x996A0000u: // klg -> Latn
+        case 0x9D6A0000u: // klh -> Latn
+        case 0xA16A0000u: // kli -> Latn
+        case 0xA96A0000u: // klk -> Latn
+        case 0xAD6A0000u: // kll -> Latn
+        case 0xB16A0000u: // klm -> Latn
+        case 0xB56A0000u: // kln -> Latn
+        case 0xB96A0000u: // klo -> Latn
+        case 0xBD6A0000u: // klp -> Latn
+        case 0xC16A0000u: // klq -> Latn
+        case 0xC96A0000u: // kls -> Latn
+        case 0xCD6A0000u: // klt -> Latn
+        case 0xD16A0000u: // klu -> Latn
+        case 0xD56A0000u: // klv -> Latn
+        case 0xD96A0000u: // klw -> Latn
+        case 0xDD6A0000u: // klx -> Latn
+        case 0xE16A0000u: // kly -> Latn
+        case 0xE56A0000u: // klz -> Latn
+        case 0x818A0000u: // kma -> Latn
+        case 0x858A0000u: // kmb -> Latn
+        case 0x898A0000u: // kmc -> Latn
+        case 0x8D8A0000u: // kmd -> Latn
+        case 0x918A0000u: // kme -> Latn
+        case 0x958A0000u: // kmf -> Latn
+        case 0x998A0000u: // kmg -> Latn
+        case 0x9D8A0000u: // kmh -> Latn
+        case 0xA18A0000u: // kmi -> Latn
+        case 0xA98A0000u: // kmk -> Latn
+        case 0xAD8A0000u: // kml -> Latn
+        case 0xB18A0000u: // kmm -> Latn
+        case 0xB58A0000u: // kmn -> Latn
+        case 0xB98A0000u: // kmo -> Latn
+        case 0xBD8A0000u: // kmp -> Latn
+        case 0xC18A0000u: // kmq -> Latn
+        case 0xC98A0000u: // kms -> Latn
+        case 0xCD8A0000u: // kmt -> Latn
+        case 0xD18A0000u: // kmu -> Latn
+        case 0xD58A0000u: // kmv -> Latn
+        case 0xD98A0000u: // kmw -> Latn
+        case 0xDD8A0000u: // kmx -> Latn
+        case 0xE18A0000u: // kmy -> Latn
+        case 0x81AA0000u: // kna -> Latn
+        case 0x85AA0000u: // knb -> Latn
+        case 0x8DAA0000u: // knd -> Latn
+        case 0x91AA0000u: // kne -> Latn
+        case 0x95AA0000u: // knf -> Latn
+        case 0xA1AA0000u: // kni -> Latn
+        case 0xA5AA0000u: // knj -> Latn
+        case 0xA9AA0000u: // knk -> Latn
+        case 0xADAA0000u: // knl -> Latn
+        case 0xB1AA0000u: // knm -> Latn
+        case 0xB9AA0000u: // kno -> Latn
+        case 0xBDAA0000u: // knp -> Latn
+        case 0xC1AA0000u: // knq -> Latn
+        case 0xC5AA0000u: // knr -> Latn
+        case 0xC9AA0000u: // kns -> Latn
+        case 0xCDAA0000u: // knt -> Latn
+        case 0xD1AA0000u: // knu -> Latn
+        case 0xD5AA0000u: // knv -> Latn
+        case 0xD9AA0000u: // knw -> Latn
+        case 0xDDAA0000u: // knx -> Latn
+        case 0xE1AA0000u: // kny -> Latn
+        case 0xE5AA0000u: // knz -> Latn
+        case 0x81CA0000u: // koa -> Latn
+        case 0x89CA0000u: // koc -> Latn
+        case 0x8DCA0000u: // kod -> Latn
+        case 0x91CA0000u: // koe -> Latn
+        case 0x95CA0000u: // kof -> Latn
+        case 0x99CA0000u: // kog -> Latn
+        case 0x9DCA0000u: // koh -> Latn
+        case 0xADCA0000u: // kol -> Latn
+        case 0xB9CA0000u: // koo -> Latn
+        case 0xBDCA0000u: // kop -> Latn
+        case 0xC1CA0000u: // koq -> Latn
+        case 0xC9CA0000u: // kos -> Latn
+        case 0xCDCA0000u: // kot -> Latn
+        case 0xD1CA0000u: // kou -> Latn
+        case 0xD5CA0000u: // kov -> Latn
+        case 0xD9CA0000u: // kow -> Latn
+        case 0xE1CA0000u: // koy -> Latn
+        case 0xE5CA0000u: // koz -> Latn
+        case 0x81EA0000u: // kpa -> Latn
+        case 0x89EA0000u: // kpc -> Latn
+        case 0x8DEA0000u: // kpd -> Latn
+        case 0x91EA0000u: // kpe -> Latn
+        case 0x95EA0000u: // kpf -> Latn
+        case 0x99EA0000u: // kpg -> Latn
+        case 0x9DEA0000u: // kph -> Latn
+        case 0xA1EA0000u: // kpi -> Latn
+        case 0xA5EA0000u: // kpj -> Latn
+        case 0xA9EA0000u: // kpk -> Latn
+        case 0xADEA0000u: // kpl -> Latn
+        case 0xB1EA0000u: // kpm -> Latn
+        case 0xB5EA0000u: // kpn -> Latn
+        case 0xB9EA0000u: // kpo -> Latn
+        case 0xC1EA0000u: // kpq -> Latn
+        case 0xC5EA0000u: // kpr -> Latn
+        case 0xC9EA0000u: // kps -> Latn
+        case 0xD1EA0000u: // kpu -> Latn
+        case 0xD9EA0000u: // kpw -> Latn
+        case 0xDDEA0000u: // kpx -> Latn
+        case 0xE5EA0000u: // kpz -> Latn
+        case 0x820A0000u: // kqa -> Latn
+        case 0x860A0000u: // kqb -> Latn
+        case 0x8A0A0000u: // kqc -> Latn
+        case 0x920A0000u: // kqe -> Latn
+        case 0x960A0000u: // kqf -> Latn
+        case 0x9A0A0000u: // kqg -> Latn
+        case 0x9E0A0000u: // kqh -> Latn
+        case 0xA20A0000u: // kqi -> Latn
+        case 0xA60A0000u: // kqj -> Latn
+        case 0xAA0A0000u: // kqk -> Latn
+        case 0xAE0A0000u: // kql -> Latn
+        case 0xB20A0000u: // kqm -> Latn
+        case 0xB60A0000u: // kqn -> Latn
+        case 0xBA0A0000u: // kqo -> Latn
+        case 0xBE0A0000u: // kqp -> Latn
+        case 0xC20A0000u: // kqq -> Latn
+        case 0xC60A0000u: // kqr -> Latn
+        case 0xCA0A0000u: // kqs -> Latn
+        case 0xCE0A0000u: // kqt -> Latn
+        case 0xD20A0000u: // kqu -> Latn
+        case 0xD60A0000u: // kqv -> Latn
+        case 0xDA0A0000u: // kqw -> Latn
+        case 0xDE0A0000u: // kqx -> Latn
+        case 0xE60A0000u: // kqz -> Latn
+        case 0x6B720000u: // kr -> Latn
+        case 0x862A0000u: // krb -> Latn
+        case 0x8E2A0000u: // krd -> Latn
+        case 0x922A0000u: // kre -> Latn
+        case 0x962A0000u: // krf -> Latn
+        case 0x9E2A0000u: // krh -> Latn
+        case 0xA22A0000u: // kri -> Latn
+        case 0xA62A0000u: // krj -> Latn
+        case 0xAE2A0000u: // krl -> Latn
+        case 0xB62A0000u: // krn -> Latn
+        case 0xBE2A0000u: // krp -> Latn
+        case 0xCA2A0000u: // krs -> Latn
+        case 0xCE2A0000u: // krt -> Latn
+        case 0xDA2A0000u: // krw -> Latn
+        case 0xDE2A0000u: // krx -> Latn
+        case 0xE22A0000u: // kry -> Latn
+        case 0xE62A0000u: // krz -> Latn
+        case 0x864A0000u: // ksb -> Latn
+        case 0x8A4A0000u: // ksc -> Latn
+        case 0x8E4A0000u: // ksd -> Latn
+        case 0x924A0000u: // kse -> Latn
+        case 0x964A0000u: // ksf -> Latn
+        case 0x9A4A0000u: // ksg -> Latn
+        case 0x9E4A0000u: // ksh -> Latn
+        case 0xA24A0000u: // ksi -> Latn
+        case 0xA64A0000u: // ksj -> Latn
+        case 0xAA4A0000u: // ksk -> Latn
+        case 0xAE4A0000u: // ksl -> Latn
+        case 0xB24A0000u: // ksm -> Latn
+        case 0xB64A0000u: // ksn -> Latn
+        case 0xBA4A0000u: // kso -> Latn
+        case 0xBE4A0000u: // ksp -> Latn
+        case 0xC24A0000u: // ksq -> Latn
+        case 0xC64A0000u: // ksr -> Latn
+        case 0xCA4A0000u: // kss -> Latn
+        case 0xCE4A0000u: // kst -> Latn
+        case 0xD64A0000u: // ksv -> Latn
+        case 0xDE4A0000u: // ksx -> Latn
+        case 0x826A0000u: // kta -> Latn
+        case 0x8A6A0000u: // ktc -> Latn
+        case 0x8E6A0000u: // ktd -> Latn
+        case 0x966A0000u: // ktf -> Latn
+        case 0x9A6A0000u: // ktg -> Latn
+        case 0x9E6A0000u: // kth -> Latn
+        case 0xA26A0000u: // kti -> Latn
+        case 0xA66A0000u: // ktj -> Latn
+        case 0xAA6A0000u: // ktk -> Latn
+        case 0xB26A0000u: // ktm -> Latn
+        case 0xB66A0000u: // ktn -> Latn
+        case 0xBA6A0000u: // kto -> Latn
+        case 0xC26A0000u: // ktq -> Latn
+        case 0xCA6A0000u: // kts -> Latn
+        case 0xCE6A0000u: // ktt -> Latn
+        case 0xD26A0000u: // ktu -> Latn
+        case 0xD66A0000u: // ktv -> Latn
+        case 0xDA6A0000u: // ktw -> Latn
+        case 0xDE6A0000u: // ktx -> Latn
+        case 0xE26A0000u: // kty -> Latn
+        case 0xE66A0000u: // ktz -> Latn
+        case 0x6B750000u: // ku -> Latn
+        case 0x868A0000u: // kub -> Latn
+        case 0x8A8A0000u: // kuc -> Latn
+        case 0x8E8A0000u: // kud -> Latn
+        case 0x928A0000u: // kue -> Latn
+        case 0x9A8A0000u: // kug -> Latn
+        case 0x9E8A0000u: // kuh -> Latn
+        case 0xA28A0000u: // kui -> Latn
+        case 0xA68A0000u: // kuj -> Latn
+        case 0xAA8A0000u: // kuk -> Latn
+        case 0xAE8A0000u: // kul -> Latn
+        case 0xB68A0000u: // kun -> Latn
+        case 0xBA8A0000u: // kuo -> Latn
+        case 0xBE8A0000u: // kup -> Latn
+        case 0xC28A0000u: // kuq -> Latn
+        case 0xCA8A0000u: // kus -> Latn
+        case 0xCE8A0000u: // kut -> Latn
+        case 0xD28A0000u: // kuu -> Latn
+        case 0xD68A0000u: // kuv -> Latn
+        case 0xDA8A0000u: // kuw -> Latn
+        case 0xDE8A0000u: // kux -> Latn
+        case 0xE28A0000u: // kuy -> Latn
+        case 0xE68A0000u: // kuz -> Latn
+        case 0x86AA0000u: // kvb -> Latn
+        case 0x8AAA0000u: // kvc -> Latn
+        case 0x8EAA0000u: // kvd -> Latn
+        case 0x92AA0000u: // kve -> Latn
+        case 0x96AA0000u: // kvf -> Latn
+        case 0x9AAA0000u: // kvg -> Latn
+        case 0x9EAA0000u: // kvh -> Latn
+        case 0xA2AA0000u: // kvi -> Latn
+        case 0xA6AA0000u: // kvj -> Latn
+        case 0xAEAA0000u: // kvl -> Latn
+        case 0xB2AA0000u: // kvm -> Latn
+        case 0xB6AA0000u: // kvn -> Latn
+        case 0xBAAA0000u: // kvo -> Latn
+        case 0xBEAA0000u: // kvp -> Latn
+        case 0xC6AA0000u: // kvr -> Latn
+        case 0xD6AA0000u: // kvv -> Latn
+        case 0xDAAA0000u: // kvw -> Latn
+        case 0xE6AA0000u: // kvz -> Latn
+        case 0x6B770000u: // kw -> Latn
+        case 0x82CA0000u: // kwa -> Latn
+        case 0x86CA0000u: // kwb -> Latn
+        case 0x8ACA0000u: // kwc -> Latn
+        case 0x8ECA0000u: // kwd -> Latn
+        case 0x92CA0000u: // kwe -> Latn
+        case 0x96CA0000u: // kwf -> Latn
+        case 0x9ACA0000u: // kwg -> Latn
+        case 0x9ECA0000u: // kwh -> Latn
+        case 0xA2CA0000u: // kwi -> Latn
+        case 0xA6CA0000u: // kwj -> Latn
+        case 0xAACA0000u: // kwk -> Latn
+        case 0xAECA0000u: // kwl -> Latn
+        case 0xB2CA0000u: // kwm -> Latn
+        case 0xB6CA0000u: // kwn -> Latn
+        case 0xBACA0000u: // kwo -> Latn
+        case 0xBECA0000u: // kwp -> Latn
+        case 0xC6CA0000u: // kwr -> Latn
+        case 0xCACA0000u: // kws -> Latn
+        case 0xCECA0000u: // kwt -> Latn
+        case 0xD2CA0000u: // kwu -> Latn
+        case 0xD6CA0000u: // kwv -> Latn
+        case 0xDACA0000u: // kww -> Latn
+        case 0xE2CA0000u: // kwy -> Latn
+        case 0xE6CA0000u: // kwz -> Latn
+        case 0x82EA0000u: // kxa -> Latn
+        case 0x86EA0000u: // kxb -> Latn
+        case 0x8AEA0000u: // kxc -> Latn
+        case 0x8EEA0000u: // kxd -> Latn
+        case 0xA2EA0000u: // kxi -> Latn
+        case 0xA6EA0000u: // kxj -> Latn
+        case 0xB6EA0000u: // kxn -> Latn
+        case 0xBAEA0000u: // kxo -> Latn
+        case 0xC2EA0000u: // kxq -> Latn
+        case 0xC6EA0000u: // kxr -> Latn
+        case 0xCEEA0000u: // kxt -> Latn
+        case 0xD6EA0000u: // kxv -> Latn
+        case 0xDAEA0000u: // kxw -> Latn
+        case 0xDEEA0000u: // kxx -> Latn
+        case 0xE2EA0000u: // kxy -> Latn
+        case 0xE6EA0000u: // kxz -> Latn
+        case 0x6B795452u: // ky-TR -> Latn
+        case 0x830A0000u: // kya -> Latn
+        case 0x870A0000u: // kyb -> Latn
+        case 0x8B0A0000u: // kyc -> Latn
+        case 0x8F0A0000u: // kyd -> Latn
+        case 0x930A0000u: // kye -> Latn
+        case 0x970A0000u: // kyf -> Latn
+        case 0x9B0A0000u: // kyg -> Latn
+        case 0x9F0A0000u: // kyh -> Latn
+        case 0xA30A0000u: // kyi -> Latn
+        case 0xA70A0000u: // kyj -> Latn
+        case 0xAB0A0000u: // kyk -> Latn
+        case 0xAF0A0000u: // kyl -> Latn
+        case 0xB30A0000u: // kym -> Latn
+        case 0xB70A0000u: // kyn -> Latn
+        case 0xBB0A0000u: // kyo -> Latn
+        case 0xC30A0000u: // kyq -> Latn
+        case 0xC70A0000u: // kyr -> Latn
+        case 0xCB0A0000u: // kys -> Latn
+        case 0xCF0A0000u: // kyt -> Latn
+        case 0xDF0A0000u: // kyx -> Latn
+        case 0xE30A0000u: // kyy -> Latn
+        case 0xE70A0000u: // kyz -> Latn
+        case 0x832A0000u: // kza -> Latn
+        case 0x872A0000u: // kzb -> Latn
+        case 0x8B2A0000u: // kzc -> Latn
+        case 0x8F2A0000u: // kzd -> Latn
+        case 0x932A0000u: // kze -> Latn
+        case 0x972A0000u: // kzf -> Latn
+        case 0xA32A0000u: // kzi -> Latn
+        case 0xAB2A0000u: // kzk -> Latn
+        case 0xAF2A0000u: // kzl -> Latn
+        case 0xB32A0000u: // kzm -> Latn
+        case 0xB72A0000u: // kzn -> Latn
+        case 0xBB2A0000u: // kzo -> Latn
+        case 0xBF2A0000u: // kzp -> Latn
+        case 0xC72A0000u: // kzr -> Latn
+        case 0xCB2A0000u: // kzs -> Latn
+        case 0xD32A0000u: // kzu -> Latn
+        case 0xD72A0000u: // kzv -> Latn
+        case 0xDB2A0000u: // kzw -> Latn
+        case 0xDF2A0000u: // kzx -> Latn
+        case 0xE32A0000u: // kzy -> Latn
+        case 0xE72A0000u: // kzz -> Latn
+        case 0x6C610000u: // la -> Latn
+        case 0x800B0000u: // laa -> Latn
+        case 0x880B0000u: // lac -> Latn
+        case 0x980B0000u: // lag -> Latn
+        case 0xA00B0000u: // lai -> Latn
+        case 0xA40B0000u: // laj -> Latn
+        case 0xAC0B0000u: // lal -> Latn
+        case 0xB00B0000u: // lam -> Latn
+        case 0xB40B0000u: // lan -> Latn
+        case 0xBC0B0000u: // lap -> Latn
+        case 0xC00B0000u: // laq -> Latn
+        case 0xC40B0000u: // lar -> Latn
+        case 0xC80B0000u: // las -> Latn
+        case 0xD00B0000u: // lau -> Latn
+        case 0xD80B0000u: // law -> Latn
+        case 0xDC0B0000u: // lax -> Latn
+        case 0xE40B0000u: // laz -> Latn
+        case 0x6C620000u: // lb -> Latn
+        case 0x842B0000u: // lbb -> Latn
+        case 0xA02B0000u: // lbi -> Latn
+        case 0xAC2B0000u: // lbl -> Latn
+        case 0xB42B0000u: // lbn -> Latn
+        case 0xC02B0000u: // lbq -> Latn
+        case 0xCC2B0000u: // lbt -> Latn
+        case 0xD02B0000u: // lbu -> Latn
+        case 0xD42B0000u: // lbv -> Latn
+        case 0xD82B0000u: // lbw -> Latn
+        case 0xDC2B0000u: // lbx -> Latn
+        case 0xE02B0000u: // lby -> Latn
+        case 0xE42B0000u: // lbz -> Latn
+        case 0x884B0000u: // lcc -> Latn
+        case 0x8C4B0000u: // lcd -> Latn
+        case 0x904B0000u: // lce -> Latn
+        case 0x944B0000u: // lcf -> Latn
+        case 0x9C4B0000u: // lch -> Latn
+        case 0xAC4B0000u: // lcl -> Latn
+        case 0xB04B0000u: // lcm -> Latn
+        case 0xC04B0000u: // lcq -> Latn
+        case 0xC84B0000u: // lcs -> Latn
+        case 0x806B0000u: // lda -> Latn
+        case 0x846B0000u: // ldb -> Latn
+        case 0x8C6B0000u: // ldd -> Latn
+        case 0x986B0000u: // ldg -> Latn
+        case 0x9C6B0000u: // ldh -> Latn
+        case 0xA06B0000u: // ldi -> Latn
+        case 0xA46B0000u: // ldj -> Latn
+        case 0xA86B0000u: // ldk -> Latn
+        case 0xAC6B0000u: // ldl -> Latn
+        case 0xB06B0000u: // ldm -> Latn
+        case 0xB46B0000u: // ldn -> Latn
+        case 0xB86B0000u: // ldo -> Latn
+        case 0xBC6B0000u: // ldp -> Latn
+        case 0xC06B0000u: // ldq -> Latn
+        case 0x808B0000u: // lea -> Latn
+        case 0x848B0000u: // leb -> Latn
+        case 0x888B0000u: // lec -> Latn
+        case 0x8C8B0000u: // led -> Latn
+        case 0x908B0000u: // lee -> Latn
+        case 0x948B0000u: // lef -> Latn
+        case 0x9C8B0000u: // leh -> Latn
+        case 0xA08B0000u: // lei -> Latn
+        case 0xA48B0000u: // lej -> Latn
+        case 0xA88B0000u: // lek -> Latn
+        case 0xAC8B0000u: // lel -> Latn
+        case 0xB08B0000u: // lem -> Latn
+        case 0xB48B0000u: // len -> Latn
+        case 0xB88B0000u: // leo -> Latn
+        case 0xC08B0000u: // leq -> Latn
+        case 0xC48B0000u: // ler -> Latn
+        case 0xC88B0000u: // les -> Latn
+        case 0xCC8B0000u: // let -> Latn
+        case 0xD08B0000u: // leu -> Latn
+        case 0xD48B0000u: // lev -> Latn
+        case 0xD88B0000u: // lew -> Latn
+        case 0xDC8B0000u: // lex -> Latn
+        case 0xE08B0000u: // ley -> Latn
+        case 0x80AB0000u: // lfa -> Latn
+        case 0xB4AB0000u: // lfn -> Latn
+        case 0x6C670000u: // lg -> Latn
+        case 0x80CB0000u: // lga -> Latn
+        case 0x84CB0000u: // lgb -> Latn
+        case 0x98CB0000u: // lgg -> Latn
+        case 0x9CCB0000u: // lgh -> Latn
+        case 0xA0CB0000u: // lgi -> Latn
+        case 0xA8CB0000u: // lgk -> Latn
+        case 0xACCB0000u: // lgl -> Latn
+        case 0xB0CB0000u: // lgm -> Latn
+        case 0xB4CB0000u: // lgn -> Latn
+        case 0xB8CB0000u: // lgo -> Latn
+        case 0xC0CB0000u: // lgq -> Latn
+        case 0xC4CB0000u: // lgr -> Latn
+        case 0xCCCB0000u: // lgt -> Latn
+        case 0xD0CB0000u: // lgu -> Latn
+        case 0xE4CB0000u: // lgz -> Latn
+        case 0x80EB0000u: // lha -> Latn
+        case 0x9CEB0000u: // lhh -> Latn
+        case 0xA0EB0000u: // lhi -> Latn
+        case 0xB4EB0000u: // lhn -> Latn
+        case 0xCCEB0000u: // lht -> Latn
+        case 0xD0EB0000u: // lhu -> Latn
+        case 0x6C690000u: // li -> Latn
+        case 0x810B0000u: // lia -> Latn
+        case 0x850B0000u: // lib -> Latn
+        case 0x890B0000u: // lic -> Latn
+        case 0x8D0B0000u: // lid -> Latn
+        case 0x910B0000u: // lie -> Latn
+        case 0x990B0000u: // lig -> Latn
+        case 0x9D0B0000u: // lih -> Latn
+        case 0xA50B0000u: // lij -> Latn
+        case 0xA90B0000u: // lik -> Latn
+        case 0xAD0B0000u: // lil -> Latn
+        case 0xB90B0000u: // lio -> Latn
+        case 0xBD0B0000u: // lip -> Latn
+        case 0xC10B0000u: // liq -> Latn
+        case 0xC50B0000u: // lir -> Latn
+        case 0xD10B0000u: // liu -> Latn
+        case 0xD50B0000u: // liv -> Latn
+        case 0xD90B0000u: // liw -> Latn
+        case 0xDD0B0000u: // lix -> Latn
+        case 0xE10B0000u: // liy -> Latn
+        case 0xE50B0000u: // liz -> Latn
+        case 0x812B0000u: // lja -> Latn
+        case 0x912B0000u: // lje -> Latn
+        case 0xA12B0000u: // lji -> Latn
+        case 0xAD2B0000u: // ljl -> Latn
+        case 0xBD2B0000u: // ljp -> Latn
+        case 0xD92B0000u: // ljw -> Latn
+        case 0xDD2B0000u: // ljx -> Latn
+        case 0x814B0000u: // lka -> Latn
+        case 0x854B0000u: // lkb -> Latn
+        case 0x894B0000u: // lkc -> Latn
+        case 0x8D4B0000u: // lkd -> Latn
+        case 0x914B0000u: // lke -> Latn
+        case 0xA54B0000u: // lkj -> Latn
+        case 0xAD4B0000u: // lkl -> Latn
+        case 0xB14B0000u: // lkm -> Latn
+        case 0xB54B0000u: // lkn -> Latn
+        case 0xB94B0000u: // lko -> Latn
+        case 0xC54B0000u: // lkr -> Latn
+        case 0xC94B0000u: // lks -> Latn
+        case 0xCD4B0000u: // lkt -> Latn
+        case 0xD14B0000u: // lku -> Latn
+        case 0xE14B0000u: // lky -> Latn
+        case 0x816B0000u: // lla -> Latn
+        case 0x856B0000u: // llb -> Latn
+        case 0x896B0000u: // llc -> Latn
+        case 0x8D6B0000u: // lld -> Latn
+        case 0x916B0000u: // lle -> Latn
+        case 0x956B0000u: // llf -> Latn
+        case 0x996B0000u: // llg -> Latn
+        case 0xA16B0000u: // lli -> Latn
+        case 0xA56B0000u: // llj -> Latn
+        case 0xA96B0000u: // llk -> Latn
+        case 0xAD6B0000u: // lll -> Latn
+        case 0xB16B0000u: // llm -> Latn
+        case 0xB56B0000u: // lln -> Latn
+        case 0xBD6B0000u: // llp -> Latn
+        case 0xC16B0000u: // llq -> Latn
+        case 0xD16B0000u: // llu -> Latn
+        case 0xDD6B0000u: // llx -> Latn
+        case 0x818B0000u: // lma -> Latn
+        case 0x858B0000u: // lmb -> Latn
+        case 0x898B0000u: // lmc -> Latn
+        case 0x8D8B0000u: // lmd -> Latn
+        case 0x918B0000u: // lme -> Latn
+        case 0x958B0000u: // lmf -> Latn
+        case 0x998B0000u: // lmg -> Latn
+        case 0xA18B0000u: // lmi -> Latn
+        case 0xA58B0000u: // lmj -> Latn
+        case 0xA98B0000u: // lmk -> Latn
+        case 0xAD8B0000u: // lml -> Latn
+        case 0xB98B0000u: // lmo -> Latn
+        case 0xBD8B0000u: // lmp -> Latn
+        case 0xC18B0000u: // lmq -> Latn
+        case 0xC58B0000u: // lmr -> Latn
+        case 0xD18B0000u: // lmu -> Latn
+        case 0xD58B0000u: // lmv -> Latn
+        case 0xD98B0000u: // lmw -> Latn
+        case 0xDD8B0000u: // lmx -> Latn
+        case 0xE18B0000u: // lmy -> Latn
+        case 0x6C6E0000u: // ln -> Latn
+        case 0x81AB0000u: // lna -> Latn
+        case 0x85AB0000u: // lnb -> Latn
+        case 0x8DAB0000u: // lnd -> Latn
+        case 0x99AB0000u: // lng -> Latn
+        case 0x9DAB0000u: // lnh -> Latn
+        case 0xA1AB0000u: // lni -> Latn
+        case 0xA5AB0000u: // lnj -> Latn
+        case 0xADAB0000u: // lnl -> Latn
+        case 0xB1AB0000u: // lnm -> Latn
+        case 0xB5AB0000u: // lnn -> Latn
+        case 0xC9AB0000u: // lns -> Latn
+        case 0xD1AB0000u: // lnu -> Latn
+        case 0xD9AB0000u: // lnw -> Latn
+        case 0xE5AB0000u: // lnz -> Latn
+        case 0x81CB0000u: // loa -> Latn
+        case 0x85CB0000u: // lob -> Latn
+        case 0x89CB0000u: // loc -> Latn
+        case 0x91CB0000u: // loe -> Latn
+        case 0x99CB0000u: // log -> Latn
+        case 0x9DCB0000u: // loh -> Latn
+        case 0xA1CB0000u: // loi -> Latn
+        case 0xA5CB0000u: // loj -> Latn
+        case 0xA9CB0000u: // lok -> Latn
+        case 0xADCB0000u: // lol -> Latn
+        case 0xB1CB0000u: // lom -> Latn
+        case 0xB5CB0000u: // lon -> Latn
+        case 0xB9CB0000u: // loo -> Latn
+        case 0xBDCB0000u: // lop -> Latn
+        case 0xC1CB0000u: // loq -> Latn
+        case 0xC5CB0000u: // lor -> Latn
+        case 0xC9CB0000u: // los -> Latn
+        case 0xCDCB0000u: // lot -> Latn
+        case 0xD1CB0000u: // lou -> Latn
+        case 0xD9CB0000u: // low -> Latn
+        case 0xDDCB0000u: // lox -> Latn
+        case 0xE5CB0000u: // loz -> Latn
+        case 0x81EB0000u: // lpa -> Latn
+        case 0x91EB0000u: // lpe -> Latn
+        case 0xB5EB0000u: // lpn -> Latn
+        case 0xDDEB0000u: // lpx -> Latn
+        case 0xC60B0000u: // lqr -> Latn
+        case 0x822B0000u: // lra -> Latn
+        case 0x9A2B0000u: // lrg -> Latn
+        case 0xA22B0000u: // lri -> Latn
+        case 0xB22B0000u: // lrm -> Latn
+        case 0xB62B0000u: // lrn -> Latn
+        case 0xBA2B0000u: // lro -> Latn
+        case 0xCE2B0000u: // lrt -> Latn
+        case 0xD62B0000u: // lrv -> Latn
+        case 0xE62B0000u: // lrz -> Latn
+        case 0x924B0000u: // lse -> Latn
+        case 0xA24B0000u: // lsi -> Latn
+        case 0xB24B0000u: // lsm -> Latn
+        case 0xC64B0000u: // lsr -> Latn
+        case 0x6C740000u: // lt -> Latn
+        case 0x9A6B0000u: // ltg -> Latn
+        case 0x9E6B0000u: // lth -> Latn
+        case 0xA26B0000u: // lti -> Latn
+        case 0xB66B0000u: // ltn -> Latn
+        case 0xBA6B0000u: // lto -> Latn
+        case 0xCA6B0000u: // lts -> Latn
+        case 0xD26B0000u: // ltu -> Latn
+        case 0x6C750000u: // lu -> Latn
+        case 0x828B0000u: // lua -> Latn
+        case 0x8A8B0000u: // luc -> Latn
+        case 0x8E8B0000u: // lud -> Latn
+        case 0x928B0000u: // lue -> Latn
+        case 0x968B0000u: // luf -> Latn
+        case 0xA28B0000u: // lui -> Latn
+        case 0xA68B0000u: // luj -> Latn
+        case 0xAE8B0000u: // lul -> Latn
+        case 0xB28B0000u: // lum -> Latn
+        case 0xB68B0000u: // lun -> Latn
+        case 0xBA8B0000u: // luo -> Latn
+        case 0xBE8B0000u: // lup -> Latn
+        case 0xC28B0000u: // luq -> Latn
+        case 0xC68B0000u: // lur -> Latn
+        case 0xCA8B0000u: // lus -> Latn
+        case 0xCE8B0000u: // lut -> Latn
+        case 0xDA8B0000u: // luw -> Latn
+        case 0xE28B0000u: // luy -> Latn
+        case 0x6C760000u: // lv -> Latn
+        case 0x82AB0000u: // lva -> Latn
+        case 0xA2AB0000u: // lvi -> Latn
+        case 0xAAAB0000u: // lvk -> Latn
+        case 0xAEAB0000u: // lvl -> Latn
+        case 0xD2AB0000u: // lvu -> Latn
+        case 0x82CB0000u: // lwa -> Latn
+        case 0x92CB0000u: // lwe -> Latn
+        case 0x9ACB0000u: // lwg -> Latn
+        case 0x9ECB0000u: // lwh -> Latn
+        case 0xBACB0000u: // lwo -> Latn
+        case 0xCECB0000u: // lwt -> Latn
+        case 0xDACB0000u: // lww -> Latn
+        case 0xB2EB0000u: // lxm -> Latn
+        case 0xB70B0000u: // lyn -> Latn
+        case 0xAF2B0000u: // lzl -> Latn
+        case 0xB72B0000u: // lzn -> Latn
+        case 0xE72B0000u: // lzz -> Latn
+        case 0x800C0000u: // maa -> Latn
+        case 0x840C0000u: // mab -> Latn
+        case 0x8C0C0000u: // mad -> Latn
+        case 0x900C0000u: // mae -> Latn
+        case 0x940C0000u: // maf -> Latn
+        case 0xA40C0000u: // maj -> Latn
+        case 0xA80C0000u: // mak -> Latn
+        case 0xB00C0000u: // mam -> Latn
+        case 0xB40C0000u: // man -> Latn
+        case 0xC00C0000u: // maq -> Latn
+        case 0xC80C0000u: // mas -> Latn
+        case 0xCC0C0000u: // mat -> Latn
+        case 0xD00C0000u: // mau -> Latn
+        case 0xD40C0000u: // mav -> Latn
+        case 0xD80C0000u: // maw -> Latn
+        case 0xDC0C0000u: // max -> Latn
+        case 0xE40C0000u: // maz -> Latn
+        case 0x802C0000u: // mba -> Latn
+        case 0x842C0000u: // mbb -> Latn
+        case 0x882C0000u: // mbc -> Latn
+        case 0x8C2C0000u: // mbd -> Latn
+        case 0x942C0000u: // mbf -> Latn
+        case 0x9C2C0000u: // mbh -> Latn
+        case 0xA02C0000u: // mbi -> Latn
+        case 0xA42C0000u: // mbj -> Latn
+        case 0xA82C0000u: // mbk -> Latn
+        case 0xAC2C0000u: // mbl -> Latn
+        case 0xB02C0000u: // mbm -> Latn
+        case 0xB42C0000u: // mbn -> Latn
+        case 0xB82C0000u: // mbo -> Latn
+        case 0xBC2C0000u: // mbp -> Latn
+        case 0xC02C0000u: // mbq -> Latn
+        case 0xC42C0000u: // mbr -> Latn
+        case 0xC82C0000u: // mbs -> Latn
+        case 0xCC2C0000u: // mbt -> Latn
+        case 0xD02C0000u: // mbu -> Latn
+        case 0xD42C0000u: // mbv -> Latn
+        case 0xD82C0000u: // mbw -> Latn
+        case 0xDC2C0000u: // mbx -> Latn
+        case 0xE42C0000u: // mbz -> Latn
+        case 0x804C0000u: // mca -> Latn
+        case 0x844C0000u: // mcb -> Latn
+        case 0x884C0000u: // mcc -> Latn
+        case 0x8C4C0000u: // mcd -> Latn
+        case 0x904C0000u: // mce -> Latn
+        case 0x944C0000u: // mcf -> Latn
+        case 0x984C0000u: // mcg -> Latn
+        case 0x9C4C0000u: // mch -> Latn
+        case 0xA04C0000u: // mci -> Latn
+        case 0xA44C0000u: // mcj -> Latn
+        case 0xA84C0000u: // mck -> Latn
+        case 0xAC4C0000u: // mcl -> Latn
+        case 0xB04C0000u: // mcm -> Latn
+        case 0xB44C0000u: // mcn -> Latn
+        case 0xB84C0000u: // mco -> Latn
+        case 0xBC4C0000u: // mcp -> Latn
+        case 0xC04C0000u: // mcq -> Latn
+        case 0xC44C0000u: // mcr -> Latn
+        case 0xC84C0000u: // mcs -> Latn
+        case 0xCC4C0000u: // mct -> Latn
+        case 0xD04C0000u: // mcu -> Latn
+        case 0xD44C0000u: // mcv -> Latn
+        case 0xD84C0000u: // mcw -> Latn
+        case 0xDC4C0000u: // mcx -> Latn
+        case 0xE04C0000u: // mcy -> Latn
+        case 0xE44C0000u: // mcz -> Latn
+        case 0x806C0000u: // mda -> Latn
+        case 0x846C0000u: // mdb -> Latn
+        case 0x886C0000u: // mdc -> Latn
+        case 0x8C6C0000u: // mdd -> Latn
+        case 0x986C0000u: // mdg -> Latn
+        case 0x9C6C0000u: // mdh -> Latn
+        case 0xA06C0000u: // mdi -> Latn
+        case 0xA46C0000u: // mdj -> Latn
+        case 0xA86C0000u: // mdk -> Latn
+        case 0xB06C0000u: // mdm -> Latn
+        case 0xB46C0000u: // mdn -> Latn
+        case 0xBC6C0000u: // mdp -> Latn
+        case 0xC06C0000u: // mdq -> Latn
+        case 0xC46C0000u: // mdr -> Latn
+        case 0xC86C0000u: // mds -> Latn
+        case 0xCC6C0000u: // mdt -> Latn
+        case 0xD06C0000u: // mdu -> Latn
+        case 0xD46C0000u: // mdv -> Latn
+        case 0xD86C0000u: // mdw -> Latn
+        case 0xE46C0000u: // mdz -> Latn
+        case 0x808C0000u: // mea -> Latn
+        case 0x848C0000u: // meb -> Latn
+        case 0x888C0000u: // mec -> Latn
+        case 0x8C8C0000u: // med -> Latn
+        case 0x908C0000u: // mee -> Latn
+        case 0x9C8C0000u: // meh -> Latn
+        case 0xA48C0000u: // mej -> Latn
+        case 0xA88C0000u: // mek -> Latn
+        case 0xAC8C0000u: // mel -> Latn
+        case 0xB08C0000u: // mem -> Latn
+        case 0xB48C0000u: // men -> Latn
+        case 0xB88C0000u: // meo -> Latn
+        case 0xBC8C0000u: // mep -> Latn
+        case 0xC08C0000u: // meq -> Latn
+        case 0xC48C0000u: // mer -> Latn
+        case 0xC88C0000u: // mes -> Latn
+        case 0xCC8C0000u: // met -> Latn
+        case 0xD08C0000u: // meu -> Latn
+        case 0xD48C0000u: // mev -> Latn
+        case 0xD88C0000u: // mew -> Latn
+        case 0xE08C0000u: // mey -> Latn
+        case 0xE48C0000u: // mez -> Latn
+        case 0x84AC0000u: // mfb -> Latn
+        case 0x88AC0000u: // mfc -> Latn
+        case 0x8CAC0000u: // mfd -> Latn
+        case 0x90AC0000u: // mfe -> Latn
+        case 0x94AC0000u: // mff -> Latn
+        case 0x98AC0000u: // mfg -> Latn
+        case 0x9CAC0000u: // mfh -> Latn
+        case 0xA4AC0000u: // mfj -> Latn
+        case 0xA8AC0000u: // mfk -> Latn
+        case 0xACAC0000u: // mfl -> Latn
+        case 0xB0AC0000u: // mfm -> Latn
+        case 0xB4AC0000u: // mfn -> Latn
+        case 0xB8AC0000u: // mfo -> Latn
+        case 0xBCAC0000u: // mfp -> Latn
+        case 0xC0AC0000u: // mfq -> Latn
+        case 0xC4AC0000u: // mfr -> Latn
+        case 0xCCAC0000u: // mft -> Latn
+        case 0xD0AC0000u: // mfu -> Latn
+        case 0xD4AC0000u: // mfv -> Latn
+        case 0xD8AC0000u: // mfw -> Latn
+        case 0xDCAC0000u: // mfx -> Latn
+        case 0xE0AC0000u: // mfy -> Latn
+        case 0xE4AC0000u: // mfz -> Latn
+        case 0x6D670000u: // mg -> Latn
+        case 0x84CC0000u: // mgb -> Latn
+        case 0x88CC0000u: // mgc -> Latn
+        case 0x8CCC0000u: // mgd -> Latn
+        case 0x90CC0000u: // mge -> Latn
+        case 0x94CC0000u: // mgf -> Latn
+        case 0x98CC0000u: // mgg -> Latn
+        case 0x9CCC0000u: // mgh -> Latn
+        case 0xA0CC0000u: // mgi -> Latn
+        case 0xA4CC0000u: // mgj -> Latn
+        case 0xA8CC0000u: // mgk -> Latn
+        case 0xACCC0000u: // mgl -> Latn
+        case 0xB0CC0000u: // mgm -> Latn
+        case 0xB4CC0000u: // mgn -> Latn
+        case 0xB8CC0000u: // mgo -> Latn
+        case 0xC0CC0000u: // mgq -> Latn
+        case 0xC4CC0000u: // mgr -> Latn
+        case 0xC8CC0000u: // mgs -> Latn
+        case 0xCCCC0000u: // mgt -> Latn
+        case 0xD0CC0000u: // mgu -> Latn
+        case 0xD4CC0000u: // mgv -> Latn
+        case 0xD8CC0000u: // mgw -> Latn
+        case 0xE0CC0000u: // mgy -> Latn
+        case 0xE4CC0000u: // mgz -> Latn
+        case 0x6D680000u: // mh -> Latn
+        case 0x84EC0000u: // mhb -> Latn
+        case 0x88EC0000u: // mhc -> Latn
+        case 0x8CEC0000u: // mhd -> Latn
+        case 0x90EC0000u: // mhe -> Latn
+        case 0x94EC0000u: // mhf -> Latn
+        case 0x98EC0000u: // mhg -> Latn
+        case 0xA0EC0000u: // mhi -> Latn
+        case 0xA8EC0000u: // mhk -> Latn
+        case 0xACEC0000u: // mhl -> Latn
+        case 0xB0EC0000u: // mhm -> Latn
+        case 0xB4EC0000u: // mhn -> Latn
+        case 0xB8EC0000u: // mho -> Latn
+        case 0xBCEC0000u: // mhp -> Latn
+        case 0xC0EC0000u: // mhq -> Latn
+        case 0xC8EC0000u: // mhs -> Latn
+        case 0xCCEC0000u: // mht -> Latn
+        case 0xD0EC0000u: // mhu -> Latn
+        case 0xD8EC0000u: // mhw -> Latn
+        case 0xDCEC0000u: // mhx -> Latn
+        case 0xE0EC0000u: // mhy -> Latn
+        case 0xE4EC0000u: // mhz -> Latn
+        case 0x6D690000u: // mi -> Latn
+        case 0x810C0000u: // mia -> Latn
+        case 0x850C0000u: // mib -> Latn
+        case 0x890C0000u: // mic -> Latn
+        case 0x910C0000u: // mie -> Latn
+        case 0x950C0000u: // mif -> Latn
+        case 0x990C0000u: // mig -> Latn
+        case 0x9D0C0000u: // mih -> Latn
+        case 0xA10C0000u: // mii -> Latn
+        case 0xA50C0000u: // mij -> Latn
+        case 0xA90C0000u: // mik -> Latn
+        case 0xAD0C0000u: // mil -> Latn
+        case 0xB10C0000u: // mim -> Latn
+        case 0xB50C0000u: // min -> Latn
+        case 0xB90C0000u: // mio -> Latn
+        case 0xBD0C0000u: // mip -> Latn
+        case 0xC10C0000u: // miq -> Latn
+        case 0xC50C0000u: // mir -> Latn
+        case 0xCD0C0000u: // mit -> Latn
+        case 0xD10C0000u: // miu -> Latn
+        case 0xD90C0000u: // miw -> Latn
+        case 0xDD0C0000u: // mix -> Latn
+        case 0xE10C0000u: // miy -> Latn
+        case 0xE50C0000u: // miz -> Latn
+        case 0x852C0000u: // mjb -> Latn
+        case 0x892C0000u: // mjc -> Latn
+        case 0x8D2C0000u: // mjd -> Latn
+        case 0x912C0000u: // mje -> Latn
+        case 0x992C0000u: // mjg -> Latn
+        case 0x9D2C0000u: // mjh -> Latn
+        case 0xA12C0000u: // mji -> Latn
+        case 0xA52C0000u: // mjj -> Latn
+        case 0xA92C0000u: // mjk -> Latn
+        case 0xB12C0000u: // mjm -> Latn
+        case 0xB52C0000u: // mjn -> Latn
+        case 0xC92C0000u: // mjs -> Latn
+        case 0xD92C0000u: // mjw -> Latn
+        case 0xDD2C0000u: // mjx -> Latn
+        case 0xE12C0000u: // mjy -> Latn
+        case 0x814C0000u: // mka -> Latn
+        case 0x894C0000u: // mkc -> Latn
+        case 0x954C0000u: // mkf -> Latn
+        case 0xA54C0000u: // mkj -> Latn
+        case 0xA94C0000u: // mkk -> Latn
+        case 0xAD4C0000u: // mkl -> Latn
+        case 0xB54C0000u: // mkn -> Latn
+        case 0xB94C0000u: // mko -> Latn
+        case 0xBD4C0000u: // mkp -> Latn
+        case 0xC54C0000u: // mkr -> Latn
+        case 0xC94C0000u: // mks -> Latn
+        case 0xCD4C0000u: // mkt -> Latn
+        case 0xD14C0000u: // mku -> Latn
+        case 0xD54C0000u: // mkv -> Latn
+        case 0xD94C0000u: // mkw -> Latn
+        case 0xDD4C0000u: // mkx -> Latn
+        case 0xE14C0000u: // mky -> Latn
+        case 0xE54C0000u: // mkz -> Latn
+        case 0x816C0000u: // mla -> Latn
+        case 0x856C0000u: // mlb -> Latn
+        case 0x896C0000u: // mlc -> Latn
+        case 0x916C0000u: // mle -> Latn
+        case 0x9D6C0000u: // mlh -> Latn
+        case 0xA16C0000u: // mli -> Latn
+        case 0xA56C0000u: // mlj -> Latn
+        case 0xA96C0000u: // mlk -> Latn
+        case 0xAD6C0000u: // mll -> Latn
+        case 0xB56C0000u: // mln -> Latn
+        case 0xB96C0000u: // mlo -> Latn
+        case 0xBD6C0000u: // mlp -> Latn
+        case 0xC16C0000u: // mlq -> Latn
+        case 0xC56C0000u: // mlr -> Latn
+        case 0xC96C0000u: // mls -> Latn
+        case 0xD16C0000u: // mlu -> Latn
+        case 0xD56C0000u: // mlv -> Latn
+        case 0xD96C0000u: // mlw -> Latn
+        case 0xDD6C0000u: // mlx -> Latn
+        case 0xE56C0000u: // mlz -> Latn
+        case 0x818C0000u: // mma -> Latn
+        case 0x858C0000u: // mmb -> Latn
+        case 0x898C0000u: // mmc -> Latn
+        case 0x8D8C0000u: // mmd -> Latn
+        case 0x918C0000u: // mme -> Latn
+        case 0x958C0000u: // mmf -> Latn
+        case 0x998C0000u: // mmg -> Latn
+        case 0x9D8C0000u: // mmh -> Latn
+        case 0xA18C0000u: // mmi -> Latn
+        case 0xB18C0000u: // mmm -> Latn
+        case 0xB58C0000u: // mmn -> Latn
+        case 0xB98C0000u: // mmo -> Latn
+        case 0xBD8C0000u: // mmp -> Latn
+        case 0xC18C0000u: // mmq -> Latn
+        case 0xC58C0000u: // mmr -> Latn
+        case 0xCD8C0000u: // mmt -> Latn
+        case 0xD18C0000u: // mmu -> Latn
+        case 0xD58C0000u: // mmv -> Latn
+        case 0xD98C0000u: // mmw -> Latn
+        case 0xDD8C0000u: // mmx -> Latn
+        case 0xE18C0000u: // mmy -> Latn
+        case 0xE58C0000u: // mmz -> Latn
+        case 0x81AC0000u: // mna -> Latn
+        case 0x85AC0000u: // mnb -> Latn
+        case 0x8DAC0000u: // mnd -> Latn
+        case 0x91AC0000u: // mne -> Latn
+        case 0x95AC0000u: // mnf -> Latn
+        case 0x99AC0000u: // mng -> Latn
+        case 0x9DAC0000u: // mnh -> Latn
+        case 0xADAC0000u: // mnl -> Latn
+        case 0xB1AC0000u: // mnm -> Latn
+        case 0xB5AC0000u: // mnn -> Latn
+        case 0xBDAC0000u: // mnp -> Latn
+        case 0xC1AC0000u: // mnq -> Latn
+        case 0xC5AC0000u: // mnr -> Latn
+        case 0xD1AC0000u: // mnu -> Latn
+        case 0xD5AC0000u: // mnv -> Latn
+        case 0xDDAC0000u: // mnx -> Latn
+        case 0xE1AC0000u: // mny -> Latn
+        case 0xE5AC0000u: // mnz -> Latn
+        case 0x6D6F0000u: // mo -> Latn
+        case 0x81CC0000u: // moa -> Latn
+        case 0x89CC0000u: // moc -> Latn
+        case 0x8DCC0000u: // mod -> Latn
+        case 0x91CC0000u: // moe -> Latn
+        case 0x99CC0000u: // mog -> Latn
+        case 0x9DCC0000u: // moh -> Latn
+        case 0xA1CC0000u: // moi -> Latn
+        case 0xA5CC0000u: // moj -> Latn
+        case 0xA9CC0000u: // mok -> Latn
+        case 0xB1CC0000u: // mom -> Latn
+        case 0xB9CC0000u: // moo -> Latn
+        case 0xBDCC0000u: // mop -> Latn
+        case 0xC1CC0000u: // moq -> Latn
+        case 0xC5CC0000u: // mor -> Latn
+        case 0xC9CC0000u: // mos -> Latn
+        case 0xCDCC0000u: // mot -> Latn
+        case 0xD1CC0000u: // mou -> Latn
+        case 0xD5CC0000u: // mov -> Latn
+        case 0xD9CC0000u: // mow -> Latn
+        case 0xDDCC0000u: // mox -> Latn
+        case 0xE1CC0000u: // moy -> Latn
+        case 0xE5CC0000u: // moz -> Latn
+        case 0x81EC0000u: // mpa -> Latn
+        case 0x85EC0000u: // mpb -> Latn
+        case 0x89EC0000u: // mpc -> Latn
+        case 0x8DEC0000u: // mpd -> Latn
+        case 0x91EC0000u: // mpe -> Latn
+        case 0x99EC0000u: // mpg -> Latn
+        case 0x9DEC0000u: // mph -> Latn
+        case 0xA1EC0000u: // mpi -> Latn
+        case 0xA5EC0000u: // mpj -> Latn
+        case 0xA9EC0000u: // mpk -> Latn
+        case 0xADEC0000u: // mpl -> Latn
+        case 0xB1EC0000u: // mpm -> Latn
+        case 0xB5EC0000u: // mpn -> Latn
+        case 0xB9EC0000u: // mpo -> Latn
+        case 0xBDEC0000u: // mpp -> Latn
+        case 0xC1EC0000u: // mpq -> Latn
+        case 0xC5EC0000u: // mpr -> Latn
+        case 0xC9EC0000u: // mps -> Latn
+        case 0xCDEC0000u: // mpt -> Latn
+        case 0xD1EC0000u: // mpu -> Latn
+        case 0xD5EC0000u: // mpv -> Latn
+        case 0xD9EC0000u: // mpw -> Latn
+        case 0xDDEC0000u: // mpx -> Latn
+        case 0xE1EC0000u: // mpy -> Latn
+        case 0x820C0000u: // mqa -> Latn
+        case 0x860C0000u: // mqb -> Latn
+        case 0x8A0C0000u: // mqc -> Latn
+        case 0x920C0000u: // mqe -> Latn
+        case 0x960C0000u: // mqf -> Latn
+        case 0x9A0C0000u: // mqg -> Latn
+        case 0x9E0C0000u: // mqh -> Latn
+        case 0xA20C0000u: // mqi -> Latn
+        case 0xA60C0000u: // mqj -> Latn
+        case 0xAA0C0000u: // mqk -> Latn
+        case 0xAE0C0000u: // mql -> Latn
+        case 0xB20C0000u: // mqm -> Latn
+        case 0xB60C0000u: // mqn -> Latn
+        case 0xBA0C0000u: // mqo -> Latn
+        case 0xBE0C0000u: // mqp -> Latn
+        case 0xC20C0000u: // mqq -> Latn
+        case 0xC60C0000u: // mqr -> Latn
+        case 0xCA0C0000u: // mqs -> Latn
+        case 0xD20C0000u: // mqu -> Latn
+        case 0xD60C0000u: // mqv -> Latn
+        case 0xDA0C0000u: // mqw -> Latn
+        case 0xDE0C0000u: // mqx -> Latn
+        case 0xE20C0000u: // mqy -> Latn
+        case 0xE60C0000u: // mqz -> Latn
+        case 0x862C0000u: // mrb -> Latn
+        case 0x8A2C0000u: // mrc -> Latn
+        case 0x962C0000u: // mrf -> Latn
+        case 0x9A2C0000u: // mrg -> Latn
+        case 0x9E2C0000u: // mrh -> Latn
+        case 0xAA2C0000u: // mrk -> Latn
+        case 0xAE2C0000u: // mrl -> Latn
+        case 0xB22C0000u: // mrm -> Latn
+        case 0xB62C0000u: // mrn -> Latn
+        case 0xBE2C0000u: // mrp -> Latn
+        case 0xC22C0000u: // mrq -> Latn
+        case 0xCA2C0000u: // mrs -> Latn
+        case 0xCE2C0000u: // mrt -> Latn
+        case 0xD22C0000u: // mru -> Latn
+        case 0xD62C0000u: // mrv -> Latn
+        case 0xDA2C0000u: // mrw -> Latn
+        case 0xDE2C0000u: // mrx -> Latn
+        case 0xE22C0000u: // mry -> Latn
+        case 0xE62C0000u: // mrz -> Latn
+        case 0x6D730000u: // ms -> Latn
+        case 0x864C0000u: // msb -> Latn
+        case 0x8A4C0000u: // msc -> Latn
+        case 0x924C0000u: // mse -> Latn
+        case 0x964C0000u: // msf -> Latn
+        case 0x9A4C0000u: // msg -> Latn
+        case 0x9E4C0000u: // msh -> Latn
+        case 0xA24C0000u: // msi -> Latn
+        case 0xA64C0000u: // msj -> Latn
+        case 0xAA4C0000u: // msk -> Latn
+        case 0xAE4C0000u: // msl -> Latn
+        case 0xB24C0000u: // msm -> Latn
+        case 0xB64C0000u: // msn -> Latn
+        case 0xBA4C0000u: // mso -> Latn
+        case 0xBE4C0000u: // msp -> Latn
+        case 0xC24C0000u: // msq -> Latn
+        case 0xCA4C0000u: // mss -> Latn
+        case 0xD24C0000u: // msu -> Latn
+        case 0xD64C0000u: // msv -> Latn
+        case 0xDA4C0000u: // msw -> Latn
+        case 0xDE4C0000u: // msx -> Latn
+        case 0xE24C0000u: // msy -> Latn
+        case 0xE64C0000u: // msz -> Latn
+        case 0x6D740000u: // mt -> Latn
+        case 0x826C0000u: // mta -> Latn
+        case 0x866C0000u: // mtb -> Latn
+        case 0x8A6C0000u: // mtc -> Latn
+        case 0x8E6C0000u: // mtd -> Latn
+        case 0x926C0000u: // mte -> Latn
+        case 0x966C0000u: // mtf -> Latn
+        case 0x9A6C0000u: // mtg -> Latn
+        case 0x9E6C0000u: // mth -> Latn
+        case 0xA26C0000u: // mti -> Latn
+        case 0xA66C0000u: // mtj -> Latn
+        case 0xAA6C0000u: // mtk -> Latn
+        case 0xAE6C0000u: // mtl -> Latn
+        case 0xB66C0000u: // mtn -> Latn
+        case 0xBA6C0000u: // mto -> Latn
+        case 0xBE6C0000u: // mtp -> Latn
+        case 0xC26C0000u: // mtq -> Latn
+        case 0xCA6C0000u: // mts -> Latn
+        case 0xCE6C0000u: // mtt -> Latn
+        case 0xD26C0000u: // mtu -> Latn
+        case 0xD66C0000u: // mtv -> Latn
+        case 0xDA6C0000u: // mtw -> Latn
+        case 0xDE6C0000u: // mtx -> Latn
+        case 0xE26C0000u: // mty -> Latn
+        case 0x828C0000u: // mua -> Latn
+        case 0x868C0000u: // mub -> Latn
+        case 0x8A8C0000u: // muc -> Latn
+        case 0x928C0000u: // mue -> Latn
+        case 0x9A8C0000u: // mug -> Latn
+        case 0x9E8C0000u: // muh -> Latn
+        case 0xA28C0000u: // mui -> Latn
+        case 0xA68C0000u: // muj -> Latn
+        case 0xB28C0000u: // mum -> Latn
+        case 0xBA8C0000u: // muo -> Latn
+        case 0xC28C0000u: // muq -> Latn
+        case 0xC68C0000u: // mur -> Latn
+        case 0xCA8C0000u: // mus -> Latn
+        case 0xD28C0000u: // muu -> Latn
+        case 0xDE8C0000u: // mux -> Latn
+        case 0xE28C0000u: // muy -> Latn
+        case 0x82AC0000u: // mva -> Latn
+        case 0x8EAC0000u: // mvd -> Latn
+        case 0x9AAC0000u: // mvg -> Latn
+        case 0x9EAC0000u: // mvh -> Latn
+        case 0xAAAC0000u: // mvk -> Latn
+        case 0xAEAC0000u: // mvl -> Latn
+        case 0xB6AC0000u: // mvn -> Latn
+        case 0xBAAC0000u: // mvo -> Latn
+        case 0xBEAC0000u: // mvp -> Latn
+        case 0xC2AC0000u: // mvq -> Latn
+        case 0xC6AC0000u: // mvr -> Latn
+        case 0xCAAC0000u: // mvs -> Latn
+        case 0xCEAC0000u: // mvt -> Latn
+        case 0xD2AC0000u: // mvu -> Latn
+        case 0xD6AC0000u: // mvv -> Latn
+        case 0xDAAC0000u: // mvw -> Latn
+        case 0xDEAC0000u: // mvx -> Latn
+        case 0x82CC0000u: // mwa -> Latn
+        case 0x86CC0000u: // mwb -> Latn
+        case 0x8ACC0000u: // mwc -> Latn
+        case 0x92CC0000u: // mwe -> Latn
+        case 0x96CC0000u: // mwf -> Latn
+        case 0x9ACC0000u: // mwg -> Latn
+        case 0x9ECC0000u: // mwh -> Latn
+        case 0xA2CC0000u: // mwi -> Latn
+        case 0xAACC0000u: // mwk -> Latn
+        case 0xAECC0000u: // mwl -> Latn
+        case 0xB2CC0000u: // mwm -> Latn
+        case 0xB6CC0000u: // mwn -> Latn
+        case 0xBACC0000u: // mwo -> Latn
+        case 0xBECC0000u: // mwp -> Latn
+        case 0xC2CC0000u: // mwq -> Latn
+        case 0xCACC0000u: // mws -> Latn
+        case 0xD2CC0000u: // mwu -> Latn
+        case 0xD6CC0000u: // mwv -> Latn
+        case 0xE6CC0000u: // mwz -> Latn
+        case 0x82EC0000u: // mxa -> Latn
+        case 0x86EC0000u: // mxb -> Latn
+        case 0x8AEC0000u: // mxc -> Latn
+        case 0x8EEC0000u: // mxd -> Latn
+        case 0x92EC0000u: // mxe -> Latn
+        case 0x96EC0000u: // mxf -> Latn
+        case 0x9AEC0000u: // mxg -> Latn
+        case 0x9EEC0000u: // mxh -> Latn
+        case 0xA2EC0000u: // mxi -> Latn
+        case 0xA6EC0000u: // mxj -> Latn
+        case 0xAAEC0000u: // mxk -> Latn
+        case 0xAEEC0000u: // mxl -> Latn
+        case 0xB2EC0000u: // mxm -> Latn
+        case 0xB6EC0000u: // mxn -> Latn
+        case 0xBAEC0000u: // mxo -> Latn
+        case 0xBEEC0000u: // mxp -> Latn
+        case 0xC2EC0000u: // mxq -> Latn
+        case 0xC6EC0000u: // mxr -> Latn
+        case 0xCAEC0000u: // mxs -> Latn
+        case 0xCEEC0000u: // mxt -> Latn
+        case 0xD2EC0000u: // mxu -> Latn
+        case 0xD6EC0000u: // mxv -> Latn
+        case 0xDAEC0000u: // mxw -> Latn
+        case 0xDEEC0000u: // mxx -> Latn
+        case 0xE2EC0000u: // mxy -> Latn
+        case 0xE6EC0000u: // mxz -> Latn
+        case 0x870C0000u: // myb -> Latn
+        case 0x8B0C0000u: // myc -> Latn
+        case 0x930C0000u: // mye -> Latn
+        case 0x970C0000u: // myf -> Latn
+        case 0x9B0C0000u: // myg -> Latn
+        case 0x9F0C0000u: // myh -> Latn
+        case 0xA70C0000u: // myj -> Latn
+        case 0xAB0C0000u: // myk -> Latn
+        case 0xAF0C0000u: // myl -> Latn
+        case 0xBF0C0000u: // myp -> Latn
+        case 0xC70C0000u: // myr -> Latn
+        case 0xD30C0000u: // myu -> Latn
+        case 0xDB0C0000u: // myw -> Latn
+        case 0xDF0C0000u: // myx -> Latn
+        case 0xE30C0000u: // myy -> Latn
+        case 0x832C0000u: // mza -> Latn
+        case 0x8F2C0000u: // mzd -> Latn
+        case 0x932C0000u: // mze -> Latn
+        case 0x9F2C0000u: // mzh -> Latn
+        case 0xA32C0000u: // mzi -> Latn
+        case 0xA72C0000u: // mzj -> Latn
+        case 0xAB2C0000u: // mzk -> Latn
+        case 0xAF2C0000u: // mzl -> Latn
+        case 0xB32C0000u: // mzm -> Latn
+        case 0xBB2C0000u: // mzo -> Latn
+        case 0xBF2C0000u: // mzp -> Latn
+        case 0xC32C0000u: // mzq -> Latn
+        case 0xC72C0000u: // mzr -> Latn
+        case 0xCF2C0000u: // mzt -> Latn
+        case 0xD32C0000u: // mzu -> Latn
+        case 0xD72C0000u: // mzv -> Latn
+        case 0xDB2C0000u: // mzw -> Latn
+        case 0xDF2C0000u: // mzx -> Latn
+        case 0xE72C0000u: // mzz -> Latn
+        case 0x6E610000u: // na -> Latn
+        case 0x800D0000u: // naa -> Latn
+        case 0x840D0000u: // nab -> Latn
+        case 0x880D0000u: // nac -> Latn
+        case 0x900D0000u: // nae -> Latn
+        case 0x940D0000u: // naf -> Latn
+        case 0x980D0000u: // nag -> Latn
+        case 0xA40D0000u: // naj -> Latn
+        case 0xA80D0000u: // nak -> Latn
+        case 0xAC0D0000u: // nal -> Latn
+        case 0xB00D0000u: // nam -> Latn
+        case 0xBC0D0000u: // nap -> Latn
+        case 0xC00D0000u: // naq -> Latn
+        case 0xC40D0000u: // nar -> Latn
+        case 0xC80D0000u: // nas -> Latn
+        case 0xCC0D0000u: // nat -> Latn
+        case 0xD80D0000u: // naw -> Latn
+        case 0xDC0D0000u: // nax -> Latn
+        case 0xE00D0000u: // nay -> Latn
+        case 0xE40D0000u: // naz -> Latn
+        case 0x6E620000u: // nb -> Latn
+        case 0x802D0000u: // nba -> Latn
+        case 0x842D0000u: // nbb -> Latn
+        case 0x882D0000u: // nbc -> Latn
+        case 0x8C2D0000u: // nbd -> Latn
+        case 0x902D0000u: // nbe -> Latn
+        case 0x9C2D0000u: // nbh -> Latn
+        case 0xA02D0000u: // nbi -> Latn
+        case 0xA42D0000u: // nbj -> Latn
+        case 0xA82D0000u: // nbk -> Latn
+        case 0xB02D0000u: // nbm -> Latn
+        case 0xB42D0000u: // nbn -> Latn
+        case 0xB82D0000u: // nbo -> Latn
+        case 0xBC2D0000u: // nbp -> Latn
+        case 0xC02D0000u: // nbq -> Latn
+        case 0xC42D0000u: // nbr -> Latn
+        case 0xCC2D0000u: // nbt -> Latn
+        case 0xD02D0000u: // nbu -> Latn
+        case 0xD42D0000u: // nbv -> Latn
+        case 0xD82D0000u: // nbw -> Latn
+        case 0xE02D0000u: // nby -> Latn
+        case 0x804D0000u: // nca -> Latn
+        case 0x844D0000u: // ncb -> Latn
+        case 0x884D0000u: // ncc -> Latn
+        case 0x904D0000u: // nce -> Latn
+        case 0x944D0000u: // ncf -> Latn
+        case 0x984D0000u: // ncg -> Latn
+        case 0x9C4D0000u: // nch -> Latn
+        case 0xA04D0000u: // nci -> Latn
+        case 0xA44D0000u: // ncj -> Latn
+        case 0xA84D0000u: // nck -> Latn
+        case 0xAC4D0000u: // ncl -> Latn
+        case 0xB04D0000u: // ncm -> Latn
+        case 0xB44D0000u: // ncn -> Latn
+        case 0xB84D0000u: // nco -> Latn
+        case 0xC44D0000u: // ncr -> Latn
+        case 0xCC4D0000u: // nct -> Latn
+        case 0xD04D0000u: // ncu -> Latn
+        case 0xDC4D0000u: // ncx -> Latn
+        case 0xE44D0000u: // ncz -> Latn
+        case 0x6E640000u: // nd -> Latn
+        case 0x806D0000u: // nda -> Latn
+        case 0x846D0000u: // ndb -> Latn
+        case 0x886D0000u: // ndc -> Latn
+        case 0x8C6D0000u: // ndd -> Latn
+        case 0x986D0000u: // ndg -> Latn
+        case 0x9C6D0000u: // ndh -> Latn
+        case 0xA06D0000u: // ndi -> Latn
+        case 0xA46D0000u: // ndj -> Latn
+        case 0xA86D0000u: // ndk -> Latn
+        case 0xAC6D0000u: // ndl -> Latn
+        case 0xB06D0000u: // ndm -> Latn
+        case 0xB46D0000u: // ndn -> Latn
+        case 0xBC6D0000u: // ndp -> Latn
+        case 0xC06D0000u: // ndq -> Latn
+        case 0xC46D0000u: // ndr -> Latn
+        case 0xC86D0000u: // nds -> Latn
+        case 0xCC6D0000u: // ndt -> Latn
+        case 0xD06D0000u: // ndu -> Latn
+        case 0xD46D0000u: // ndv -> Latn
+        case 0xD86D0000u: // ndw -> Latn
+        case 0xDC6D0000u: // ndx -> Latn
+        case 0xE06D0000u: // ndy -> Latn
+        case 0xE46D0000u: // ndz -> Latn
+        case 0x808D0000u: // nea -> Latn
+        case 0x848D0000u: // neb -> Latn
+        case 0x888D0000u: // nec -> Latn
+        case 0x8C8D0000u: // ned -> Latn
+        case 0x908D0000u: // nee -> Latn
+        case 0xA48D0000u: // nej -> Latn
+        case 0xA88D0000u: // nek -> Latn
+        case 0xB08D0000u: // nem -> Latn
+        case 0xB48D0000u: // nen -> Latn
+        case 0xB88D0000u: // neo -> Latn
+        case 0xC08D0000u: // neq -> Latn
+        case 0xC48D0000u: // ner -> Latn
+        case 0xCC8D0000u: // net -> Latn
+        case 0xD08D0000u: // neu -> Latn
+        case 0xDC8D0000u: // nex -> Latn
+        case 0xE08D0000u: // ney -> Latn
+        case 0xE48D0000u: // nez -> Latn
+        case 0x80AD0000u: // nfa -> Latn
+        case 0x8CAD0000u: // nfd -> Latn
+        case 0xACAD0000u: // nfl -> Latn
+        case 0xC4AD0000u: // nfr -> Latn
+        case 0xD0AD0000u: // nfu -> Latn
+        case 0x6E670000u: // ng -> Latn
+        case 0x80CD0000u: // nga -> Latn
+        case 0x84CD0000u: // ngb -> Latn
+        case 0x88CD0000u: // ngc -> Latn
+        case 0x8CCD0000u: // ngd -> Latn
+        case 0x90CD0000u: // nge -> Latn
+        case 0x98CD0000u: // ngg -> Latn
+        case 0x9CCD0000u: // ngh -> Latn
+        case 0xA0CD0000u: // ngi -> Latn
+        case 0xA4CD0000u: // ngj -> Latn
+        case 0xA8CD0000u: // ngk -> Latn
+        case 0xACCD0000u: // ngl -> Latn
+        case 0xB0CD0000u: // ngm -> Latn
+        case 0xB4CD0000u: // ngn -> Latn
+        case 0xBCCD0000u: // ngp -> Latn
+        case 0xC0CD0000u: // ngq -> Latn
+        case 0xC4CD0000u: // ngr -> Latn
+        case 0xC8CD0000u: // ngs -> Latn
+        case 0xD0CD0000u: // ngu -> Latn
+        case 0xD4CD0000u: // ngv -> Latn
+        case 0xD8CD0000u: // ngw -> Latn
+        case 0xDCCD0000u: // ngx -> Latn
+        case 0xE0CD0000u: // ngy -> Latn
+        case 0xE4CD0000u: // ngz -> Latn
+        case 0x80ED0000u: // nha -> Latn
+        case 0x84ED0000u: // nhb -> Latn
+        case 0x88ED0000u: // nhc -> Latn
+        case 0x8CED0000u: // nhd -> Latn
+        case 0x90ED0000u: // nhe -> Latn
+        case 0x94ED0000u: // nhf -> Latn
+        case 0x98ED0000u: // nhg -> Latn
+        case 0xA0ED0000u: // nhi -> Latn
+        case 0xA8ED0000u: // nhk -> Latn
+        case 0xB0ED0000u: // nhm -> Latn
+        case 0xB4ED0000u: // nhn -> Latn
+        case 0xB8ED0000u: // nho -> Latn
+        case 0xBCED0000u: // nhp -> Latn
+        case 0xC0ED0000u: // nhq -> Latn
+        case 0xC4ED0000u: // nhr -> Latn
+        case 0xCCED0000u: // nht -> Latn
+        case 0xD0ED0000u: // nhu -> Latn
+        case 0xD4ED0000u: // nhv -> Latn
+        case 0xD8ED0000u: // nhw -> Latn
+        case 0xDCED0000u: // nhx -> Latn
+        case 0xE0ED0000u: // nhy -> Latn
+        case 0xE4ED0000u: // nhz -> Latn
+        case 0x810D0000u: // nia -> Latn
+        case 0x850D0000u: // nib -> Latn
+        case 0x8D0D0000u: // nid -> Latn
+        case 0x910D0000u: // nie -> Latn
+        case 0x950D0000u: // nif -> Latn
+        case 0x990D0000u: // nig -> Latn
+        case 0x9D0D0000u: // nih -> Latn
+        case 0xA10D0000u: // nii -> Latn
+        case 0xA50D0000u: // nij -> Latn
+        case 0xAD0D0000u: // nil -> Latn
+        case 0xB10D0000u: // nim -> Latn
+        case 0xB50D0000u: // nin -> Latn
+        case 0xC10D0000u: // niq -> Latn
+        case 0xC50D0000u: // nir -> Latn
+        case 0xC90D0000u: // nis -> Latn
+        case 0xD10D0000u: // niu -> Latn
+        case 0xD90D0000u: // niw -> Latn
+        case 0xDD0D0000u: // nix -> Latn
+        case 0xE10D0000u: // niy -> Latn
+        case 0xE50D0000u: // niz -> Latn
+        case 0x812D0000u: // nja -> Latn
+        case 0x852D0000u: // njb -> Latn
+        case 0x8D2D0000u: // njd -> Latn
+        case 0x9D2D0000u: // njh -> Latn
+        case 0xA12D0000u: // nji -> Latn
+        case 0xA52D0000u: // njj -> Latn
+        case 0xAD2D0000u: // njl -> Latn
+        case 0xB12D0000u: // njm -> Latn
+        case 0xB52D0000u: // njn -> Latn
+        case 0xB92D0000u: // njo -> Latn
+        case 0xC52D0000u: // njr -> Latn
+        case 0xC92D0000u: // njs -> Latn
+        case 0xCD2D0000u: // njt -> Latn
+        case 0xD12D0000u: // nju -> Latn
+        case 0xDD2D0000u: // njx -> Latn
+        case 0xE12D0000u: // njy -> Latn
+        case 0xE52D0000u: // njz -> Latn
+        case 0x814D0000u: // nka -> Latn
+        case 0x854D0000u: // nkb -> Latn
+        case 0x894D0000u: // nkc -> Latn
+        case 0x8D4D0000u: // nkd -> Latn
+        case 0x914D0000u: // nke -> Latn
+        case 0x954D0000u: // nkf -> Latn
+        case 0x994D0000u: // nkg -> Latn
+        case 0x9D4D0000u: // nkh -> Latn
+        case 0xA14D0000u: // nki -> Latn
+        case 0xA54D0000u: // nkj -> Latn
+        case 0xA94D0000u: // nkk -> Latn
+        case 0xB14D0000u: // nkm -> Latn
+        case 0xB54D0000u: // nkn -> Latn
+        case 0xB94D0000u: // nko -> Latn
+        case 0xC14D0000u: // nkq -> Latn
+        case 0xC54D0000u: // nkr -> Latn
+        case 0xC94D0000u: // nks -> Latn
+        case 0xCD4D0000u: // nkt -> Latn
+        case 0xD14D0000u: // nku -> Latn
+        case 0xD54D0000u: // nkv -> Latn
+        case 0xD94D0000u: // nkw -> Latn
+        case 0xDD4D0000u: // nkx -> Latn
+        case 0xE54D0000u: // nkz -> Latn
+        case 0x6E6C0000u: // nl -> Latn
+        case 0x816D0000u: // nla -> Latn
+        case 0x896D0000u: // nlc -> Latn
+        case 0x916D0000u: // nle -> Latn
+        case 0x996D0000u: // nlg -> Latn
+        case 0xA56D0000u: // nlj -> Latn
+        case 0xA96D0000u: // nlk -> Latn
+        case 0xB96D0000u: // nlo -> Latn
+        case 0xC16D0000u: // nlq -> Latn
+        case 0xD16D0000u: // nlu -> Latn
+        case 0xD56D0000u: // nlv -> Latn
+        case 0xD96D0000u: // nlw -> Latn
+        case 0xE16D0000u: // nly -> Latn
+        case 0xE56D0000u: // nlz -> Latn
+        case 0x818D0000u: // nma -> Latn
+        case 0x858D0000u: // nmb -> Latn
+        case 0x898D0000u: // nmc -> Latn
+        case 0x8D8D0000u: // nmd -> Latn
+        case 0x918D0000u: // nme -> Latn
+        case 0x958D0000u: // nmf -> Latn
+        case 0x998D0000u: // nmg -> Latn
+        case 0x9D8D0000u: // nmh -> Latn
+        case 0xA18D0000u: // nmi -> Latn
+        case 0xA58D0000u: // nmj -> Latn
+        case 0xA98D0000u: // nmk -> Latn
+        case 0xAD8D0000u: // nml -> Latn
+        case 0xB58D0000u: // nmn -> Latn
+        case 0xB98D0000u: // nmo -> Latn
+        case 0xBD8D0000u: // nmp -> Latn
+        case 0xC18D0000u: // nmq -> Latn
+        case 0xC58D0000u: // nmr -> Latn
+        case 0xC98D0000u: // nms -> Latn
+        case 0xCD8D0000u: // nmt -> Latn
+        case 0xD18D0000u: // nmu -> Latn
+        case 0xD58D0000u: // nmv -> Latn
+        case 0xD98D0000u: // nmw -> Latn
+        case 0xDD8D0000u: // nmx -> Latn
+        case 0xE58D0000u: // nmz -> Latn
+        case 0x6E6E0000u: // nn -> Latn
+        case 0x81AD0000u: // nna -> Latn
+        case 0x85AD0000u: // nnb -> Latn
+        case 0x89AD0000u: // nnc -> Latn
+        case 0x8DAD0000u: // nnd -> Latn
+        case 0x91AD0000u: // nne -> Latn
+        case 0x95AD0000u: // nnf -> Latn
+        case 0x99AD0000u: // nng -> Latn
+        case 0x9DAD0000u: // nnh -> Latn
+        case 0xA1AD0000u: // nni -> Latn
+        case 0xA5AD0000u: // nnj -> Latn
+        case 0xA9AD0000u: // nnk -> Latn
+        case 0xADAD0000u: // nnl -> Latn
+        case 0xB1AD0000u: // nnm -> Latn
+        case 0xB5AD0000u: // nnn -> Latn
+        case 0xC1AD0000u: // nnq -> Latn
+        case 0xC5AD0000u: // nnr -> Latn
+        case 0xCDAD0000u: // nnt -> Latn
+        case 0xD1AD0000u: // nnu -> Latn
+        case 0xD5AD0000u: // nnv -> Latn
+        case 0xD9AD0000u: // nnw -> Latn
+        case 0xE1AD0000u: // nny -> Latn
+        case 0xE5AD0000u: // nnz -> Latn
+        case 0x6E6F0000u: // no -> Latn
+        case 0x81CD0000u: // noa -> Latn
+        case 0x89CD0000u: // noc -> Latn
+        case 0x95CD0000u: // nof -> Latn
+        case 0x9DCD0000u: // noh -> Latn
+        case 0xA5CD0000u: // noj -> Latn
+        case 0xA9CD0000u: // nok -> Latn
+        case 0xBDCD0000u: // nop -> Latn
+        case 0xC1CD0000u: // noq -> Latn
+        case 0xCDCD0000u: // not -> Latn
+        case 0xD1CD0000u: // nou -> Latn
+        case 0xD5CD0000u: // nov -> Latn
+        case 0xD9CD0000u: // now -> Latn
+        case 0xE1CD0000u: // noy -> Latn
+        case 0x99ED0000u: // npg -> Latn
+        case 0x9DED0000u: // nph -> Latn
+        case 0xADED0000u: // npl -> Latn
+        case 0xB5ED0000u: // npn -> Latn
+        case 0xB9ED0000u: // npo -> Latn
+        case 0xC9ED0000u: // nps -> Latn
+        case 0xD1ED0000u: // npu -> Latn
+        case 0xDDED0000u: // npx -> Latn
+        case 0xE1ED0000u: // npy -> Latn
+        case 0x9A0D0000u: // nqg -> Latn
+        case 0xAA0D0000u: // nqk -> Latn
+        case 0xAE0D0000u: // nql -> Latn
+        case 0xB20D0000u: // nqm -> Latn
+        case 0xB60D0000u: // nqn -> Latn
+        case 0xC20D0000u: // nqq -> Latn
+        case 0xCE0D0000u: // nqt -> Latn
+        case 0xE20D0000u: // nqy -> Latn
+        case 0x6E720000u: // nr -> Latn
+        case 0x822D0000u: // nra -> Latn
+        case 0x862D0000u: // nrb -> Latn
+        case 0x922D0000u: // nre -> Latn
+        case 0x962D0000u: // nrf -> Latn
+        case 0x9A2D0000u: // nrg -> Latn
+        case 0xA22D0000u: // nri -> Latn
+        case 0xAA2D0000u: // nrk -> Latn
+        case 0xAE2D0000u: // nrl -> Latn
+        case 0xB22D0000u: // nrm -> Latn
+        case 0xBE2D0000u: // nrp -> Latn
+        case 0xD22D0000u: // nru -> Latn
+        case 0xDE2D0000u: // nrx -> Latn
+        case 0xE62D0000u: // nrz -> Latn
+        case 0x824D0000u: // nsa -> Latn
+        case 0x864D0000u: // nsb -> Latn
+        case 0x8A4D0000u: // nsc -> Latn
+        case 0x924D0000u: // nse -> Latn
+        case 0x9A4D0000u: // nsg -> Latn
+        case 0x9E4D0000u: // nsh -> Latn
+        case 0xB24D0000u: // nsm -> Latn
+        case 0xB64D0000u: // nsn -> Latn
+        case 0xBA4D0000u: // nso -> Latn
+        case 0xC24D0000u: // nsq -> Latn
+        case 0xCA4D0000u: // nss -> Latn
+        case 0xD24D0000u: // nsu -> Latn
+        case 0xDA4D0000u: // nsw -> Latn
+        case 0xDE4D0000u: // nsx -> Latn
+        case 0xE24D0000u: // nsy -> Latn
+        case 0xE64D0000u: // nsz -> Latn
+        case 0x8E6D0000u: // ntd -> Latn
+        case 0x926D0000u: // nte -> Latn
+        case 0x9A6D0000u: // ntg -> Latn
+        case 0xA26D0000u: // nti -> Latn
+        case 0xA66D0000u: // ntj -> Latn
+        case 0xAA6D0000u: // ntk -> Latn
+        case 0xB26D0000u: // ntm -> Latn
+        case 0xBA6D0000u: // nto -> Latn
+        case 0xBE6D0000u: // ntp -> Latn
+        case 0xC66D0000u: // ntr -> Latn
+        case 0xD26D0000u: // ntu -> Latn
+        case 0xDE6D0000u: // ntx -> Latn
+        case 0x828D0000u: // nua -> Latn
+        case 0x8A8D0000u: // nuc -> Latn
+        case 0x8E8D0000u: // nud -> Latn
+        case 0x928D0000u: // nue -> Latn
+        case 0x968D0000u: // nuf -> Latn
+        case 0x9A8D0000u: // nug -> Latn
+        case 0x9E8D0000u: // nuh -> Latn
+        case 0xA28D0000u: // nui -> Latn
+        case 0xA68D0000u: // nuj -> Latn
+        case 0xAA8D0000u: // nuk -> Latn
+        case 0xB28D0000u: // num -> Latn
+        case 0xB68D0000u: // nun -> Latn
+        case 0xBA8D0000u: // nuo -> Latn
+        case 0xBE8D0000u: // nup -> Latn
+        case 0xC28D0000u: // nuq -> Latn
+        case 0xC68D0000u: // nur -> Latn
+        case 0xCA8D0000u: // nus -> Latn
+        case 0xCE8D0000u: // nut -> Latn
+        case 0xD28D0000u: // nuu -> Latn
+        case 0xD68D0000u: // nuv -> Latn
+        case 0xDA8D0000u: // nuw -> Latn
+        case 0xDE8D0000u: // nux -> Latn
+        case 0xE28D0000u: // nuy -> Latn
+        case 0xE68D0000u: // nuz -> Latn
+        case 0x6E760000u: // nv -> Latn
+        case 0x9EAD0000u: // nvh -> Latn
+        case 0xB2AD0000u: // nvm -> Latn
+        case 0xBAAD0000u: // nvo -> Latn
+        case 0x86CD0000u: // nwb -> Latn
+        case 0x92CD0000u: // nwe -> Latn
+        case 0x9ACD0000u: // nwg -> Latn
+        case 0xA2CD0000u: // nwi -> Latn
+        case 0xB2CD0000u: // nwm -> Latn
+        case 0xBACD0000u: // nwo -> Latn
+        case 0xC6CD0000u: // nwr -> Latn
+        case 0xDACD0000u: // nww -> Latn
+        case 0x82ED0000u: // nxa -> Latn
+        case 0x8EED0000u: // nxd -> Latn
+        case 0x92ED0000u: // nxe -> Latn
+        case 0x9AED0000u: // nxg -> Latn
+        case 0xA2ED0000u: // nxi -> Latn
+        case 0xAEED0000u: // nxl -> Latn
+        case 0xB6ED0000u: // nxn -> Latn
+        case 0xBAED0000u: // nxo -> Latn
+        case 0xC2ED0000u: // nxq -> Latn
+        case 0xC6ED0000u: // nxr -> Latn
+        case 0xDEED0000u: // nxx -> Latn
+        case 0x6E790000u: // ny -> Latn
+        case 0x870D0000u: // nyb -> Latn
+        case 0x8B0D0000u: // nyc -> Latn
+        case 0x8F0D0000u: // nyd -> Latn
+        case 0x930D0000u: // nye -> Latn
+        case 0x970D0000u: // nyf -> Latn
+        case 0x9B0D0000u: // nyg -> Latn
+        case 0x9F0D0000u: // nyh -> Latn
+        case 0xA30D0000u: // nyi -> Latn
+        case 0xA70D0000u: // nyj -> Latn
+        case 0xAB0D0000u: // nyk -> Latn
+        case 0xB30D0000u: // nym -> Latn
+        case 0xB70D0000u: // nyn -> Latn
+        case 0xBB0D0000u: // nyo -> Latn
+        case 0xBF0D0000u: // nyp -> Latn
+        case 0xC70D0000u: // nyr -> Latn
+        case 0xCB0D0000u: // nys -> Latn
+        case 0xCF0D0000u: // nyt -> Latn
+        case 0xD30D0000u: // nyu -> Latn
+        case 0xD70D0000u: // nyv -> Latn
+        case 0xDF0D0000u: // nyx -> Latn
+        case 0xE30D0000u: // nyy -> Latn
+        case 0x832D0000u: // nza -> Latn
+        case 0x872D0000u: // nzb -> Latn
+        case 0x8F2D0000u: // nzd -> Latn
+        case 0xA32D0000u: // nzi -> Latn
+        case 0xAB2D0000u: // nzk -> Latn
+        case 0xB32D0000u: // nzm -> Latn
+        case 0xC72D0000u: // nzr -> Latn
+        case 0xD32D0000u: // nzu -> Latn
+        case 0xE32D0000u: // nzy -> Latn
+        case 0xE72D0000u: // nzz -> Latn
+        case 0xA02E0000u: // obi -> Latn
+        case 0xA82E0000u: // obk -> Latn
+        case 0xAC2E0000u: // obl -> Latn
+        case 0xB82E0000u: // obo -> Latn
+        case 0xCC2E0000u: // obt -> Latn
+        case 0xD02E0000u: // obu -> Latn
+        case 0x6F630000u: // oc -> Latn
+        case 0x804E0000u: // oca -> Latn
+        case 0xB84E0000u: // oco -> Latn
+        case 0xD04E0000u: // ocu -> Latn
+        case 0x806E0000u: // oda -> Latn
+        case 0xCC6E0000u: // odt -> Latn
+        case 0xD06E0000u: // odu -> Latn
+        case 0xC8AE0000u: // ofs -> Latn
+        case 0xD0AE0000u: // ofu -> Latn
+        case 0x84CE0000u: // ogb -> Latn
+        case 0x88CE0000u: // ogc -> Latn
+        case 0x98CE0000u: // ogg -> Latn
+        case 0xB8CE0000u: // ogo -> Latn
+        case 0xD0CE0000u: // ogu -> Latn
+        case 0xD0EE0000u: // ohu -> Latn
+        case 0x810E0000u: // oia -> Latn
+        case 0x910E0000u: // oie -> Latn
+        case 0xB50E0000u: // oin -> Latn
+        case 0x852E0000u: // ojb -> Latn
+        case 0x892E0000u: // ojc -> Latn
+        case 0xD52E0000u: // ojv -> Latn
+        case 0xD92E0000u: // ojw -> Latn
+        case 0x814E0000u: // oka -> Latn
+        case 0x854E0000u: // okb -> Latn
+        case 0x894E0000u: // okc -> Latn
+        case 0x8D4E0000u: // okd -> Latn
+        case 0x914E0000u: // oke -> Latn
+        case 0x994E0000u: // okg -> Latn
+        case 0xA14E0000u: // oki -> Latn
+        case 0xA94E0000u: // okk -> Latn
+        case 0xC54E0000u: // okr -> Latn
+        case 0xC94E0000u: // oks -> Latn
+        case 0xD14E0000u: // oku -> Latn
+        case 0xD54E0000u: // okv -> Latn
+        case 0xDD4E0000u: // okx -> Latn
+        case 0x8D6E0000u: // old -> Latn
+        case 0xA96E0000u: // olk -> Latn
+        case 0xB16E0000u: // olm -> Latn
+        case 0xB96E0000u: // olo -> Latn
+        case 0xC56E0000u: // olr -> Latn
+        case 0xCD6E0000u: // olt -> Latn
+        case 0xD16E0000u: // olu -> Latn
+        case 0x6F6D0000u: // om -> Latn
+        case 0x818E0000u: // oma -> Latn
+        case 0x858E0000u: // omb -> Latn
+        case 0x898E0000u: // omc -> Latn
+        case 0x998E0000u: // omg -> Latn
+        case 0xA18E0000u: // omi -> Latn
+        case 0xAD8E0000u: // oml -> Latn
+        case 0xB98E0000u: // omo -> Latn
+        case 0xCD8E0000u: // omt -> Latn
+        case 0xD18E0000u: // omu -> Latn
+        case 0xD98E0000u: // omw -> Latn
+        case 0x81AE0000u: // ona -> Latn
+        case 0x91AE0000u: // one -> Latn
+        case 0x99AE0000u: // ong -> Latn
+        case 0xA1AE0000u: // oni -> Latn
+        case 0xA5AE0000u: // onj -> Latn
+        case 0xA9AE0000u: // onk -> Latn
+        case 0xB5AE0000u: // onn -> Latn
+        case 0xB9AE0000u: // ono -> Latn
+        case 0xBDAE0000u: // onp -> Latn
+        case 0xC5AE0000u: // onr -> Latn
+        case 0xC9AE0000u: // ons -> Latn
+        case 0xCDAE0000u: // ont -> Latn
+        case 0xD1AE0000u: // onu -> Latn
+        case 0xDDAE0000u: // onx -> Latn
+        case 0x8DCE0000u: // ood -> Latn
+        case 0xC5CE0000u: // oor -> Latn
+        case 0x81EE0000u: // opa -> Latn
+        case 0xA9EE0000u: // opk -> Latn
+        case 0xB1EE0000u: // opm -> Latn
+        case 0xB9EE0000u: // opo -> Latn
+        case 0xCDEE0000u: // opt -> Latn
+        case 0xE1EE0000u: // opy -> Latn
+        case 0x822E0000u: // ora -> Latn
+        case 0x8A2E0000u: // orc -> Latn
+        case 0x922E0000u: // ore -> Latn
+        case 0x9A2E0000u: // org -> Latn
+        case 0xB62E0000u: // orn -> Latn
+        case 0xBA2E0000u: // oro -> Latn
+        case 0xC62E0000u: // orr -> Latn
+        case 0xCA2E0000u: // ors -> Latn
+        case 0xDA2E0000u: // orw -> Latn
+        case 0xDE2E0000u: // orx -> Latn
+        case 0xE62E0000u: // orz -> Latn
+        case 0xBA4E0000u: // oso -> Latn
+        case 0xBE4E0000u: // osp -> Latn
+        case 0xCE4E0000u: // ost -> Latn
+        case 0xD24E0000u: // osu -> Latn
+        case 0xDE4E0000u: // osx -> Latn
+        case 0x8E6E0000u: // otd -> Latn
+        case 0x926E0000u: // ote -> Latn
+        case 0xA26E0000u: // oti -> Latn
+        case 0xAE6E0000u: // otl -> Latn
+        case 0xB26E0000u: // otm -> Latn
+        case 0xB66E0000u: // otn -> Latn
+        case 0xC26E0000u: // otq -> Latn
+        case 0xC66E0000u: // otr -> Latn
+        case 0xCA6E0000u: // ots -> Latn
+        case 0xCE6E0000u: // ott -> Latn
+        case 0xD26E0000u: // otu -> Latn
+        case 0xDA6E0000u: // otw -> Latn
+        case 0xDE6E0000u: // otx -> Latn
+        case 0xE66E0000u: // otz -> Latn
+        case 0x868E0000u: // oub -> Latn
+        case 0x928E0000u: // oue -> Latn
+        case 0xB28E0000u: // oum -> Latn
+        case 0x8EAE0000u: // ovd -> Latn
+        case 0xA2CE0000u: // owi -> Latn
+        case 0xAECE0000u: // owl -> Latn
+        case 0x8F0E0000u: // oyd -> Latn
+        case 0xB30E0000u: // oym -> Latn
+        case 0xE30E0000u: // oyy -> Latn
+        case 0xB32E0000u: // ozm -> Latn
+        case 0x840F0000u: // pab -> Latn
+        case 0x880F0000u: // pac -> Latn
+        case 0x8C0F0000u: // pad -> Latn
+        case 0x900F0000u: // pae -> Latn
+        case 0x940F0000u: // paf -> Latn
+        case 0x980F0000u: // pag -> Latn
+        case 0x9C0F0000u: // pah -> Latn
+        case 0xA00F0000u: // pai -> Latn
+        case 0xA80F0000u: // pak -> Latn
+        case 0xB00F0000u: // pam -> Latn
+        case 0xB80F0000u: // pao -> Latn
+        case 0xBC0F0000u: // pap -> Latn
+        case 0xC40F0000u: // par -> Latn
+        case 0xC80F0000u: // pas -> Latn
+        case 0xD00F0000u: // pau -> Latn
+        case 0xD40F0000u: // pav -> Latn
+        case 0xD80F0000u: // paw -> Latn
+        case 0xDC0F0000u: // pax -> Latn
+        case 0xE00F0000u: // pay -> Latn
+        case 0xE40F0000u: // paz -> Latn
+        case 0x842F0000u: // pbb -> Latn
+        case 0x882F0000u: // pbc -> Latn
+        case 0x902F0000u: // pbe -> Latn
+        case 0x942F0000u: // pbf -> Latn
+        case 0x982F0000u: // pbg -> Latn
+        case 0x9C2F0000u: // pbh -> Latn
+        case 0xA02F0000u: // pbi -> Latn
+        case 0xAC2F0000u: // pbl -> Latn
+        case 0xB02F0000u: // pbm -> Latn
+        case 0xB42F0000u: // pbn -> Latn
+        case 0xB82F0000u: // pbo -> Latn
+        case 0xBC2F0000u: // pbp -> Latn
+        case 0xC42F0000u: // pbr -> Latn
+        case 0xC82F0000u: // pbs -> Latn
+        case 0xD42F0000u: // pbv -> Latn
+        case 0xE02F0000u: // pby -> Latn
+        case 0x804F0000u: // pca -> Latn
+        case 0x884F0000u: // pcc -> Latn
+        case 0x8C4F0000u: // pcd -> Latn
+        case 0xA84F0000u: // pck -> Latn
+        case 0xB04F0000u: // pcm -> Latn
+        case 0xB44F0000u: // pcn -> Latn
+        case 0xBC4F0000u: // pcp -> Latn
+        case 0xD84F0000u: // pcw -> Latn
+        case 0x806F0000u: // pda -> Latn
+        case 0x886F0000u: // pdc -> Latn
+        case 0xB46F0000u: // pdn -> Latn
+        case 0xB86F0000u: // pdo -> Latn
+        case 0xCC6F0000u: // pdt -> Latn
+        case 0xD06F0000u: // pdu -> Latn
+        case 0x808F0000u: // pea -> Latn
+        case 0x848F0000u: // peb -> Latn
+        case 0x8C8F0000u: // ped -> Latn
+        case 0x908F0000u: // pee -> Latn
+        case 0xA08F0000u: // pei -> Latn
+        case 0xA88F0000u: // pek -> Latn
+        case 0xAC8F0000u: // pel -> Latn
+        case 0xB08F0000u: // pem -> Latn
+        case 0xBC8F0000u: // pep -> Latn
+        case 0xC08F0000u: // peq -> Latn
+        case 0xD48F0000u: // pev -> Latn
+        case 0xDC8F0000u: // pex -> Latn
+        case 0xE08F0000u: // pey -> Latn
+        case 0xE48F0000u: // pez -> Latn
+        case 0x80AF0000u: // pfa -> Latn
+        case 0x90AF0000u: // pfe -> Latn
+        case 0xACAF0000u: // pfl -> Latn
+        case 0x80CF0000u: // pga -> Latn
+        case 0xA0CF0000u: // pgi -> Latn
+        case 0xA8CF0000u: // pgk -> Latn
+        case 0xC8CF0000u: // pgs -> Latn
+        case 0xD0CF0000u: // pgu -> Latn
+        case 0x98EF0000u: // phg -> Latn
+        case 0x9CEF0000u: // phh -> Latn
+        case 0xB0EF0000u: // phm -> Latn
+        case 0x810F0000u: // pia -> Latn
+        case 0x850F0000u: // pib -> Latn
+        case 0x890F0000u: // pic -> Latn
+        case 0x8D0F0000u: // pid -> Latn
+        case 0x950F0000u: // pif -> Latn
+        case 0x990F0000u: // pig -> Latn
+        case 0x9D0F0000u: // pih -> Latn
+        case 0xA50F0000u: // pij -> Latn
+        case 0xAD0F0000u: // pil -> Latn
+        case 0xB10F0000u: // pim -> Latn
+        case 0xB50F0000u: // pin -> Latn
+        case 0xB90F0000u: // pio -> Latn
+        case 0xBD0F0000u: // pip -> Latn
+        case 0xC50F0000u: // pir -> Latn
+        case 0xC90F0000u: // pis -> Latn
+        case 0xCD0F0000u: // pit -> Latn
+        case 0xD10F0000u: // piu -> Latn
+        case 0xD50F0000u: // piv -> Latn
+        case 0xD90F0000u: // piw -> Latn
+        case 0xDD0F0000u: // pix -> Latn
+        case 0xE10F0000u: // piy -> Latn
+        case 0xE50F0000u: // piz -> Latn
+        case 0xCD2F0000u: // pjt -> Latn
+        case 0x854F0000u: // pkb -> Latn
+        case 0x994F0000u: // pkg -> Latn
+        case 0x9D4F0000u: // pkh -> Latn
+        case 0xB54F0000u: // pkn -> Latn
+        case 0xB94F0000u: // pko -> Latn
+        case 0xBD4F0000u: // pkp -> Latn
+        case 0xD14F0000u: // pku -> Latn
+        case 0x706C0000u: // pl -> Latn
+        case 0x816F0000u: // pla -> Latn
+        case 0x856F0000u: // plb -> Latn
+        case 0x896F0000u: // plc -> Latn
+        case 0x8D6F0000u: // pld -> Latn
+        case 0x916F0000u: // ple -> Latn
+        case 0x996F0000u: // plg -> Latn
+        case 0x9D6F0000u: // plh -> Latn
+        case 0xB56F0000u: // pln -> Latn
+        case 0xB96F0000u: // plo -> Latn
+        case 0xC56F0000u: // plr -> Latn
+        case 0xC96F0000u: // pls -> Latn
+        case 0xD16F0000u: // plu -> Latn
+        case 0xD56F0000u: // plv -> Latn
+        case 0xD96F0000u: // plw -> Latn
+        case 0xE56F0000u: // plz -> Latn
+        case 0x818F0000u: // pma -> Latn
+        case 0x858F0000u: // pmb -> Latn
+        case 0x8D8F0000u: // pmd -> Latn
+        case 0x918F0000u: // pme -> Latn
+        case 0x958F0000u: // pmf -> Latn
+        case 0xA18F0000u: // pmi -> Latn
+        case 0xA58F0000u: // pmj -> Latn
+        case 0xAD8F0000u: // pml -> Latn
+        case 0xB18F0000u: // pmm -> Latn
+        case 0xB58F0000u: // pmn -> Latn
+        case 0xB98F0000u: // pmo -> Latn
+        case 0xC18F0000u: // pmq -> Latn
+        case 0xC58F0000u: // pmr -> Latn
+        case 0xC98F0000u: // pms -> Latn
+        case 0xCD8F0000u: // pmt -> Latn
+        case 0xD98F0000u: // pmw -> Latn
+        case 0xDD8F0000u: // pmx -> Latn
+        case 0xE18F0000u: // pmy -> Latn
+        case 0xE58F0000u: // pmz -> Latn
+        case 0x81AF0000u: // pna -> Latn
+        case 0x89AF0000u: // pnc -> Latn
+        case 0x8DAF0000u: // pnd -> Latn
+        case 0x91AF0000u: // pne -> Latn
+        case 0x99AF0000u: // png -> Latn
+        case 0x9DAF0000u: // pnh -> Latn
+        case 0xA1AF0000u: // pni -> Latn
+        case 0xA5AF0000u: // pnj -> Latn
+        case 0xA9AF0000u: // pnk -> Latn
+        case 0xADAF0000u: // pnl -> Latn
+        case 0xB1AF0000u: // pnm -> Latn
+        case 0xB5AF0000u: // pnn -> Latn
+        case 0xB9AF0000u: // pno -> Latn
+        case 0xBDAF0000u: // pnp -> Latn
+        case 0xC1AF0000u: // pnq -> Latn
+        case 0xC5AF0000u: // pnr -> Latn
+        case 0xC9AF0000u: // pns -> Latn
+        case 0xD5AF0000u: // pnv -> Latn
+        case 0xD9AF0000u: // pnw -> Latn
+        case 0xE1AF0000u: // pny -> Latn
+        case 0xE5AF0000u: // pnz -> Latn
+        case 0x89CF0000u: // poc -> Latn
+        case 0x91CF0000u: // poe -> Latn
+        case 0x95CF0000u: // pof -> Latn
+        case 0x99CF0000u: // pog -> Latn
+        case 0x9DCF0000u: // poh -> Latn
+        case 0xA1CF0000u: // poi -> Latn
+        case 0xA9CF0000u: // pok -> Latn
+        case 0xB1CF0000u: // pom -> Latn
+        case 0xB5CF0000u: // pon -> Latn
+        case 0xB9CF0000u: // poo -> Latn
+        case 0xBDCF0000u: // pop -> Latn
+        case 0xC1CF0000u: // poq -> Latn
+        case 0xC9CF0000u: // pos -> Latn
+        case 0xCDCF0000u: // pot -> Latn
+        case 0xD5CF0000u: // pov -> Latn
+        case 0xD9CF0000u: // pow -> Latn
+        case 0xE1CF0000u: // poy -> Latn
+        case 0x91EF0000u: // ppe -> Latn
+        case 0xA1EF0000u: // ppi -> Latn
+        case 0xA9EF0000u: // ppk -> Latn
+        case 0xADEF0000u: // ppl -> Latn
+        case 0xB1EF0000u: // ppm -> Latn
+        case 0xB5EF0000u: // ppn -> Latn
+        case 0xB9EF0000u: // ppo -> Latn
+        case 0xBDEF0000u: // ppp -> Latn
+        case 0xC1EF0000u: // ppq -> Latn
+        case 0xC9EF0000u: // pps -> Latn
+        case 0xCDEF0000u: // ppt -> Latn
+        case 0x820F0000u: // pqa -> Latn
+        case 0xB20F0000u: // pqm -> Latn
+        case 0x922F0000u: // pre -> Latn
+        case 0x962F0000u: // prf -> Latn
+        case 0x9A2F0000u: // prg -> Latn
+        case 0x9E2F0000u: // prh -> Latn
+        case 0xA22F0000u: // pri -> Latn
+        case 0xAA2F0000u: // prk -> Latn
+        case 0xB22F0000u: // prm -> Latn
+        case 0xBA2F0000u: // pro -> Latn
+        case 0xC22F0000u: // prq -> Latn
+        case 0xC62F0000u: // prr -> Latn
+        case 0xD22F0000u: // pru -> Latn
+        case 0xDA2F0000u: // prw -> Latn
+        case 0x824F0000u: // psa -> Latn
+        case 0x924F0000u: // pse -> Latn
+        case 0xB24F0000u: // psm -> Latn
+        case 0xB64F0000u: // psn -> Latn
+        case 0xC24F0000u: // psq -> Latn
+        case 0xCA4F0000u: // pss -> Latn
+        case 0xDA4F0000u: // psw -> Latn
+        case 0x70740000u: // pt -> Latn
+        case 0x826F0000u: // pta -> Latn
+        case 0x9E6F0000u: // pth -> Latn
+        case 0xA26F0000u: // pti -> Latn
+        case 0xB66F0000u: // ptn -> Latn
+        case 0xBA6F0000u: // pto -> Latn
+        case 0xBE6F0000u: // ptp -> Latn
+        case 0xC66F0000u: // ptr -> Latn
+        case 0xCE6F0000u: // ptt -> Latn
+        case 0xD26F0000u: // ptu -> Latn
+        case 0xD66F0000u: // ptv -> Latn
+        case 0x828F0000u: // pua -> Latn
+        case 0x868F0000u: // pub -> Latn
+        case 0x8A8F0000u: // puc -> Latn
+        case 0x8E8F0000u: // pud -> Latn
+        case 0x928F0000u: // pue -> Latn
+        case 0x968F0000u: // puf -> Latn
+        case 0x9A8F0000u: // pug -> Latn
+        case 0xA28F0000u: // pui -> Latn
+        case 0xA68F0000u: // puj -> Latn
+        case 0xBA8F0000u: // puo -> Latn
+        case 0xBE8F0000u: // pup -> Latn
+        case 0xC28F0000u: // puq -> Latn
+        case 0xC68F0000u: // pur -> Latn
+        case 0xCE8F0000u: // put -> Latn
+        case 0xD28F0000u: // puu -> Latn
+        case 0xDA8F0000u: // puw -> Latn
+        case 0xDE8F0000u: // pux -> Latn
+        case 0xE28F0000u: // puy -> Latn
+        case 0x82CF0000u: // pwa -> Latn
+        case 0x86CF0000u: // pwb -> Latn
+        case 0x9ACF0000u: // pwg -> Latn
+        case 0xB2CF0000u: // pwm -> Latn
+        case 0xB6CF0000u: // pwn -> Latn
+        case 0xB2EF0000u: // pxm -> Latn
+        case 0x930F0000u: // pye -> Latn
+        case 0xB30F0000u: // pym -> Latn
+        case 0xB70F0000u: // pyn -> Latn
+        case 0xD30F0000u: // pyu -> Latn
+        case 0xE30F0000u: // pyy -> Latn
+        case 0x932F0000u: // pze -> Latn
+        case 0x9F2F0000u: // pzh -> Latn
+        case 0xB72F0000u: // pzn -> Latn
+        case 0x71750000u: // qu -> Latn
+        case 0x82900000u: // qua -> Latn
+        case 0x86900000u: // qub -> Latn
+        case 0x8A900000u: // quc -> Latn
+        case 0x8E900000u: // qud -> Latn
+        case 0x96900000u: // quf -> Latn
+        case 0x9A900000u: // qug -> Latn
+        case 0xA2900000u: // qui -> Latn
+        case 0xAA900000u: // quk -> Latn
+        case 0xAE900000u: // qul -> Latn
+        case 0xB2900000u: // qum -> Latn
+        case 0xB6900000u: // qun -> Latn
+        case 0xBE900000u: // qup -> Latn
+        case 0xC2900000u: // quq -> Latn
+        case 0xC6900000u: // qur -> Latn
+        case 0xCA900000u: // qus -> Latn
+        case 0xD6900000u: // quv -> Latn
+        case 0xDA900000u: // quw -> Latn
+        case 0xDE900000u: // qux -> Latn
+        case 0xE2900000u: // quy -> Latn
+        case 0x82B00000u: // qva -> Latn
+        case 0x8AB00000u: // qvc -> Latn
+        case 0x92B00000u: // qve -> Latn
+        case 0x9EB00000u: // qvh -> Latn
+        case 0xA2B00000u: // qvi -> Latn
+        case 0xA6B00000u: // qvj -> Latn
+        case 0xAEB00000u: // qvl -> Latn
+        case 0xB2B00000u: // qvm -> Latn
+        case 0xB6B00000u: // qvn -> Latn
+        case 0xBAB00000u: // qvo -> Latn
+        case 0xBEB00000u: // qvp -> Latn
+        case 0xCAB00000u: // qvs -> Latn
+        case 0xDAB00000u: // qvw -> Latn
+        case 0xE6B00000u: // qvz -> Latn
+        case 0x82D00000u: // qwa -> Latn
+        case 0x8AD00000u: // qwc -> Latn
+        case 0x9ED00000u: // qwh -> Latn
+        case 0xB2D00000u: // qwm -> Latn
+        case 0xCAD00000u: // qws -> Latn
+        case 0xCED00000u: // qwt -> Latn
+        case 0x82F00000u: // qxa -> Latn
+        case 0x8AF00000u: // qxc -> Latn
+        case 0x9EF00000u: // qxh -> Latn
+        case 0xAEF00000u: // qxl -> Latn
+        case 0xB6F00000u: // qxn -> Latn
+        case 0xBAF00000u: // qxo -> Latn
+        case 0xBEF00000u: // qxp -> Latn
+        case 0xC6F00000u: // qxr -> Latn
+        case 0xCEF00000u: // qxt -> Latn
+        case 0xD2F00000u: // qxu -> Latn
+        case 0xDAF00000u: // qxw -> Latn
+        case 0x83100000u: // qya -> Latn
+        case 0xBF100000u: // qyp -> Latn
+        case 0x88110000u: // rac -> Latn
+        case 0x8C110000u: // rad -> Latn
+        case 0x98110000u: // rag -> Latn
+        case 0xA0110000u: // rai -> Latn
+        case 0xA8110000u: // rak -> Latn
+        case 0xB0110000u: // ram -> Latn
+        case 0xB4110000u: // ran -> Latn
+        case 0xB8110000u: // rao -> Latn
+        case 0xBC110000u: // rap -> Latn
+        case 0xC4110000u: // rar -> Latn
+        case 0xD8110000u: // raw -> Latn
+        case 0xDC110000u: // rax -> Latn
+        case 0xE0110000u: // ray -> Latn
+        case 0xE4110000u: // raz -> Latn
+        case 0xA8310000u: // rbk -> Latn
+        case 0xAC310000u: // rbl -> Latn
+        case 0xBC310000u: // rbp -> Latn
+        case 0x94510000u: // rcf -> Latn
+        case 0x80910000u: // rea -> Latn
+        case 0x84910000u: // reb -> Latn
+        case 0x90910000u: // ree -> Latn
+        case 0x98910000u: // reg -> Latn
+        case 0xA4910000u: // rej -> Latn
+        case 0xAC910000u: // rel -> Latn
+        case 0xB0910000u: // rem -> Latn
+        case 0xB4910000u: // ren -> Latn
+        case 0xC8910000u: // res -> Latn
+        case 0xCC910000u: // ret -> Latn
+        case 0xE0910000u: // rey -> Latn
+        case 0x80D10000u: // rga -> Latn
+        case 0xB4D10000u: // rgn -> Latn
+        case 0xC4D10000u: // rgr -> Latn
+        case 0xC8D10000u: // rgs -> Latn
+        case 0xD0D10000u: // rgu -> Latn
+        case 0xBCF10000u: // rhp -> Latn
+        case 0x81110000u: // ria -> Latn
+        case 0x95110000u: // rif -> Latn
+        case 0xAD110000u: // ril -> Latn
+        case 0xB1110000u: // rim -> Latn
+        case 0xB5110000u: // rin -> Latn
+        case 0xC5110000u: // rir -> Latn
+        case 0xCD110000u: // rit -> Latn
+        case 0xD1110000u: // riu -> Latn
+        case 0x99310000u: // rjg -> Latn
+        case 0x85510000u: // rkb -> Latn
+        case 0x9D510000u: // rkh -> Latn
+        case 0xB1510000u: // rkm -> Latn
+        case 0xD9510000u: // rkw -> Latn
+        case 0x726D0000u: // rm -> Latn
+        case 0x81910000u: // rma -> Latn
+        case 0x85910000u: // rmb -> Latn
+        case 0x89910000u: // rmc -> Latn
+        case 0x8D910000u: // rmd -> Latn
+        case 0x91910000u: // rme -> Latn
+        case 0x95910000u: // rmf -> Latn
+        case 0x99910000u: // rmg -> Latn
+        case 0x9D910000u: // rmh -> Latn
+        case 0xA9910000u: // rmk -> Latn
+        case 0xAD910000u: // rml -> Latn
+        case 0xB1910000u: // rmm -> Latn
+        case 0xB5910000u: // rmn -> Latn
+        case 0xB9910000u: // rmo -> Latn
+        case 0xBD910000u: // rmp -> Latn
+        case 0xC1910000u: // rmq -> Latn
+        case 0xD1910000u: // rmu -> Latn
+        case 0xD9910000u: // rmw -> Latn
+        case 0xDD910000u: // rmx -> Latn
+        case 0x726E0000u: // rn -> Latn
+        case 0x8DB10000u: // rnd -> Latn
+        case 0x99B10000u: // rng -> Latn
+        case 0xADB10000u: // rnl -> Latn
+        case 0xB5B10000u: // rnn -> Latn
+        case 0xC5B10000u: // rnr -> Latn
+        case 0xD9B10000u: // rnw -> Latn
+        case 0x726F0000u: // ro -> Latn
+        case 0x85D10000u: // rob -> Latn
+        case 0x89D10000u: // roc -> Latn
+        case 0x8DD10000u: // rod -> Latn
+        case 0x91D10000u: // roe -> Latn
+        case 0x95D10000u: // rof -> Latn
+        case 0x99D10000u: // rog -> Latn
+        case 0xADD10000u: // rol -> Latn
+        case 0xB1D10000u: // rom -> Latn
+        case 0xB9D10000u: // roo -> Latn
+        case 0xBDD10000u: // rop -> Latn
+        case 0xC5D10000u: // ror -> Latn
+        case 0xD1D10000u: // rou -> Latn
+        case 0xD9D10000u: // row -> Latn
+        case 0xB5F10000u: // rpn -> Latn
+        case 0xCDF10000u: // rpt -> Latn
+        case 0xA2310000u: // rri -> Latn
+        case 0xB2310000u: // rrm -> Latn
+        case 0xBA310000u: // rro -> Latn
+        case 0xCE310000u: // rrt -> Latn
+        case 0xDA510000u: // rsw -> Latn
+        case 0x8A710000u: // rtc -> Latn
+        case 0x9E710000u: // rth -> Latn
+        case 0xB2710000u: // rtm -> Latn
+        case 0x86910000u: // rub -> Latn
+        case 0x8A910000u: // ruc -> Latn
+        case 0x96910000u: // ruf -> Latn
+        case 0x9A910000u: // rug -> Latn
+        case 0xA2910000u: // rui -> Latn
+        case 0xAA910000u: // ruk -> Latn
+        case 0xBA910000u: // ruo -> Latn
+        case 0xBE910000u: // rup -> Latn
+        case 0xC2910000u: // ruq -> Latn
+        case 0xD2910000u: // ruu -> Latn
+        case 0xE2910000u: // ruy -> Latn
+        case 0xE6910000u: // ruz -> Latn
+        case 0x72770000u: // rw -> Latn
+        case 0x82D10000u: // rwa -> Latn
+        case 0xAAD10000u: // rwk -> Latn
+        case 0xAED10000u: // rwl -> Latn
+        case 0xB2D10000u: // rwm -> Latn
+        case 0xBAD10000u: // rwo -> Latn
+        case 0x8EF10000u: // rxd -> Latn
+        case 0xDAF10000u: // rxw -> Latn
+        case 0x80120000u: // saa -> Latn
+        case 0x84120000u: // sab -> Latn
+        case 0x88120000u: // sac -> Latn
+        case 0x8C120000u: // sad -> Latn
+        case 0x90120000u: // sae -> Latn
+        case 0x94120000u: // saf -> Latn
+        case 0xA4120000u: // saj -> Latn
+        case 0xA8120000u: // sak -> Latn
+        case 0xB8120000u: // sao -> Latn
+        case 0xC0120000u: // saq -> Latn
+        case 0xC4120000u: // sar -> Latn
+        case 0xC8120000u: // sas -> Latn
+        case 0xD0120000u: // sau -> Latn
+        case 0xD4120000u: // sav -> Latn
+        case 0xD8120000u: // saw -> Latn
+        case 0xDC120000u: // sax -> Latn
+        case 0xE0120000u: // say -> Latn
+        case 0x80320000u: // sba -> Latn
+        case 0x84320000u: // sbb -> Latn
+        case 0x88320000u: // sbc -> Latn
+        case 0x8C320000u: // sbd -> Latn
+        case 0x90320000u: // sbe -> Latn
+        case 0x98320000u: // sbg -> Latn
+        case 0x9C320000u: // sbh -> Latn
+        case 0xA0320000u: // sbi -> Latn
+        case 0xA4320000u: // sbj -> Latn
+        case 0xA8320000u: // sbk -> Latn
+        case 0xAC320000u: // sbl -> Latn
+        case 0xB0320000u: // sbm -> Latn
+        case 0xB8320000u: // sbo -> Latn
+        case 0xBC320000u: // sbp -> Latn
+        case 0xC0320000u: // sbq -> Latn
+        case 0xC4320000u: // sbr -> Latn
+        case 0xC8320000u: // sbs -> Latn
+        case 0xCC320000u: // sbt -> Latn
+        case 0xD4320000u: // sbv -> Latn
+        case 0xD8320000u: // sbw -> Latn
+        case 0xDC320000u: // sbx -> Latn
+        case 0xE0320000u: // sby -> Latn
+        case 0xE4320000u: // sbz -> Latn
+        case 0x73630000u: // sc -> Latn
+        case 0x84520000u: // scb -> Latn
+        case 0x90520000u: // sce -> Latn
+        case 0x94520000u: // scf -> Latn
+        case 0x98520000u: // scg -> Latn
+        case 0x9C520000u: // sch -> Latn
+        case 0xA0520000u: // sci -> Latn
+        case 0xB4520000u: // scn -> Latn
+        case 0xB8520000u: // sco -> Latn
+        case 0xC8520000u: // scs -> Latn
+        case 0xD4520000u: // scv -> Latn
+        case 0xD8520000u: // scw -> Latn
+        case 0x80720000u: // sda -> Latn
+        case 0x88720000u: // sdc -> Latn
+        case 0x90720000u: // sde -> Latn
+        case 0xA4720000u: // sdj -> Latn
+        case 0xA8720000u: // sdk -> Latn
+        case 0xB4720000u: // sdn -> Latn
+        case 0xB8720000u: // sdo -> Latn
+        case 0xC0720000u: // sdq -> Latn
+        case 0xD0720000u: // sdu -> Latn
+        case 0xDC720000u: // sdx -> Latn
+        case 0x73650000u: // se -> Latn
+        case 0x80920000u: // sea -> Latn
+        case 0x84920000u: // seb -> Latn
+        case 0x88920000u: // sec -> Latn
+        case 0x8C920000u: // sed -> Latn
+        case 0x90920000u: // see -> Latn
+        case 0x94920000u: // sef -> Latn
+        case 0x98920000u: // seg -> Latn
+        case 0x9C920000u: // seh -> Latn
+        case 0xA0920000u: // sei -> Latn
+        case 0xA4920000u: // sej -> Latn
+        case 0xA8920000u: // sek -> Latn
+        case 0xB4920000u: // sen -> Latn
+        case 0xB8920000u: // seo -> Latn
+        case 0xBC920000u: // sep -> Latn
+        case 0xC0920000u: // seq -> Latn
+        case 0xC4920000u: // ser -> Latn
+        case 0xC8920000u: // ses -> Latn
+        case 0xCC920000u: // set -> Latn
+        case 0xD0920000u: // seu -> Latn
+        case 0xD4920000u: // sev -> Latn
+        case 0xD8920000u: // sew -> Latn
+        case 0xE0920000u: // sey -> Latn
+        case 0xE4920000u: // sez -> Latn
+        case 0x90B20000u: // sfe -> Latn
+        case 0xD8B20000u: // sfw -> Latn
+        case 0x73670000u: // sg -> Latn
+        case 0x84D20000u: // sgb -> Latn
+        case 0x88D20000u: // sgc -> Latn
+        case 0x8CD20000u: // sgd -> Latn
+        case 0x90D20000u: // sge -> Latn
+        case 0xA0D20000u: // sgi -> Latn
+        case 0xB0D20000u: // sgm -> Latn
+        case 0xBCD20000u: // sgp -> Latn
+        case 0xC8D20000u: // sgs -> Latn
+        case 0xD0D20000u: // sgu -> Latn
+        case 0xE4D20000u: // sgz -> Latn
+        case 0x80F20000u: // sha -> Latn
+        case 0x84F20000u: // shb -> Latn
+        case 0x88F20000u: // shc -> Latn
+        case 0x90F20000u: // she -> Latn
+        case 0x98F20000u: // shg -> Latn
+        case 0x9CF20000u: // shh -> Latn
+        case 0xA4F20000u: // shj -> Latn
+        case 0xA8F20000u: // shk -> Latn
+        case 0xB8F20000u: // sho -> Latn
+        case 0xBCF20000u: // shp -> Latn
+        case 0xC0F20000u: // shq -> Latn
+        case 0xC4F20000u: // shr -> Latn
+        case 0xC8F20000u: // shs -> Latn
+        case 0xCCF20000u: // sht -> Latn
+        case 0xD8F20000u: // shw -> Latn
+        case 0xE0F20000u: // shy -> Latn
+        case 0xE4F20000u: // shz -> Latn
+        case 0x85120000u: // sib -> Latn
+        case 0x8D120000u: // sid -> Latn
+        case 0x91120000u: // sie -> Latn
+        case 0x95120000u: // sif -> Latn
+        case 0x99120000u: // sig -> Latn
+        case 0x9D120000u: // sih -> Latn
+        case 0xA1120000u: // sii -> Latn
+        case 0xA5120000u: // sij -> Latn
+        case 0xA9120000u: // sik -> Latn
+        case 0xAD120000u: // sil -> Latn
+        case 0xB1120000u: // sim -> Latn
+        case 0xC1120000u: // siq -> Latn
+        case 0xC5120000u: // sir -> Latn
+        case 0xC9120000u: // sis -> Latn
+        case 0xD1120000u: // siu -> Latn
+        case 0xD5120000u: // siv -> Latn
+        case 0xD9120000u: // siw -> Latn
+        case 0xDD120000u: // six -> Latn
+        case 0x81320000u: // sja -> Latn
+        case 0x85320000u: // sjb -> Latn
+        case 0x91320000u: // sje -> Latn
+        case 0x99320000u: // sjg -> Latn
+        case 0xAD320000u: // sjl -> Latn
+        case 0xB1320000u: // sjm -> Latn
+        case 0xC5320000u: // sjr -> Latn
+        case 0xD1320000u: // sju -> Latn
+        case 0xD9320000u: // sjw -> Latn
+        case 0x736B0000u: // sk -> Latn
+        case 0x81520000u: // ska -> Latn
+        case 0x89520000u: // skc -> Latn
+        case 0x8D520000u: // skd -> Latn
+        case 0x91520000u: // ske -> Latn
+        case 0x95520000u: // skf -> Latn
+        case 0x99520000u: // skg -> Latn
+        case 0x9D520000u: // skh -> Latn
+        case 0xA1520000u: // ski -> Latn
+        case 0xB1520000u: // skm -> Latn
+        case 0xB5520000u: // skn -> Latn
+        case 0xB9520000u: // sko -> Latn
+        case 0xBD520000u: // skp -> Latn
+        case 0xC1520000u: // skq -> Latn
+        case 0xC9520000u: // sks -> Latn
+        case 0xCD520000u: // skt -> Latn
+        case 0xD1520000u: // sku -> Latn
+        case 0xD5520000u: // skv -> Latn
+        case 0xD9520000u: // skw -> Latn
+        case 0xDD520000u: // skx -> Latn
+        case 0xE1520000u: // sky -> Latn
+        case 0xE5520000u: // skz -> Latn
+        case 0x736C0000u: // sl -> Latn
+        case 0x89720000u: // slc -> Latn
+        case 0x8D720000u: // sld -> Latn
+        case 0x99720000u: // slg -> Latn
+        case 0x9D720000u: // slh -> Latn
+        case 0xA1720000u: // sli -> Latn
+        case 0xA5720000u: // slj -> Latn
+        case 0xAD720000u: // sll -> Latn
+        case 0xB1720000u: // slm -> Latn
+        case 0xB5720000u: // sln -> Latn
+        case 0xBD720000u: // slp -> Latn
+        case 0xC5720000u: // slr -> Latn
+        case 0xD1720000u: // slu -> Latn
+        case 0xD9720000u: // slw -> Latn
+        case 0xDD720000u: // slx -> Latn
+        case 0xE1720000u: // sly -> Latn
+        case 0xE5720000u: // slz -> Latn
+        case 0x736D0000u: // sm -> Latn
+        case 0x81920000u: // sma -> Latn
+        case 0x85920000u: // smb -> Latn
+        case 0x89920000u: // smc -> Latn
+        case 0x95920000u: // smf -> Latn
+        case 0x99920000u: // smg -> Latn
+        case 0xA5920000u: // smj -> Latn
+        case 0xA9920000u: // smk -> Latn
+        case 0xAD920000u: // sml -> Latn
+        case 0xB5920000u: // smn -> Latn
+        case 0xC1920000u: // smq -> Latn
+        case 0xC5920000u: // smr -> Latn
+        case 0xC9920000u: // sms -> Latn
+        case 0xCD920000u: // smt -> Latn
+        case 0xD9920000u: // smw -> Latn
+        case 0xDD920000u: // smx -> Latn
+        case 0xE5920000u: // smz -> Latn
+        case 0x736E0000u: // sn -> Latn
+        case 0x89B20000u: // snc -> Latn
+        case 0x91B20000u: // sne -> Latn
+        case 0x95B20000u: // snf -> Latn
+        case 0x99B20000u: // sng -> Latn
+        case 0xA1B20000u: // sni -> Latn
+        case 0xA5B20000u: // snj -> Latn
+        case 0xA9B20000u: // snk -> Latn
+        case 0xADB20000u: // snl -> Latn
+        case 0xB1B20000u: // snm -> Latn
+        case 0xB5B20000u: // snn -> Latn
+        case 0xB9B20000u: // sno -> Latn
+        case 0xBDB20000u: // snp -> Latn
+        case 0xC1B20000u: // snq -> Latn
+        case 0xC5B20000u: // snr -> Latn
+        case 0xC9B20000u: // sns -> Latn
+        case 0xD1B20000u: // snu -> Latn
+        case 0xD5B20000u: // snv -> Latn
+        case 0xD9B20000u: // snw -> Latn
+        case 0xDDB20000u: // snx -> Latn
+        case 0xE1B20000u: // sny -> Latn
+        case 0xE5B20000u: // snz -> Latn
+        case 0x736F0000u: // so -> Latn
+        case 0x85D20000u: // sob -> Latn
+        case 0x89D20000u: // soc -> Latn
+        case 0x8DD20000u: // sod -> Latn
+        case 0x91D20000u: // soe -> Latn
+        case 0xA9D20000u: // sok -> Latn
+        case 0xADD20000u: // sol -> Latn
+        case 0xB9D20000u: // soo -> Latn
+        case 0xBDD20000u: // sop -> Latn
+        case 0xC1D20000u: // soq -> Latn
+        case 0xC5D20000u: // sor -> Latn
+        case 0xC9D20000u: // sos -> Latn
+        case 0xD5D20000u: // sov -> Latn
+        case 0xD9D20000u: // sow -> Latn
+        case 0xDDD20000u: // sox -> Latn
+        case 0xE1D20000u: // soy -> Latn
+        case 0xE5D20000u: // soz -> Latn
+        case 0x85F20000u: // spb -> Latn
+        case 0x89F20000u: // spc -> Latn
+        case 0x8DF20000u: // spd -> Latn
+        case 0x91F20000u: // spe -> Latn
+        case 0x99F20000u: // spg -> Latn
+        case 0xA1F20000u: // spi -> Latn
+        case 0xA9F20000u: // spk -> Latn
+        case 0xADF20000u: // spl -> Latn
+        case 0xB1F20000u: // spm -> Latn
+        case 0xB5F20000u: // spn -> Latn
+        case 0xB9F20000u: // spo -> Latn
+        case 0xBDF20000u: // spp -> Latn
+        case 0xC1F20000u: // spq -> Latn
+        case 0xC5F20000u: // spr -> Latn
+        case 0xC9F20000u: // sps -> Latn
+        case 0x73710000u: // sq -> Latn
+        case 0x82120000u: // sqa -> Latn
+        case 0x9E120000u: // sqh -> Latn
+        case 0xB2120000u: // sqm -> Latn
+        case 0xD2120000u: // squ -> Latn
+        case 0x73724D45u: // sr-ME -> Latn
+        case 0x7372524Fu: // sr-RO -> Latn
+        case 0x73725255u: // sr-RU -> Latn
+        case 0x73725452u: // sr-TR -> Latn
+        case 0x82320000u: // sra -> Latn
+        case 0x92320000u: // sre -> Latn
+        case 0x96320000u: // srf -> Latn
+        case 0x9A320000u: // srg -> Latn
+        case 0xA2320000u: // sri -> Latn
+        case 0xAA320000u: // srk -> Latn
+        case 0xAE320000u: // srl -> Latn
+        case 0xB2320000u: // srm -> Latn
+        case 0xB6320000u: // srn -> Latn
+        case 0xBA320000u: // sro -> Latn
+        case 0xC2320000u: // srq -> Latn
+        case 0xC6320000u: // srr -> Latn
+        case 0xCA320000u: // srs -> Latn
+        case 0xCE320000u: // srt -> Latn
+        case 0xD2320000u: // sru -> Latn
+        case 0xD6320000u: // srv -> Latn
+        case 0xDA320000u: // srw -> Latn
+        case 0xE2320000u: // sry -> Latn
+        case 0x73730000u: // ss -> Latn
+        case 0x86520000u: // ssb -> Latn
+        case 0x8A520000u: // ssc -> Latn
+        case 0x8E520000u: // ssd -> Latn
+        case 0x92520000u: // sse -> Latn
+        case 0x96520000u: // ssf -> Latn
+        case 0x9A520000u: // ssg -> Latn
+        case 0xA6520000u: // ssj -> Latn
+        case 0xAE520000u: // ssl -> Latn
+        case 0xB2520000u: // ssm -> Latn
+        case 0xB6520000u: // ssn -> Latn
+        case 0xBA520000u: // sso -> Latn
+        case 0xC2520000u: // ssq -> Latn
+        case 0xCE520000u: // sst -> Latn
+        case 0xD2520000u: // ssu -> Latn
+        case 0xD6520000u: // ssv -> Latn
+        case 0xDE520000u: // ssx -> Latn
+        case 0xE2520000u: // ssy -> Latn
+        case 0xE6520000u: // ssz -> Latn
+        case 0x73740000u: // st -> Latn
+        case 0x82720000u: // sta -> Latn
+        case 0x86720000u: // stb -> Latn
+        case 0x92720000u: // ste -> Latn
+        case 0x96720000u: // stf -> Latn
+        case 0x9A720000u: // stg -> Latn
+        case 0x9E720000u: // sth -> Latn
+        case 0xA2720000u: // sti -> Latn
+        case 0xA6720000u: // stj -> Latn
+        case 0xAA720000u: // stk -> Latn
+        case 0xAE720000u: // stl -> Latn
+        case 0xB2720000u: // stm -> Latn
+        case 0xB6720000u: // stn -> Latn
+        case 0xBA720000u: // sto -> Latn
+        case 0xBE720000u: // stp -> Latn
+        case 0xC2720000u: // stq -> Latn
+        case 0xC6720000u: // str -> Latn
+        case 0xCE720000u: // stt -> Latn
+        case 0xDA720000u: // stw -> Latn
+        case 0x73750000u: // su -> Latn
+        case 0x82920000u: // sua -> Latn
+        case 0x86920000u: // sub -> Latn
+        case 0x8A920000u: // suc -> Latn
+        case 0x92920000u: // sue -> Latn
+        case 0x9A920000u: // sug -> Latn
+        case 0xA2920000u: // sui -> Latn
+        case 0xA6920000u: // suj -> Latn
+        case 0xAA920000u: // suk -> Latn
+        case 0xBA920000u: // suo -> Latn
+        case 0xC2920000u: // suq -> Latn
+        case 0xC6920000u: // sur -> Latn
+        case 0xCA920000u: // sus -> Latn
+        case 0xCE920000u: // sut -> Latn
+        case 0xD6920000u: // suv -> Latn
+        case 0xDA920000u: // suw -> Latn
+        case 0xE2920000u: // suy -> Latn
+        case 0x73760000u: // sv -> Latn
+        case 0x86B20000u: // svb -> Latn
+        case 0x8AB20000u: // svc -> Latn
+        case 0x92B20000u: // sve -> Latn
+        case 0xB2B20000u: // svm -> Latn
+        case 0xCAB20000u: // svs -> Latn
+        case 0x73770000u: // sw -> Latn
+        case 0x96D20000u: // swf -> Latn
+        case 0x9AD20000u: // swg -> Latn
+        case 0xA6D20000u: // swj -> Latn
+        case 0xAAD20000u: // swk -> Latn
+        case 0xB2D20000u: // swm -> Latn
+        case 0xBAD20000u: // swo -> Latn
+        case 0xBED20000u: // swp -> Latn
+        case 0xC2D20000u: // swq -> Latn
+        case 0xC6D20000u: // swr -> Latn
+        case 0xCAD20000u: // sws -> Latn
+        case 0xCED20000u: // swt -> Latn
+        case 0xD2D20000u: // swu -> Latn
+        case 0xDAD20000u: // sww -> Latn
+        case 0xDED20000u: // swx -> Latn
+        case 0xE2D20000u: // swy -> Latn
+        case 0x86F20000u: // sxb -> Latn
+        case 0x92F20000u: // sxe -> Latn
+        case 0xB6F20000u: // sxn -> Latn
+        case 0xC6F20000u: // sxr -> Latn
+        case 0xCAF20000u: // sxs -> Latn
+        case 0xDAF20000u: // sxw -> Latn
+        case 0x83120000u: // sya -> Latn
+        case 0x87120000u: // syb -> Latn
+        case 0xA3120000u: // syi -> Latn
+        case 0xAB120000u: // syk -> Latn
+        case 0xB3120000u: // sym -> Latn
+        case 0xBB120000u: // syo -> Latn
+        case 0xCB120000u: // sys -> Latn
+        case 0xDF120000u: // syx -> Latn
+        case 0x83320000u: // sza -> Latn
+        case 0x87320000u: // szb -> Latn
+        case 0x8B320000u: // szc -> Latn
+        case 0x9B320000u: // szg -> Latn
+        case 0xAF320000u: // szl -> Latn
+        case 0xB7320000u: // szn -> Latn
+        case 0xBF320000u: // szp -> Latn
+        case 0xD7320000u: // szv -> Latn
+        case 0xDB320000u: // szw -> Latn
+        case 0xE3320000u: // szy -> Latn
+        case 0x80130000u: // taa -> Latn
+        case 0x88130000u: // tac -> Latn
+        case 0x8C130000u: // tad -> Latn
+        case 0x90130000u: // tae -> Latn
+        case 0x94130000u: // taf -> Latn
+        case 0x98130000u: // tag -> Latn
+        case 0xA8130000u: // tak -> Latn
+        case 0xAC130000u: // tal -> Latn
+        case 0xB4130000u: // tan -> Latn
+        case 0xB8130000u: // tao -> Latn
+        case 0xBC130000u: // tap -> Latn
+        case 0xC0130000u: // taq -> Latn
+        case 0xC4130000u: // tar -> Latn
+        case 0xC8130000u: // tas -> Latn
+        case 0xD0130000u: // tau -> Latn
+        case 0xD4130000u: // tav -> Latn
+        case 0xD8130000u: // taw -> Latn
+        case 0xDC130000u: // tax -> Latn
+        case 0xE0130000u: // tay -> Latn
+        case 0xE4130000u: // taz -> Latn
+        case 0x80330000u: // tba -> Latn
+        case 0x88330000u: // tbc -> Latn
+        case 0x8C330000u: // tbd -> Latn
+        case 0x90330000u: // tbe -> Latn
+        case 0x94330000u: // tbf -> Latn
+        case 0x98330000u: // tbg -> Latn
+        case 0x9C330000u: // tbh -> Latn
+        case 0xA0330000u: // tbi -> Latn
+        case 0xA4330000u: // tbj -> Latn
+        case 0xAC330000u: // tbl -> Latn
+        case 0xB0330000u: // tbm -> Latn
+        case 0xB4330000u: // tbn -> Latn
+        case 0xB8330000u: // tbo -> Latn
+        case 0xBC330000u: // tbp -> Latn
+        case 0xC8330000u: // tbs -> Latn
+        case 0xCC330000u: // tbt -> Latn
+        case 0xD0330000u: // tbu -> Latn
+        case 0xD4330000u: // tbv -> Latn
+        case 0xD8330000u: // tbw -> Latn
+        case 0xDC330000u: // tbx -> Latn
+        case 0xE0330000u: // tby -> Latn
+        case 0xE4330000u: // tbz -> Latn
+        case 0x80530000u: // tca -> Latn
+        case 0x84530000u: // tcb -> Latn
+        case 0x88530000u: // tcc -> Latn
+        case 0x8C530000u: // tcd -> Latn
+        case 0x90530000u: // tce -> Latn
+        case 0x94530000u: // tcf -> Latn
+        case 0x98530000u: // tcg -> Latn
+        case 0x9C530000u: // tch -> Latn
+        case 0xA0530000u: // tci -> Latn
+        case 0xA8530000u: // tck -> Latn
+        case 0xB0530000u: // tcm -> Latn
+        case 0xBC530000u: // tcp -> Latn
+        case 0xC0530000u: // tcq -> Latn
+        case 0xC8530000u: // tcs -> Latn
+        case 0xD0530000u: // tcu -> Latn
+        case 0xD8530000u: // tcw -> Latn
+        case 0xE4530000u: // tcz -> Latn
+        case 0x88730000u: // tdc -> Latn
+        case 0x90730000u: // tde -> Latn
+        case 0xA0730000u: // tdi -> Latn
+        case 0xA4730000u: // tdj -> Latn
+        case 0xA8730000u: // tdk -> Latn
+        case 0xAC730000u: // tdl -> Latn
+        case 0xB0730000u: // tdm -> Latn
+        case 0xB4730000u: // tdn -> Latn
+        case 0xB8730000u: // tdo -> Latn
+        case 0xC0730000u: // tdq -> Latn
+        case 0xC4730000u: // tdr -> Latn
+        case 0xC8730000u: // tds -> Latn
+        case 0xCC730000u: // tdt -> Latn
+        case 0xD4730000u: // tdv -> Latn
+        case 0xDC730000u: // tdx -> Latn
+        case 0xE0730000u: // tdy -> Latn
+        case 0x80930000u: // tea -> Latn
+        case 0x84930000u: // teb -> Latn
+        case 0x88930000u: // tec -> Latn
+        case 0x8C930000u: // ted -> Latn
+        case 0x90930000u: // tee -> Latn
+        case 0x98930000u: // teg -> Latn
+        case 0x9C930000u: // teh -> Latn
+        case 0xA0930000u: // tei -> Latn
+        case 0xA8930000u: // tek -> Latn
+        case 0xB0930000u: // tem -> Latn
+        case 0xB4930000u: // ten -> Latn
+        case 0xB8930000u: // teo -> Latn
+        case 0xBC930000u: // tep -> Latn
+        case 0xC0930000u: // teq -> Latn
+        case 0xC4930000u: // ter -> Latn
+        case 0xCC930000u: // tet -> Latn
+        case 0xD0930000u: // teu -> Latn
+        case 0xD4930000u: // tev -> Latn
+        case 0xD8930000u: // tew -> Latn
+        case 0xDC930000u: // tex -> Latn
+        case 0xE0930000u: // tey -> Latn
+        case 0xE4930000u: // tez -> Latn
+        case 0xA0B30000u: // tfi -> Latn
+        case 0xB4B30000u: // tfn -> Latn
+        case 0xB8B30000u: // tfo -> Latn
+        case 0xC4B30000u: // tfr -> Latn
+        case 0xCCB30000u: // tft -> Latn
+        case 0x80D30000u: // tga -> Latn
+        case 0x84D30000u: // tgb -> Latn
+        case 0x88D30000u: // tgc -> Latn
+        case 0x8CD30000u: // tgd -> Latn
+        case 0x9CD30000u: // tgh -> Latn
+        case 0xA0D30000u: // tgi -> Latn
+        case 0xA4D30000u: // tgj -> Latn
+        case 0xB4D30000u: // tgn -> Latn
+        case 0xB8D30000u: // tgo -> Latn
+        case 0xBCD30000u: // tgp -> Latn
+        case 0xC0D30000u: // tgq -> Latn
+        case 0xC8D30000u: // tgs -> Latn
+        case 0xCCD30000u: // tgt -> Latn
+        case 0xD0D30000u: // tgu -> Latn
+        case 0xD4D30000u: // tgv -> Latn
+        case 0xD8D30000u: // tgw -> Latn
+        case 0xDCD30000u: // tgx -> Latn
+        case 0xE0D30000u: // tgy -> Latn
+        case 0xE4D30000u: // tgz -> Latn
+        case 0x8CF30000u: // thd -> Latn
+        case 0x9CF30000u: // thh -> Latn
+        case 0xA8F30000u: // thk -> Latn
+        case 0xBCF30000u: // thp -> Latn
+        case 0xCCF30000u: // tht -> Latn
+        case 0xD0F30000u: // thu -> Latn
+        case 0xD4F30000u: // thv -> Latn
+        case 0xE0F30000u: // thy -> Latn
+        case 0xE4F30000u: // thz -> Latn
+        case 0x89130000u: // tic -> Latn
+        case 0x95130000u: // tif -> Latn
+        case 0x9D130000u: // tih -> Latn
+        case 0xA1130000u: // tii -> Latn
+        case 0xA9130000u: // tik -> Latn
+        case 0xAD130000u: // til -> Latn
+        case 0xB1130000u: // tim -> Latn
+        case 0xB9130000u: // tio -> Latn
+        case 0xBD130000u: // tip -> Latn
+        case 0xC1130000u: // tiq -> Latn
+        case 0xC9130000u: // tis -> Latn
+        case 0xCD130000u: // tit -> Latn
+        case 0xD1130000u: // tiu -> Latn
+        case 0xD5130000u: // tiv -> Latn
+        case 0xD9130000u: // tiw -> Latn
+        case 0xDD130000u: // tix -> Latn
+        case 0xE1130000u: // tiy -> Latn
+        case 0x81330000u: // tja -> Latn
+        case 0x99330000u: // tjg -> Latn
+        case 0xA1330000u: // tji -> Latn
+        case 0xA5330000u: // tjj -> Latn
+        case 0xB5330000u: // tjn -> Latn
+        case 0xBD330000u: // tjp -> Latn
+        case 0xC9330000u: // tjs -> Latn
+        case 0xD1330000u: // tju -> Latn
+        case 0xD9330000u: // tjw -> Latn
+        case 0x746B0000u: // tk -> Latn
+        case 0x81530000u: // tka -> Latn
+        case 0x8D530000u: // tkd -> Latn
+        case 0x91530000u: // tke -> Latn
+        case 0x95530000u: // tkf -> Latn
+        case 0x99530000u: // tkg -> Latn
+        case 0xAD530000u: // tkl -> Latn
+        case 0xBD530000u: // tkp -> Latn
+        case 0xC1530000u: // tkq -> Latn
+        case 0xC5530000u: // tkr -> Latn
+        case 0xD1530000u: // tku -> Latn
+        case 0xD5530000u: // tkv -> Latn
+        case 0xD9530000u: // tkw -> Latn
+        case 0xDD530000u: // tkx -> Latn
+        case 0xE5530000u: // tkz -> Latn
+        case 0x746C0000u: // tl -> Latn
+        case 0x81730000u: // tla -> Latn
+        case 0x85730000u: // tlb -> Latn
+        case 0x89730000u: // tlc -> Latn
+        case 0x8D730000u: // tld -> Latn
+        case 0x95730000u: // tlf -> Latn
+        case 0x99730000u: // tlg -> Latn
+        case 0xA1730000u: // tli -> Latn
+        case 0xA5730000u: // tlj -> Latn
+        case 0xA9730000u: // tlk -> Latn
+        case 0xAD730000u: // tll -> Latn
+        case 0xB1730000u: // tlm -> Latn
+        case 0xB5730000u: // tln -> Latn
+        case 0xBD730000u: // tlp -> Latn
+        case 0xC1730000u: // tlq -> Latn
+        case 0xC5730000u: // tlr -> Latn
+        case 0xC9730000u: // tls -> Latn
+        case 0xCD730000u: // tlt -> Latn
+        case 0xD1730000u: // tlu -> Latn
+        case 0xD5730000u: // tlv -> Latn
+        case 0xDD730000u: // tlx -> Latn
+        case 0xE1730000u: // tly -> Latn
+        case 0x81930000u: // tma -> Latn
+        case 0x85930000u: // tmb -> Latn
+        case 0x89930000u: // tmc -> Latn
+        case 0x8D930000u: // tmd -> Latn
+        case 0x91930000u: // tme -> Latn
+        case 0x95930000u: // tmf -> Latn
+        case 0x99930000u: // tmg -> Latn
+        case 0x9D930000u: // tmh -> Latn
+        case 0xA1930000u: // tmi -> Latn
+        case 0xA5930000u: // tmj -> Latn
+        case 0xAD930000u: // tml -> Latn
+        case 0xB1930000u: // tmm -> Latn
+        case 0xB5930000u: // tmn -> Latn
+        case 0xB9930000u: // tmo -> Latn
+        case 0xC1930000u: // tmq -> Latn
+        case 0xCD930000u: // tmt -> Latn
+        case 0xD1930000u: // tmu -> Latn
+        case 0xD5930000u: // tmv -> Latn
+        case 0xD9930000u: // tmw -> Latn
+        case 0xE1930000u: // tmy -> Latn
+        case 0xE5930000u: // tmz -> Latn
+        case 0x746E0000u: // tn -> Latn
+        case 0x81B30000u: // tna -> Latn
+        case 0x85B30000u: // tnb -> Latn
+        case 0x89B30000u: // tnc -> Latn
+        case 0x8DB30000u: // tnd -> Latn
+        case 0x99B30000u: // tng -> Latn
+        case 0x9DB30000u: // tnh -> Latn
+        case 0xA1B30000u: // tni -> Latn
+        case 0xA9B30000u: // tnk -> Latn
+        case 0xADB30000u: // tnl -> Latn
+        case 0xB1B30000u: // tnm -> Latn
+        case 0xB5B30000u: // tnn -> Latn
+        case 0xB9B30000u: // tno -> Latn
+        case 0xBDB30000u: // tnp -> Latn
+        case 0xC1B30000u: // tnq -> Latn
+        case 0xC5B30000u: // tnr -> Latn
+        case 0xC9B30000u: // tns -> Latn
+        case 0xCDB30000u: // tnt -> Latn
+        case 0xD9B30000u: // tnw -> Latn
+        case 0xDDB30000u: // tnx -> Latn
+        case 0xE1B30000u: // tny -> Latn
+        case 0x746F0000u: // to -> Latn
+        case 0x85D30000u: // tob -> Latn
+        case 0x89D30000u: // toc -> Latn
+        case 0x8DD30000u: // tod -> Latn
+        case 0x95D30000u: // tof -> Latn
+        case 0x99D30000u: // tog -> Latn
+        case 0x9DD30000u: // toh -> Latn
+        case 0xA1D30000u: // toi -> Latn
+        case 0xA5D30000u: // toj -> Latn
+        case 0xA9D30000u: // tok -> Latn
+        case 0xADD30000u: // tol -> Latn
+        case 0xB1D30000u: // tom -> Latn
+        case 0xB9D30000u: // too -> Latn
+        case 0xBDD30000u: // top -> Latn
+        case 0xC1D30000u: // toq -> Latn
+        case 0xC5D30000u: // tor -> Latn
+        case 0xC9D30000u: // tos -> Latn
+        case 0xD1D30000u: // tou -> Latn
+        case 0xD9D30000u: // tow -> Latn
+        case 0xDDD30000u: // tox -> Latn
+        case 0xE1D30000u: // toy -> Latn
+        case 0xE5D30000u: // toz -> Latn
+        case 0x81F30000u: // tpa -> Latn
+        case 0x89F30000u: // tpc -> Latn
+        case 0x91F30000u: // tpe -> Latn
+        case 0x95F30000u: // tpf -> Latn
+        case 0x99F30000u: // tpg -> Latn
+        case 0xA1F30000u: // tpi -> Latn
+        case 0xA5F30000u: // tpj -> Latn
+        case 0xA9F30000u: // tpk -> Latn
+        case 0xADF30000u: // tpl -> Latn
+        case 0xB1F30000u: // tpm -> Latn
+        case 0xB5F30000u: // tpn -> Latn
+        case 0xBDF30000u: // tpp -> Latn
+        case 0xC5F30000u: // tpr -> Latn
+        case 0xCDF30000u: // tpt -> Latn
+        case 0xD5F30000u: // tpv -> Latn
+        case 0xDDF30000u: // tpx -> Latn
+        case 0xE1F30000u: // tpy -> Latn
+        case 0xE5F30000u: // tpz -> Latn
+        case 0x86130000u: // tqb -> Latn
+        case 0xAE130000u: // tql -> Latn
+        case 0xB2130000u: // tqm -> Latn
+        case 0xB6130000u: // tqn -> Latn
+        case 0xBA130000u: // tqo -> Latn
+        case 0xBE130000u: // tqp -> Latn
+        case 0xCE130000u: // tqt -> Latn
+        case 0xD2130000u: // tqu -> Latn
+        case 0xDA130000u: // tqw -> Latn
+        case 0x74720000u: // tr -> Latn
+        case 0x86330000u: // trb -> Latn
+        case 0x8A330000u: // trc -> Latn
+        case 0x92330000u: // tre -> Latn
+        case 0x96330000u: // trf -> Latn
+        case 0x9E330000u: // trh -> Latn
+        case 0xA2330000u: // tri -> Latn
+        case 0xA6330000u: // trj -> Latn
+        case 0xAE330000u: // trl -> Latn
+        case 0xB6330000u: // trn -> Latn
+        case 0xBA330000u: // tro -> Latn
+        case 0xBE330000u: // trp -> Latn
+        case 0xC2330000u: // trq -> Latn
+        case 0xC6330000u: // trr -> Latn
+        case 0xCA330000u: // trs -> Latn
+        case 0xCE330000u: // trt -> Latn
+        case 0xD2330000u: // tru -> Latn
+        case 0xD6330000u: // trv -> Latn
+        case 0xDE330000u: // trx -> Latn
+        case 0xE2330000u: // try -> Latn
+        case 0xE6330000u: // trz -> Latn
+        case 0x74730000u: // ts -> Latn
+        case 0x82530000u: // tsa -> Latn
+        case 0x86530000u: // tsb -> Latn
+        case 0x8A530000u: // tsc -> Latn
+        case 0x9A530000u: // tsg -> Latn
+        case 0x9E530000u: // tsh -> Latn
+        case 0xA2530000u: // tsi -> Latn
+        case 0xAE530000u: // tsl -> Latn
+        case 0xBE530000u: // tsp -> Latn
+        case 0xC6530000u: // tsr -> Latn
+        case 0xCE530000u: // tst -> Latn
+        case 0xD2530000u: // tsu -> Latn
+        case 0xD6530000u: // tsv -> Latn
+        case 0xDA530000u: // tsw -> Latn
+        case 0xDE530000u: // tsx -> Latn
+        case 0xE6530000u: // tsz -> Latn
+        case 0x86730000u: // ttb -> Latn
+        case 0x8A730000u: // ttc -> Latn
+        case 0x8E730000u: // ttd -> Latn
+        case 0x92730000u: // tte -> Latn
+        case 0x96730000u: // ttf -> Latn
+        case 0xA2730000u: // tti -> Latn
+        case 0xA6730000u: // ttj -> Latn
+        case 0xAA730000u: // ttk -> Latn
+        case 0xAE730000u: // ttl -> Latn
+        case 0xB2730000u: // ttm -> Latn
+        case 0xB6730000u: // ttn -> Latn
+        case 0xBE730000u: // ttp -> Latn
+        case 0xC6730000u: // ttr -> Latn
+        case 0xCE730000u: // ttt -> Latn
+        case 0xD2730000u: // ttu -> Latn
+        case 0xD6730000u: // ttv -> Latn
+        case 0xDA730000u: // ttw -> Latn
+        case 0xE2730000u: // tty -> Latn
+        case 0x82930000u: // tua -> Latn
+        case 0x86930000u: // tub -> Latn
+        case 0x8A930000u: // tuc -> Latn
+        case 0x8E930000u: // tud -> Latn
+        case 0x92930000u: // tue -> Latn
+        case 0x96930000u: // tuf -> Latn
+        case 0x9A930000u: // tug -> Latn
+        case 0x9E930000u: // tuh -> Latn
+        case 0xA2930000u: // tui -> Latn
+        case 0xA6930000u: // tuj -> Latn
+        case 0xAE930000u: // tul -> Latn
+        case 0xB2930000u: // tum -> Latn
+        case 0xB6930000u: // tun -> Latn
+        case 0xBA930000u: // tuo -> Latn
+        case 0xC2930000u: // tuq -> Latn
+        case 0xCA930000u: // tus -> Latn
+        case 0xD2930000u: // tuu -> Latn
+        case 0xD6930000u: // tuv -> Latn
+        case 0xDE930000u: // tux -> Latn
+        case 0xE2930000u: // tuy -> Latn
+        case 0xE6930000u: // tuz -> Latn
+        case 0x82B30000u: // tva -> Latn
+        case 0x8EB30000u: // tvd -> Latn
+        case 0x92B30000u: // tve -> Latn
+        case 0xA2B30000u: // tvi -> Latn
+        case 0xAAB30000u: // tvk -> Latn
+        case 0xAEB30000u: // tvl -> Latn
+        case 0xB2B30000u: // tvm -> Latn
+        case 0xBAB30000u: // tvo -> Latn
+        case 0xCAB30000u: // tvs -> Latn
+        case 0xCEB30000u: // tvt -> Latn
+        case 0xD2B30000u: // tvu -> Latn
+        case 0xDAB30000u: // tvw -> Latn
+        case 0xDEB30000u: // tvx -> Latn
+        case 0x82D30000u: // twa -> Latn
+        case 0x86D30000u: // twb -> Latn
+        case 0x8ED30000u: // twd -> Latn
+        case 0x92D30000u: // twe -> Latn
+        case 0x96D30000u: // twf -> Latn
+        case 0x9AD30000u: // twg -> Latn
+        case 0x9ED30000u: // twh -> Latn
+        case 0xAED30000u: // twl -> Latn
+        case 0xB6D30000u: // twn -> Latn
+        case 0xBAD30000u: // two -> Latn
+        case 0xBED30000u: // twp -> Latn
+        case 0xC2D30000u: // twq -> Latn
+        case 0xC6D30000u: // twr -> Latn
+        case 0xCED30000u: // twt -> Latn
+        case 0xD2D30000u: // twu -> Latn
+        case 0xDAD30000u: // tww -> Latn
+        case 0xDED30000u: // twx -> Latn
+        case 0xE2D30000u: // twy -> Latn
+        case 0x82F30000u: // txa -> Latn
+        case 0x92F30000u: // txe -> Latn
+        case 0xA2F30000u: // txi -> Latn
+        case 0xA6F30000u: // txj -> Latn
+        case 0xB2F30000u: // txm -> Latn
+        case 0xB6F30000u: // txn -> Latn
+        case 0xC2F30000u: // txq -> Latn
+        case 0xCAF30000u: // txs -> Latn
+        case 0xCEF30000u: // txt -> Latn
+        case 0xD2F30000u: // txu -> Latn
+        case 0xDEF30000u: // txx -> Latn
+        case 0xE2F30000u: // txy -> Latn
+        case 0x74790000u: // ty -> Latn
+        case 0x83130000u: // tya -> Latn
+        case 0x93130000u: // tye -> Latn
+        case 0x9F130000u: // tyh -> Latn
+        case 0xA3130000u: // tyi -> Latn
+        case 0xA7130000u: // tyj -> Latn
+        case 0xAF130000u: // tyl -> Latn
+        case 0xB7130000u: // tyn -> Latn
+        case 0xBF130000u: // typ -> Latn
+        case 0xCB130000u: // tys -> Latn
+        case 0xCF130000u: // tyt -> Latn
+        case 0xD3130000u: // tyu -> Latn
+        case 0xDF130000u: // tyx -> Latn
+        case 0xE3130000u: // tyy -> Latn
+        case 0xE7130000u: // tyz -> Latn
+        case 0x9F330000u: // tzh -> Latn
+        case 0xA7330000u: // tzj -> Latn
+        case 0xAF330000u: // tzl -> Latn
+        case 0xB3330000u: // tzm -> Latn
+        case 0xB7330000u: // tzn -> Latn
+        case 0xBB330000u: // tzo -> Latn
+        case 0xDF330000u: // tzx -> Latn
+        case 0xB0140000u: // uam -> Latn
+        case 0xC4140000u: // uar -> Latn
+        case 0x80340000u: // uba -> Latn
+        case 0xA0340000u: // ubi -> Latn
+        case 0xAC340000u: // ubl -> Latn
+        case 0xC4340000u: // ubr -> Latn
+        case 0xD0340000u: // ubu -> Latn
+        case 0xE0340000u: // uby -> Latn
+        case 0x80740000u: // uda -> Latn
+        case 0xA4740000u: // udj -> Latn
+        case 0xAC740000u: // udl -> Latn
+        case 0xD0740000u: // udu -> Latn
+        case 0xC8940000u: // ues -> Latn
+        case 0xA0B40000u: // ufi -> Latn
+        case 0x84D40000u: // ugb -> Latn
+        case 0x90D40000u: // uge -> Latn
+        case 0x80F40000u: // uha -> Latn
+        case 0xB4F40000u: // uhn -> Latn
+        case 0xC9140000u: // uis -> Latn
+        case 0xD5140000u: // uiv -> Latn
+        case 0xA1340000u: // uji -> Latn
+        case 0x81540000u: // uka -> Latn
+        case 0x99540000u: // ukg -> Latn
+        case 0x9D540000u: // ukh -> Latn
+        case 0xA9540000u: // ukk -> Latn
+        case 0xBD540000u: // ukp -> Latn
+        case 0xC1540000u: // ukq -> Latn
+        case 0xD1540000u: // uku -> Latn
+        case 0xD5540000u: // ukv -> Latn
+        case 0xD9540000u: // ukw -> Latn
+        case 0xE1540000u: // uky -> Latn
+        case 0x81740000u: // ula -> Latn
+        case 0x85740000u: // ulb -> Latn
+        case 0x91740000u: // ule -> Latn
+        case 0x95740000u: // ulf -> Latn
+        case 0xA1740000u: // uli -> Latn
+        case 0xA9740000u: // ulk -> Latn
+        case 0xB1740000u: // ulm -> Latn
+        case 0xB5740000u: // uln -> Latn
+        case 0xD1740000u: // ulu -> Latn
+        case 0xD9740000u: // ulw -> Latn
+        case 0xE1740000u: // uly -> Latn
+        case 0x81940000u: // uma -> Latn
+        case 0x85940000u: // umb -> Latn
+        case 0x8D940000u: // umd -> Latn
+        case 0x99940000u: // umg -> Latn
+        case 0xA1940000u: // umi -> Latn
+        case 0xB1940000u: // umm -> Latn
+        case 0xB5940000u: // umn -> Latn
+        case 0xB9940000u: // umo -> Latn
+        case 0xBD940000u: // ump -> Latn
+        case 0xC5940000u: // umr -> Latn
+        case 0xC9940000u: // ums -> Latn
+        case 0x81B40000u: // una -> Latn
+        case 0x91B40000u: // une -> Latn
+        case 0x99B40000u: // ung -> Latn
+        case 0xA1B40000u: // uni -> Latn
+        case 0xA9B40000u: // unk -> Latn
+        case 0xB1B40000u: // unm -> Latn
+        case 0xB5B40000u: // unn -> Latn
+        case 0xD1B40000u: // unu -> Latn
+        case 0xE5B40000u: // unz -> Latn
+        case 0xB5D40000u: // uon -> Latn
+        case 0xA1F40000u: // upi -> Latn
+        case 0xD5F40000u: // upv -> Latn
+        case 0x82340000u: // ura -> Latn
+        case 0x86340000u: // urb -> Latn
+        case 0x8A340000u: // urc -> Latn
+        case 0x92340000u: // ure -> Latn
+        case 0x96340000u: // urf -> Latn
+        case 0x9A340000u: // urg -> Latn
+        case 0x9E340000u: // urh -> Latn
+        case 0xA2340000u: // uri -> Latn
+        case 0xB2340000u: // urm -> Latn
+        case 0xB6340000u: // urn -> Latn
+        case 0xBA340000u: // uro -> Latn
+        case 0xBE340000u: // urp -> Latn
+        case 0xC6340000u: // urr -> Latn
+        case 0xCE340000u: // urt -> Latn
+        case 0xD2340000u: // uru -> Latn
+        case 0xD6340000u: // urv -> Latn
+        case 0xDA340000u: // urw -> Latn
+        case 0xDE340000u: // urx -> Latn
+        case 0xE2340000u: // ury -> Latn
+        case 0xE6340000u: // urz -> Latn
+        case 0x82540000u: // usa -> Latn
+        case 0xA2540000u: // usi -> Latn
+        case 0xAA540000u: // usk -> Latn
+        case 0xBE540000u: // usp -> Latn
+        case 0xCA540000u: // uss -> Latn
+        case 0xD2540000u: // usu -> Latn
+        case 0x82740000u: // uta -> Latn
+        case 0x92740000u: // ute -> Latn
+        case 0x9E740000u: // uth -> Latn
+        case 0xBE740000u: // utp -> Latn
+        case 0xC6740000u: // utr -> Latn
+        case 0xD2740000u: // utu -> Latn
+        case 0xC6940000u: // uur -> Latn
+        case 0x92B40000u: // uve -> Latn
+        case 0x9EB40000u: // uvh -> Latn
+        case 0xAEB40000u: // uvl -> Latn
+        case 0x82D40000u: // uwa -> Latn
+        case 0x83140000u: // uya -> Latn
+        case 0x757A0000u: // uz -> Latn
+        case 0x90150000u: // vae -> Latn
+        case 0x98150000u: // vag -> Latn
+        case 0xA4150000u: // vaj -> Latn
+        case 0xAC150000u: // val -> Latn
+        case 0xB0150000u: // vam -> Latn
+        case 0xB4150000u: // van -> Latn
+        case 0xB8150000u: // vao -> Latn
+        case 0xBC150000u: // vap -> Latn
+        case 0xC4150000u: // var -> Latn
+        case 0xD0150000u: // vau -> Latn
+        case 0x84350000u: // vbb -> Latn
+        case 0xA8350000u: // vbk -> Latn
+        case 0x76650000u: // ve -> Latn
+        case 0x88950000u: // vec -> Latn
+        case 0xB0950000u: // vem -> Latn
+        case 0xB8950000u: // veo -> Latn
+        case 0xBC950000u: // vep -> Latn
+        case 0xC4950000u: // ver -> Latn
+        case 0x76690000u: // vi -> Latn
+        case 0x89150000u: // vic -> Latn
+        case 0x8D150000u: // vid -> Latn
+        case 0x95150000u: // vif -> Latn
+        case 0x99150000u: // vig -> Latn
+        case 0xAD150000u: // vil -> Latn
+        case 0xB5150000u: // vin -> Latn
+        case 0xCD150000u: // vit -> Latn
+        case 0xD5150000u: // viv -> Latn
+        case 0x81550000u: // vka -> Latn
+        case 0xA5550000u: // vkj -> Latn
+        case 0xA9550000u: // vkk -> Latn
+        case 0xAD550000u: // vkl -> Latn
+        case 0xB1550000u: // vkm -> Latn
+        case 0xB5550000u: // vkn -> Latn
+        case 0xB9550000u: // vko -> Latn
+        case 0xBD550000u: // vkp -> Latn
+        case 0xCD550000u: // vkt -> Latn
+        case 0xD1550000u: // vku -> Latn
+        case 0xE5550000u: // vkz -> Latn
+        case 0xBD750000u: // vlp -> Latn
+        case 0xC9750000u: // vls -> Latn
+        case 0x81950000u: // vma -> Latn
+        case 0x85950000u: // vmb -> Latn
+        case 0x89950000u: // vmc -> Latn
+        case 0x91950000u: // vme -> Latn
+        case 0x95950000u: // vmf -> Latn
+        case 0x99950000u: // vmg -> Latn
+        case 0xA1950000u: // vmi -> Latn
+        case 0xA5950000u: // vmj -> Latn
+        case 0xA9950000u: // vmk -> Latn
+        case 0xAD950000u: // vml -> Latn
+        case 0xB1950000u: // vmm -> Latn
+        case 0xBD950000u: // vmp -> Latn
+        case 0xC1950000u: // vmq -> Latn
+        case 0xC5950000u: // vmr -> Latn
+        case 0xC9950000u: // vms -> Latn
+        case 0xD1950000u: // vmu -> Latn
+        case 0xD9950000u: // vmw -> Latn
+        case 0xDD950000u: // vmx -> Latn
+        case 0xE1950000u: // vmy -> Latn
+        case 0xE5950000u: // vmz -> Latn
+        case 0xA9B50000u: // vnk -> Latn
+        case 0xB1B50000u: // vnm -> Latn
+        case 0xBDB50000u: // vnp -> Latn
+        case 0x766F0000u: // vo -> Latn
+        case 0xC5D50000u: // vor -> Latn
+        case 0xCDD50000u: // vot -> Latn
+        case 0x82350000u: // vra -> Latn
+        case 0xBA350000u: // vro -> Latn
+        case 0xCA350000u: // vrs -> Latn
+        case 0xCE350000u: // vrt -> Latn
+        case 0xBA750000u: // vto -> Latn
+        case 0xB2950000u: // vum -> Latn
+        case 0xB6950000u: // vun -> Latn
+        case 0xCE950000u: // vut -> Latn
+        case 0x82D50000u: // vwa -> Latn
+        case 0x77610000u: // wa -> Latn
+        case 0x80160000u: // waa -> Latn
+        case 0x84160000u: // wab -> Latn
+        case 0x88160000u: // wac -> Latn
+        case 0x8C160000u: // wad -> Latn
+        case 0x90160000u: // wae -> Latn
+        case 0x94160000u: // waf -> Latn
+        case 0x98160000u: // wag -> Latn
+        case 0x9C160000u: // wah -> Latn
+        case 0xA0160000u: // wai -> Latn
+        case 0xA4160000u: // waj -> Latn
+        case 0xB0160000u: // wam -> Latn
+        case 0xB4160000u: // wan -> Latn
+        case 0xBC160000u: // wap -> Latn
+        case 0xC0160000u: // waq -> Latn
+        case 0xC4160000u: // war -> Latn
+        case 0xC8160000u: // was -> Latn
+        case 0xCC160000u: // wat -> Latn
+        case 0xD0160000u: // wau -> Latn
+        case 0xD4160000u: // wav -> Latn
+        case 0xD8160000u: // waw -> Latn
+        case 0xDC160000u: // wax -> Latn
+        case 0xE0160000u: // way -> Latn
+        case 0xE4160000u: // waz -> Latn
+        case 0x80360000u: // wba -> Latn
+        case 0x84360000u: // wbb -> Latn
+        case 0x90360000u: // wbe -> Latn
+        case 0x94360000u: // wbf -> Latn
+        case 0x9C360000u: // wbh -> Latn
+        case 0xA0360000u: // wbi -> Latn
+        case 0xA4360000u: // wbj -> Latn
+        case 0xAC360000u: // wbl -> Latn
+        case 0xB0360000u: // wbm -> Latn
+        case 0xBC360000u: // wbp -> Latn
+        case 0xCC360000u: // wbt -> Latn
+        case 0xD4360000u: // wbv -> Latn
+        case 0xD8360000u: // wbw -> Latn
+        case 0x80560000u: // wca -> Latn
+        case 0xA0560000u: // wci -> Latn
+        case 0x8C760000u: // wdd -> Latn
+        case 0x98760000u: // wdg -> Latn
+        case 0xA4760000u: // wdj -> Latn
+        case 0xA8760000u: // wdk -> Latn
+        case 0xCC760000u: // wdt -> Latn
+        case 0xD0760000u: // wdu -> Latn
+        case 0xE0760000u: // wdy -> Latn
+        case 0x88960000u: // wec -> Latn
+        case 0x8C960000u: // wed -> Latn
+        case 0x98960000u: // weg -> Latn
+        case 0x9C960000u: // weh -> Latn
+        case 0xA0960000u: // wei -> Latn
+        case 0xB0960000u: // wem -> Latn
+        case 0xB8960000u: // weo -> Latn
+        case 0xBC960000u: // wep -> Latn
+        case 0xC4960000u: // wer -> Latn
+        case 0xC8960000u: // wes -> Latn
+        case 0xCC960000u: // wet -> Latn
+        case 0xD0960000u: // weu -> Latn
+        case 0xD8960000u: // wew -> Latn
+        case 0x98B60000u: // wfg -> Latn
+        case 0x80D60000u: // wga -> Latn
+        case 0x84D60000u: // wgb -> Latn
+        case 0x98D60000u: // wgg -> Latn
+        case 0xA0D60000u: // wgi -> Latn
+        case 0xB8D60000u: // wgo -> Latn
+        case 0xD0D60000u: // wgu -> Latn
+        case 0xE0D60000u: // wgy -> Latn
+        case 0x80F60000u: // wha -> Latn
+        case 0x98F60000u: // whg -> Latn
+        case 0xA8F60000u: // whk -> Latn
+        case 0xD0F60000u: // whu -> Latn
+        case 0x85160000u: // wib -> Latn
+        case 0x89160000u: // wic -> Latn
+        case 0x91160000u: // wie -> Latn
+        case 0x95160000u: // wif -> Latn
+        case 0x99160000u: // wig -> Latn
+        case 0x9D160000u: // wih -> Latn
+        case 0xA1160000u: // wii -> Latn
+        case 0xA5160000u: // wij -> Latn
+        case 0xA9160000u: // wik -> Latn
+        case 0xAD160000u: // wil -> Latn
+        case 0xB1160000u: // wim -> Latn
+        case 0xB5160000u: // win -> Latn
+        case 0xC5160000u: // wir -> Latn
+        case 0xD1160000u: // wiu -> Latn
+        case 0xD5160000u: // wiv -> Latn
+        case 0xE1160000u: // wiy -> Latn
+        case 0x81360000u: // wja -> Latn
+        case 0xA1360000u: // wji -> Latn
+        case 0x81560000u: // wka -> Latn
+        case 0x8D560000u: // wkd -> Latn
+        case 0xC5560000u: // wkr -> Latn
+        case 0xD9560000u: // wkw -> Latn
+        case 0xE1560000u: // wky -> Latn
+        case 0x81760000u: // wla -> Latn
+        case 0x99760000u: // wlg -> Latn
+        case 0x9D760000u: // wlh -> Latn
+        case 0xA1760000u: // wli -> Latn
+        case 0xB1760000u: // wlm -> Latn
+        case 0xC5760000u: // wlr -> Latn
+        case 0xC9760000u: // wls -> Latn
+        case 0xD1760000u: // wlu -> Latn
+        case 0xD5760000u: // wlv -> Latn
+        case 0xD9760000u: // wlw -> Latn
+        case 0xDD760000u: // wlx -> Latn
+        case 0x81960000u: // wma -> Latn
+        case 0x85960000u: // wmb -> Latn
+        case 0x89960000u: // wmc -> Latn
+        case 0x8D960000u: // wmd -> Latn
+        case 0x9D960000u: // wmh -> Latn
+        case 0xA1960000u: // wmi -> Latn
+        case 0xB1960000u: // wmm -> Latn
+        case 0xB5960000u: // wmn -> Latn
+        case 0xB9960000u: // wmo -> Latn
+        case 0xC9960000u: // wms -> Latn
+        case 0xCD960000u: // wmt -> Latn
+        case 0xD9960000u: // wmw -> Latn
+        case 0xDD960000u: // wmx -> Latn
+        case 0x85B60000u: // wnb -> Latn
+        case 0x89B60000u: // wnc -> Latn
+        case 0x8DB60000u: // wnd -> Latn
+        case 0x99B60000u: // wng -> Latn
+        case 0xA9B60000u: // wnk -> Latn
+        case 0xB1B60000u: // wnm -> Latn
+        case 0xB5B60000u: // wnn -> Latn
+        case 0xB9B60000u: // wno -> Latn
+        case 0xBDB60000u: // wnp -> Latn
+        case 0xD1B60000u: // wnu -> Latn
+        case 0xD9B60000u: // wnw -> Latn
+        case 0xE1B60000u: // wny -> Latn
+        case 0x776F0000u: // wo -> Latn
+        case 0x81D60000u: // woa -> Latn
+        case 0x85D60000u: // wob -> Latn
+        case 0x89D60000u: // woc -> Latn
+        case 0x8DD60000u: // wod -> Latn
+        case 0x91D60000u: // woe -> Latn
+        case 0x95D60000u: // wof -> Latn
+        case 0x99D60000u: // wog -> Latn
+        case 0xA1D60000u: // woi -> Latn
+        case 0xA9D60000u: // wok -> Latn
+        case 0xB1D60000u: // wom -> Latn
+        case 0xB5D60000u: // won -> Latn
+        case 0xB9D60000u: // woo -> Latn
+        case 0xC5D60000u: // wor -> Latn
+        case 0xC9D60000u: // wos -> Latn
+        case 0xD9D60000u: // wow -> Latn
+        case 0x89F60000u: // wpc -> Latn
+        case 0x86360000u: // wrb -> Latn
+        case 0x9A360000u: // wrg -> Latn
+        case 0x9E360000u: // wrh -> Latn
+        case 0xA2360000u: // wri -> Latn
+        case 0xAA360000u: // wrk -> Latn
+        case 0xAE360000u: // wrl -> Latn
+        case 0xB2360000u: // wrm -> Latn
+        case 0xBA360000u: // wro -> Latn
+        case 0xBE360000u: // wrp -> Latn
+        case 0xC6360000u: // wrr -> Latn
+        case 0xCA360000u: // wrs -> Latn
+        case 0xD2360000u: // wru -> Latn
+        case 0xD6360000u: // wrv -> Latn
+        case 0xDA360000u: // wrw -> Latn
+        case 0xDE360000u: // wrx -> Latn
+        case 0xE6360000u: // wrz -> Latn
+        case 0x82560000u: // wsa -> Latn
+        case 0xA2560000u: // wsi -> Latn
+        case 0xAA560000u: // wsk -> Latn
+        case 0xC6560000u: // wsr -> Latn
+        case 0xCA560000u: // wss -> Latn
+        case 0xD2560000u: // wsu -> Latn
+        case 0x86760000u: // wtb -> Latn
+        case 0x96760000u: // wtf -> Latn
+        case 0x9E760000u: // wth -> Latn
+        case 0xA2760000u: // wti -> Latn
+        case 0xAA760000u: // wtk -> Latn
+        case 0xDA760000u: // wtw -> Latn
+        case 0x82960000u: // wua -> Latn
+        case 0x86960000u: // wub -> Latn
+        case 0x8E960000u: // wud -> Latn
+        case 0xAE960000u: // wul -> Latn
+        case 0xB2960000u: // wum -> Latn
+        case 0xB6960000u: // wun -> Latn
+        case 0xC6960000u: // wur -> Latn
+        case 0xCE960000u: // wut -> Latn
+        case 0xD6960000u: // wuv -> Latn
+        case 0xDE960000u: // wux -> Latn
+        case 0xE2960000u: // wuy -> Latn
+        case 0x82D60000u: // wwa -> Latn
+        case 0x86D60000u: // wwb -> Latn
+        case 0xBAD60000u: // wwo -> Latn
+        case 0xC6D60000u: // wwr -> Latn
+        case 0xDAD60000u: // www -> Latn
+        case 0xDAF60000u: // wxw -> Latn
+        case 0x87160000u: // wyb -> Latn
+        case 0xA3160000u: // wyi -> Latn
+        case 0xB3160000u: // wym -> Latn
+        case 0xB7160000u: // wyn -> Latn
+        case 0xC7160000u: // wyr -> Latn
+        case 0xE3160000u: // wyy -> Latn
+        case 0x80170000u: // xaa -> Latn
+        case 0x84170000u: // xab -> Latn
+        case 0xA0170000u: // xai -> Latn
+        case 0xA4170000u: // xaj -> Latn
+        case 0xA8170000u: // xak -> Latn
+        case 0xB0170000u: // xam -> Latn
+        case 0xB8170000u: // xao -> Latn
+        case 0xC4170000u: // xar -> Latn
+        case 0xCC170000u: // xat -> Latn
+        case 0xD0170000u: // xau -> Latn
+        case 0xD4170000u: // xav -> Latn
+        case 0xD8170000u: // xaw -> Latn
+        case 0xE0170000u: // xay -> Latn
+        case 0x84370000u: // xbb -> Latn
+        case 0x8C370000u: // xbd -> Latn
+        case 0x90370000u: // xbe -> Latn
+        case 0x98370000u: // xbg -> Latn
+        case 0xA0370000u: // xbi -> Latn
+        case 0xA4370000u: // xbj -> Latn
+        case 0xB0370000u: // xbm -> Latn
+        case 0xB4370000u: // xbn -> Latn
+        case 0xBC370000u: // xbp -> Latn
+        case 0xC4370000u: // xbr -> Latn
+        case 0xD8370000u: // xbw -> Latn
+        case 0xE0370000u: // xby -> Latn
+        case 0x9C570000u: // xch -> Latn
+        case 0x80770000u: // xda -> Latn
+        case 0xA8770000u: // xdk -> Latn
+        case 0xB8770000u: // xdo -> Latn
+        case 0xE0770000u: // xdy -> Latn
+        case 0x8C970000u: // xed -> Latn
+        case 0x98970000u: // xeg -> Latn
+        case 0xB0970000u: // xem -> Latn
+        case 0xC4970000u: // xer -> Latn
+        case 0xC8970000u: // xes -> Latn
+        case 0xCC970000u: // xet -> Latn
+        case 0xD0970000u: // xeu -> Latn
+        case 0x84D70000u: // xgb -> Latn
+        case 0x8CD70000u: // xgd -> Latn
+        case 0x98D70000u: // xgg -> Latn
+        case 0xA0D70000u: // xgi -> Latn
+        case 0xB0D70000u: // xgm -> Latn
+        case 0xD0D70000u: // xgu -> Latn
+        case 0xD8D70000u: // xgw -> Latn
+        case 0x78680000u: // xh -> Latn
+        case 0xD4F70000u: // xhv -> Latn
+        case 0xA1170000u: // xii -> Latn
+        case 0xB5170000u: // xin -> Latn
+        case 0xC5170000u: // xir -> Latn
+        case 0xE1170000u: // xiy -> Latn
+        case 0x85370000u: // xjb -> Latn
+        case 0xCD370000u: // xjt -> Latn
+        case 0x85570000u: // xkb -> Latn
+        case 0x8D570000u: // xkd -> Latn
+        case 0x91570000u: // xke -> Latn
+        case 0x99570000u: // xkg -> Latn
+        case 0xAD570000u: // xkl -> Latn
+        case 0xB5570000u: // xkn -> Latn
+        case 0xC1570000u: // xkq -> Latn
+        case 0xC5570000u: // xkr -> Latn
+        case 0xC9570000u: // xks -> Latn
+        case 0xCD570000u: // xkt -> Latn
+        case 0xD1570000u: // xku -> Latn
+        case 0xD5570000u: // xkv -> Latn
+        case 0xD9570000u: // xkw -> Latn
+        case 0xDD570000u: // xkx -> Latn
+        case 0xE1570000u: // xky -> Latn
+        case 0xE5570000u: // xkz -> Latn
+        case 0x81770000u: // xla -> Latn
+        case 0x81970000u: // xma -> Latn
+        case 0x85970000u: // xmb -> Latn
+        case 0x89970000u: // xmc -> Latn
+        case 0x8D970000u: // xmd -> Latn
+        case 0x99970000u: // xmg -> Latn
+        case 0x9D970000u: // xmh -> Latn
+        case 0xA5970000u: // xmj -> Latn
+        case 0xB1970000u: // xmm -> Latn
+        case 0xB9970000u: // xmo -> Latn
+        case 0xBD970000u: // xmp -> Latn
+        case 0xC1970000u: // xmq -> Latn
+        case 0xCD970000u: // xmt -> Latn
+        case 0xD1970000u: // xmu -> Latn
+        case 0xD5970000u: // xmv -> Latn
+        case 0xD9970000u: // xmw -> Latn
+        case 0xDD970000u: // xmx -> Latn
+        case 0xE1970000u: // xmy -> Latn
+        case 0xE5970000u: // xmz -> Latn
+        case 0x85B70000u: // xnb -> Latn
+        case 0xA1B70000u: // xni -> Latn
+        case 0xA5B70000u: // xnj -> Latn
+        case 0xA9B70000u: // xnk -> Latn
+        case 0xB1B70000u: // xnm -> Latn
+        case 0xB5B70000u: // xnn -> Latn
+        case 0xC1B70000u: // xnq -> Latn
+        case 0xCDB70000u: // xnt -> Latn
+        case 0xD1B70000u: // xnu -> Latn
+        case 0xE1B70000u: // xny -> Latn
+        case 0xE5B70000u: // xnz -> Latn
+        case 0x89D70000u: // xoc -> Latn
+        case 0x8DD70000u: // xod -> Latn
+        case 0x99D70000u: // xog -> Latn
+        case 0xA1D70000u: // xoi -> Latn
+        case 0xA9D70000u: // xok -> Latn
+        case 0xB1D70000u: // xom -> Latn
+        case 0xB5D70000u: // xon -> Latn
+        case 0xB9D70000u: // xoo -> Latn
+        case 0xBDD70000u: // xop -> Latn
+        case 0xC5D70000u: // xor -> Latn
+        case 0xD9D70000u: // xow -> Latn
+        case 0x81F70000u: // xpa -> Latn
+        case 0x85F70000u: // xpb -> Latn
+        case 0x8DF70000u: // xpd -> Latn
+        case 0x95F70000u: // xpf -> Latn
+        case 0x9DF70000u: // xph -> Latn
+        case 0xA5F70000u: // xpj -> Latn
+        case 0xA9F70000u: // xpk -> Latn
+        case 0xADF70000u: // xpl -> Latn
+        case 0xB5F70000u: // xpn -> Latn
+        case 0xB9F70000u: // xpo -> Latn
+        case 0xC1F70000u: // xpq -> Latn
+        case 0xCDF70000u: // xpt -> Latn
+        case 0xD5F70000u: // xpv -> Latn
+        case 0xD9F70000u: // xpw -> Latn
+        case 0xDDF70000u: // xpx -> Latn
+        case 0xE5F70000u: // xpz -> Latn
+        case 0x82370000u: // xra -> Latn
+        case 0x86370000u: // xrb -> Latn
+        case 0x8E370000u: // xrd -> Latn
+        case 0x92370000u: // xre -> Latn
+        case 0x9A370000u: // xrg -> Latn
+        case 0xA2370000u: // xri -> Latn
+        case 0xC6370000u: // xrr -> Latn
+        case 0xD2370000u: // xru -> Latn
+        case 0xDA370000u: // xrw -> Latn
+        case 0x86570000u: // xsb -> Latn
+        case 0x92570000u: // xse -> Latn
+        case 0x9E570000u: // xsh -> Latn
+        case 0xA2570000u: // xsi -> Latn
+        case 0xB2570000u: // xsm -> Latn
+        case 0xB6570000u: // xsn -> Latn
+        case 0xBE570000u: // xsp -> Latn
+        case 0xC2570000u: // xsq -> Latn
+        case 0xD2570000u: // xsu -> Latn
+        case 0xE2570000u: // xsy -> Latn
+        case 0x82770000u: // xta -> Latn
+        case 0x86770000u: // xtb -> Latn
+        case 0x8A770000u: // xtc -> Latn
+        case 0x8E770000u: // xtd -> Latn
+        case 0x92770000u: // xte -> Latn
+        case 0x9E770000u: // xth -> Latn
+        case 0xA2770000u: // xti -> Latn
+        case 0xA6770000u: // xtj -> Latn
+        case 0xAE770000u: // xtl -> Latn
+        case 0xB2770000u: // xtm -> Latn
+        case 0xB6770000u: // xtn -> Latn
+        case 0xBE770000u: // xtp -> Latn
+        case 0xCA770000u: // xts -> Latn
+        case 0xCE770000u: // xtt -> Latn
+        case 0xD2770000u: // xtu -> Latn
+        case 0xD6770000u: // xtv -> Latn
+        case 0xDA770000u: // xtw -> Latn
+        case 0xE2770000u: // xty -> Latn
+        case 0x8E970000u: // xud -> Latn
+        case 0xAE970000u: // xul -> Latn
+        case 0xB2970000u: // xum -> Latn
+        case 0xB6970000u: // xun -> Latn
+        case 0xBA970000u: // xuo -> Latn
+        case 0xCE970000u: // xut -> Latn
+        case 0xD2970000u: // xuu -> Latn
+        case 0xB6B70000u: // xvn -> Latn
+        case 0xBAB70000u: // xvo -> Latn
+        case 0xCAB70000u: // xvs -> Latn
+        case 0x82D70000u: // xwa -> Latn
+        case 0x8ED70000u: // xwd -> Latn
+        case 0x92D70000u: // xwe -> Latn
+        case 0xA6D70000u: // xwj -> Latn
+        case 0xAAD70000u: // xwk -> Latn
+        case 0xAED70000u: // xwl -> Latn
+        case 0xC6D70000u: // xwr -> Latn
+        case 0xCED70000u: // xwt -> Latn
+        case 0xDAD70000u: // xww -> Latn
+        case 0x86F70000u: // xxb -> Latn
+        case 0xAAF70000u: // xxk -> Latn
+        case 0xB2F70000u: // xxm -> Latn
+        case 0xC6F70000u: // xxr -> Latn
+        case 0xCEF70000u: // xxt -> Latn
+        case 0x83170000u: // xya -> Latn
+        case 0x87170000u: // xyb -> Latn
+        case 0xA7170000u: // xyj -> Latn
+        case 0xAB170000u: // xyk -> Latn
+        case 0xAF170000u: // xyl -> Latn
+        case 0xCF170000u: // xyt -> Latn
+        case 0xE3170000u: // xyy -> Latn
+        case 0xBF370000u: // xzp -> Latn
+        case 0x80180000u: // yaa -> Latn
+        case 0x84180000u: // yab -> Latn
+        case 0x88180000u: // yac -> Latn
+        case 0x8C180000u: // yad -> Latn
+        case 0x90180000u: // yae -> Latn
+        case 0x94180000u: // yaf -> Latn
+        case 0x98180000u: // yag -> Latn
+        case 0x9C180000u: // yah -> Latn
+        case 0xA4180000u: // yaj -> Latn
+        case 0xA8180000u: // yak -> Latn
+        case 0xAC180000u: // yal -> Latn
+        case 0xB0180000u: // yam -> Latn
+        case 0xB4180000u: // yan -> Latn
+        case 0xB8180000u: // yao -> Latn
+        case 0xBC180000u: // yap -> Latn
+        case 0xC0180000u: // yaq -> Latn
+        case 0xC4180000u: // yar -> Latn
+        case 0xC8180000u: // yas -> Latn
+        case 0xCC180000u: // yat -> Latn
+        case 0xD0180000u: // yau -> Latn
+        case 0xD4180000u: // yav -> Latn
+        case 0xD8180000u: // yaw -> Latn
+        case 0xDC180000u: // yax -> Latn
+        case 0xE0180000u: // yay -> Latn
+        case 0xE4180000u: // yaz -> Latn
+        case 0x80380000u: // yba -> Latn
+        case 0x84380000u: // ybb -> Latn
+        case 0x90380000u: // ybe -> Latn
+        case 0xA4380000u: // ybj -> Latn
+        case 0xAC380000u: // ybl -> Latn
+        case 0xB0380000u: // ybm -> Latn
+        case 0xB4380000u: // ybn -> Latn
+        case 0xB8380000u: // ybo -> Latn
+        case 0xDC380000u: // ybx -> Latn
+        case 0xE0380000u: // yby -> Latn
+        case 0xAC580000u: // ycl -> Latn
+        case 0xB4580000u: // ycn -> Latn
+        case 0xC4580000u: // ycr -> Latn
+        case 0x80780000u: // yda -> Latn
+        case 0x90780000u: // yde -> Latn
+        case 0xA8780000u: // ydk -> Latn
+        case 0x88980000u: // yec -> Latn
+        case 0x90980000u: // yee -> Latn
+        case 0xA0980000u: // yei -> Latn
+        case 0xAC980000u: // yel -> Latn
+        case 0xC4980000u: // yer -> Latn
+        case 0xC8980000u: // yes -> Latn
+        case 0xCC980000u: // yet -> Latn
+        case 0xD4980000u: // yev -> Latn
+        case 0xE0980000u: // yey -> Latn
+        case 0x80D80000u: // yga -> Latn
+        case 0xA0D80000u: // ygi -> Latn
+        case 0xACD80000u: // ygl -> Latn
+        case 0xB0D80000u: // ygm -> Latn
+        case 0xC4D80000u: // ygr -> Latn
+        case 0xD0D80000u: // ygu -> Latn
+        case 0xD8D80000u: // ygw -> Latn
+        case 0x81180000u: // yia -> Latn
+        case 0xA1180000u: // yii -> Latn
+        case 0xA5180000u: // yij -> Latn
+        case 0xAD180000u: // yil -> Latn
+        case 0xB1180000u: // yim -> Latn
+        case 0xC5180000u: // yir -> Latn
+        case 0xC9180000u: // yis -> Latn
+        case 0x81580000u: // yka -> Latn
+        case 0xA1580000u: // yki -> Latn
+        case 0xA9580000u: // ykk -> Latn
+        case 0xB1580000u: // ykm -> Latn
+        case 0xB9580000u: // yko -> Latn
+        case 0xC5580000u: // ykr -> Latn
+        case 0xE1580000u: // yky -> Latn
+        case 0x81780000u: // yla -> Latn
+        case 0x85780000u: // ylb -> Latn
+        case 0x91780000u: // yle -> Latn
+        case 0x99780000u: // ylg -> Latn
+        case 0xA1780000u: // yli -> Latn
+        case 0xAD780000u: // yll -> Latn
+        case 0xC5780000u: // ylr -> Latn
+        case 0xD1780000u: // ylu -> Latn
+        case 0xE1780000u: // yly -> Latn
+        case 0x85980000u: // ymb -> Latn
+        case 0x91980000u: // yme -> Latn
+        case 0x99980000u: // ymg -> Latn
+        case 0xA9980000u: // ymk -> Latn
+        case 0xAD980000u: // yml -> Latn
+        case 0xB1980000u: // ymm -> Latn
+        case 0xB5980000u: // ymn -> Latn
+        case 0xB9980000u: // ymo -> Latn
+        case 0xBD980000u: // ymp -> Latn
+        case 0x8DB80000u: // ynd -> Latn
+        case 0x99B80000u: // yng -> Latn
+        case 0xADB80000u: // ynl -> Latn
+        case 0xC1B80000u: // ynq -> Latn
+        case 0xC9B80000u: // yns -> Latn
+        case 0xD1B80000u: // ynu -> Latn
+        case 0x796F0000u: // yo -> Latn
+        case 0x85D80000u: // yob -> Latn
+        case 0x99D80000u: // yog -> Latn
+        case 0xA9D80000u: // yok -> Latn
+        case 0xADD80000u: // yol -> Latn
+        case 0xB1D80000u: // yom -> Latn
+        case 0xB5D80000u: // yon -> Latn
+        case 0xCDD80000u: // yot -> Latn
+        case 0x82380000u: // yra -> Latn
+        case 0x86380000u: // yrb -> Latn
+        case 0x92380000u: // yre -> Latn
+        case 0xAE380000u: // yrl -> Latn
+        case 0xB2380000u: // yrm -> Latn
+        case 0xBA380000u: // yro -> Latn
+        case 0xCA380000u: // yrs -> Latn
+        case 0xDA380000u: // yrw -> Latn
+        case 0xE2380000u: // yry -> Latn
+        case 0xCA580000u: // yss -> Latn
+        case 0xDA780000u: // ytw -> Latn
+        case 0xE2780000u: // yty -> Latn
+        case 0x82980000u: // yua -> Latn
+        case 0x86980000u: // yub -> Latn
+        case 0x8A980000u: // yuc -> Latn
+        case 0x96980000u: // yuf -> Latn
+        case 0xA2980000u: // yui -> Latn
+        case 0xA6980000u: // yuj -> Latn
+        case 0xAE980000u: // yul -> Latn
+        case 0xB2980000u: // yum -> Latn
+        case 0xB6980000u: // yun -> Latn
+        case 0xBE980000u: // yup -> Latn
+        case 0xC2980000u: // yuq -> Latn
+        case 0xC6980000u: // yur -> Latn
+        case 0xCE980000u: // yut -> Latn
+        case 0xDA980000u: // yuw -> Latn
+        case 0xE6980000u: // yuz -> Latn
+        case 0x82B80000u: // yva -> Latn
+        case 0xCEB80000u: // yvt -> Latn
+        case 0x82D80000u: // ywa -> Latn
+        case 0x9AD80000u: // ywg -> Latn
+        case 0xB6D80000u: // ywn -> Latn
+        case 0xC6D80000u: // ywr -> Latn
+        case 0xDAD80000u: // yww -> Latn
+        case 0x82F80000u: // yxa -> Latn
+        case 0x9AF80000u: // yxg -> Latn
+        case 0xAEF80000u: // yxl -> Latn
+        case 0xB2F80000u: // yxm -> Latn
+        case 0xD2F80000u: // yxu -> Latn
+        case 0xE2F80000u: // yxy -> Latn
+        case 0xC7180000u: // yyr -> Latn
+        case 0xD3180000u: // yyu -> Latn
+        case 0x7A610000u: // za -> Latn
+        case 0x80190000u: // zaa -> Latn
+        case 0x84190000u: // zab -> Latn
+        case 0x88190000u: // zac -> Latn
+        case 0x8C190000u: // zad -> Latn
+        case 0x90190000u: // zae -> Latn
+        case 0x94190000u: // zaf -> Latn
+        case 0x98190000u: // zag -> Latn
+        case 0x9C190000u: // zah -> Latn
+        case 0xA4190000u: // zaj -> Latn
+        case 0xA8190000u: // zak -> Latn
+        case 0xB0190000u: // zam -> Latn
+        case 0xB8190000u: // zao -> Latn
+        case 0xBC190000u: // zap -> Latn
+        case 0xC0190000u: // zaq -> Latn
+        case 0xC4190000u: // zar -> Latn
+        case 0xC8190000u: // zas -> Latn
+        case 0xCC190000u: // zat -> Latn
+        case 0xD4190000u: // zav -> Latn
+        case 0xD8190000u: // zaw -> Latn
+        case 0xDC190000u: // zax -> Latn
+        case 0xE0190000u: // zay -> Latn
+        case 0xE4190000u: // zaz -> Latn
+        case 0x88390000u: // zbc -> Latn
+        case 0x90390000u: // zbe -> Latn
+        case 0xCC390000u: // zbt -> Latn
+        case 0xD0390000u: // zbu -> Latn
+        case 0xD8390000u: // zbw -> Latn
+        case 0x80590000u: // zca -> Latn
+        case 0x80990000u: // zea -> Latn
+        case 0x98990000u: // zeg -> Latn
+        case 0xB0990000u: // zem -> Latn
+        case 0x80D90000u: // zga -> Latn
+        case 0xC4D90000u: // zgr -> Latn
+        case 0xA0F90000u: // zhi -> Latn
+        case 0xB4F90000u: // zhn -> Latn
+        case 0xD8F90000u: // zhw -> Latn
+        case 0x81190000u: // zia -> Latn
+        case 0xA9190000u: // zik -> Latn
+        case 0xAD190000u: // zil -> Latn
+        case 0xB1190000u: // zim -> Latn
+        case 0xB5190000u: // zin -> Latn
+        case 0xD9190000u: // ziw -> Latn
+        case 0xE5190000u: // ziz -> Latn
+        case 0x81590000u: // zka -> Latn
+        case 0x8D590000u: // zkd -> Latn
+        case 0xBD590000u: // zkp -> Latn
+        case 0xD1590000u: // zku -> Latn
+        case 0x81790000u: // zla -> Latn
+        case 0xB1790000u: // zlm -> Latn
+        case 0xD1790000u: // zlu -> Latn
+        case 0x81990000u: // zma -> Latn
+        case 0x85990000u: // zmb -> Latn
+        case 0x89990000u: // zmc -> Latn
+        case 0x8D990000u: // zmd -> Latn
+        case 0x91990000u: // zme -> Latn
+        case 0x95990000u: // zmf -> Latn
+        case 0x99990000u: // zmg -> Latn
+        case 0x9D990000u: // zmh -> Latn
+        case 0xA1990000u: // zmi -> Latn
+        case 0xA5990000u: // zmj -> Latn
+        case 0xA9990000u: // zmk -> Latn
+        case 0xAD990000u: // zml -> Latn
+        case 0xB1990000u: // zmm -> Latn
+        case 0xB5990000u: // zmn -> Latn
+        case 0xB9990000u: // zmo -> Latn
+        case 0xBD990000u: // zmp -> Latn
+        case 0xC1990000u: // zmq -> Latn
+        case 0xC5990000u: // zmr -> Latn
+        case 0xC9990000u: // zms -> Latn
+        case 0xCD990000u: // zmt -> Latn
+        case 0xD1990000u: // zmu -> Latn
+        case 0xD5990000u: // zmv -> Latn
+        case 0xD9990000u: // zmw -> Latn
+        case 0xDD990000u: // zmx -> Latn
+        case 0xE1990000u: // zmy -> Latn
+        case 0xE5990000u: // zmz -> Latn
+        case 0x81B90000u: // zna -> Latn
+        case 0x91B90000u: // zne -> Latn
+        case 0x99B90000u: // zng -> Latn
+        case 0xA9B90000u: // znk -> Latn
+        case 0xC9B90000u: // zns -> Latn
+        case 0x89D90000u: // zoc -> Latn
+        case 0x9DD90000u: // zoh -> Latn
+        case 0xB1D90000u: // zom -> Latn
+        case 0xB9D90000u: // zoo -> Latn
+        case 0xC1D90000u: // zoq -> Latn
+        case 0xC5D90000u: // zor -> Latn
+        case 0xC9D90000u: // zos -> Latn
+        case 0x81F90000u: // zpa -> Latn
+        case 0x85F90000u: // zpb -> Latn
+        case 0x89F90000u: // zpc -> Latn
+        case 0x8DF90000u: // zpd -> Latn
+        case 0x91F90000u: // zpe -> Latn
+        case 0x95F90000u: // zpf -> Latn
+        case 0x99F90000u: // zpg -> Latn
+        case 0x9DF90000u: // zph -> Latn
+        case 0xA1F90000u: // zpi -> Latn
+        case 0xA5F90000u: // zpj -> Latn
+        case 0xA9F90000u: // zpk -> Latn
+        case 0xADF90000u: // zpl -> Latn
+        case 0xB1F90000u: // zpm -> Latn
+        case 0xB5F90000u: // zpn -> Latn
+        case 0xB9F90000u: // zpo -> Latn
+        case 0xBDF90000u: // zpp -> Latn
+        case 0xC1F90000u: // zpq -> Latn
+        case 0xC5F90000u: // zpr -> Latn
+        case 0xC9F90000u: // zps -> Latn
+        case 0xCDF90000u: // zpt -> Latn
+        case 0xD1F90000u: // zpu -> Latn
+        case 0xD5F90000u: // zpv -> Latn
+        case 0xD9F90000u: // zpw -> Latn
+        case 0xDDF90000u: // zpx -> Latn
+        case 0xE1F90000u: // zpy -> Latn
+        case 0xE5F90000u: // zpz -> Latn
+        case 0xB6390000u: // zrn -> Latn
+        case 0xBA390000u: // zro -> Latn
+        case 0xCA390000u: // zrs -> Latn
+        case 0x82590000u: // zsa -> Latn
+        case 0xC6590000u: // zsr -> Latn
+        case 0xD2590000u: // zsu -> Latn
+        case 0x92790000u: // zte -> Latn
+        case 0x9A790000u: // ztg -> Latn
+        case 0xAE790000u: // ztl -> Latn
+        case 0xB2790000u: // ztm -> Latn
+        case 0xB6790000u: // ztn -> Latn
+        case 0xBE790000u: // ztp -> Latn
+        case 0xC2790000u: // ztq -> Latn
+        case 0xCA790000u: // zts -> Latn
+        case 0xCE790000u: // ztt -> Latn
+        case 0xD2790000u: // ztu -> Latn
+        case 0xDE790000u: // ztx -> Latn
+        case 0xE2790000u: // zty -> Latn
+        case 0x7A750000u: // zu -> Latn
+        case 0x9E990000u: // zuh -> Latn
+        case 0xB6990000u: // zun -> Latn
+        case 0xE2990000u: // zuy -> Latn
+        case 0xA7190000u: // zyj -> Latn
+        case 0xBF190000u: // zyp -> Latn
+        case 0x83390000u: // zza -> Latn
+            return SCRIPT_CODES[55u];
+        case 0xBC8B0000u: // lep -> Lepc
+            return SCRIPT_CODES[56u];
+        case 0x840B0000u: // lab -> Lina
+            return SCRIPT_CODES[57u];
+        case 0xE1860000u: // gmy -> Linb
+            return SCRIPT_CODES[58u];
+        case 0xC90B0000u: // lis -> Lisu
+            return SCRIPT_CODES[59u];
+        case 0xE1880000u: // imy -> Lyci
+        case 0x89770000u: // xlc -> Lyci
+            return SCRIPT_CODES[60u];
+        case 0x8D770000u: // xld -> Lydi
+            return SCRIPT_CODES[61u];
+        case 0x8D0C0000u: // mid -> Mand
+        case 0xE70C0000u: // myz -> Mand
+            return SCRIPT_CODES[62u];
+        case 0xB5970000u: // xmn -> Mani
+            return SCRIPT_CODES[63u];
+        case 0x9F370000u: // xzh -> Marc
+            return SCRIPT_CODES[64u];
+        case 0x95830000u: // dmf -> Medf
+            return SCRIPT_CODES[65u];
+        case 0xC5970000u: // xmr -> Merc
+            return SCRIPT_CODES[66u];
+        case 0x94000000u: // aaf -> Mlym
+        case 0xAD600000u: // all -> Mlym
+        case 0xD48A0000u: // kev -> Mlym
+        case 0x9CAA0000u: // kfh -> Mlym
+        case 0xC12C0000u: // mjq -> Mlym
+        case 0xC52C0000u: // mjr -> Mlym
+        case 0xD52C0000u: // mjv -> Mlym
+        case 0x6D6C0000u: // ml -> Mlym
+        case 0x944F0000u: // pcf -> Mlym
+        case 0x984F0000u: // pcg -> Mlym
+        case 0xC54F0000u: // pkr -> Mlym
+        case 0x98740000u: // udg -> Mlym
+        case 0x80980000u: // yea -> Mlym
+            return SCRIPT_CODES[67u];
+        case 0xC58E0000u: // omr -> Modi
+            return SCRIPT_CODES[68u];
+        case 0xD2E10000u: // bxu -> Mong
+        case 0x6D6E434Eu: // mn-CN -> Mong
+        case 0x89AC0000u: // mnc -> Mong
+        case 0x96AC0000u: // mvf -> Mong
+            return SCRIPT_CODES[69u];
+        case 0xBA2C0000u: // mro -> Mroo
+            return SCRIPT_CODES[70u];
+        case 0xBD8E0000u: // omp -> Mtei
+            return SCRIPT_CODES[71u];
+        case 0xB9000000u: // aio -> Mymr
+        case 0xA9610000u: // blk -> Mymr
+        case 0x92C10000u: // bwe -> Mymr
+        case 0x9E420000u: // csh -> Mymr
+        case 0xD1A30000u: // dnu -> Mymr
+        case 0xD5A30000u: // dnv -> Mymr
+        case 0xB9E70000u: // hpo -> Mymr
+        case 0xCDA80000u: // int -> Mymr
+        case 0xB1490000u: // jkm -> Mymr
+        case 0xCCEA0000u: // kht -> Mymr
+        case 0xBD2A0000u: // kjp -> Mymr
+        case 0xD24A0000u: // ksu -> Mymr
+        case 0xDA4A0000u: // ksw -> Mymr
+        case 0xC2AA0000u: // kvq -> Mymr
+        case 0xCEAA0000u: // kvt -> Mymr
+        case 0x96EA0000u: // kxf -> Mymr
+        case 0xAAEA0000u: // kxk -> Mymr
+        case 0xD9AC0000u: // mnw -> Mymr
+        case 0xCECC0000u: // mwt -> Mymr
+        case 0x6D790000u: // my -> Mymr
+        case 0xC42E0000u: // obr -> Mymr
+        case 0xDD8E0000u: // omx -> Mymr
+        case 0x904F0000u: // pce -> Mymr
+        case 0xA8EF0000u: // phk -> Mymr
+        case 0xAD6F0000u: // pll -> Mymr
+        case 0xBACF0000u: // pwo -> Mymr
+        case 0xDF0F0000u: // pyx -> Mymr
+        case 0x84310000u: // rbb -> Mymr
+        case 0xA1510000u: // rki -> Mymr
+        case 0xE5910000u: // rmz -> Mymr
+        case 0xB4F20000u: // shn -> Mymr
+        case 0xB8530000u: // tco -> Mymr
+        case 0xAD330000u: // tjl -> Mymr
+        case 0xB6B30000u: // tvn -> Mymr
+            return SCRIPT_CODES[72u];
+        case 0x81B70000u: // xna -> Narb
+            return SCRIPT_CODES[73u];
+        case 0x8ACD0000u: // nwc -> Newa
+            return SCRIPT_CODES[74u];
+        case 0xB40C474Eu: // man-GN -> Nkoo
+        case 0xBA0D0000u: // nqo -> Nkoo
+            return SCRIPT_CODES[75u];
+        case 0xDCF90000u: // zhx -> Nshu
+            return SCRIPT_CODES[76u];
+        case 0xACCF0000u: // pgl -> Ogam
+        case 0x80D20000u: // sga -> Ogam
+        case 0xA1F70000u: // xpi -> Ogam
+            return SCRIPT_CODES[77u];
+        case 0xCC120000u: // sat -> Olck
+            return SCRIPT_CODES[78u];
+        case 0xAA6E0000u: // otk -> Orkh
+            return SCRIPT_CODES[79u];
+        case 0xD4610000u: // bdv -> Orya
+        case 0xD8A10000u: // bfw -> Orya
+        case 0xBA430000u: // dso -> Orya
+        case 0xAAC30000u: // dwk -> Orya
+        case 0xC0060000u: // gaq -> Orya
+        case 0xA4260000u: // gbj -> Orya
+        case 0x84660000u: // gdb -> Orya
+        case 0xB6890000u: // jun -> Orya
+        case 0xE2890000u: // juy -> Orya
+        case 0x6F720000u: // or -> Orya
+        case 0x988F0000u: // peg -> Orya
+        case 0xA0910000u: // rei -> Orya
+        case 0xD5F20000u: // spv -> Orya
+        case 0xA1540000u: // uki -> Orya
+        case 0xC9170000u: // xis -> Orya
+        case 0x9A390000u: // zrg -> Orya
+            return SCRIPT_CODES[80u];
+        case 0x824E0000u: // osa -> Osge
+            return SCRIPT_CODES[81u];
+        case 0xA28E0000u: // oui -> Ougr
+            return SCRIPT_CODES[82u];
+        case 0x8E620000u: // ctd -> Pauc
+            return SCRIPT_CODES[83u];
+        case 0xAC0F0000u: // pal -> Phli
+            return SCRIPT_CODES[84u];
+        case 0xB02E0000u: // obm -> Phnx
+        case 0xB4EF0000u: // phn -> Phnx
+            return SCRIPT_CODES[85u];
+        case 0x8D870000u: // hmd -> Plrd
+        case 0xBE6A0000u: // ktp -> Plrd
+        case 0xB9EB0000u: // lpo -> Plrd
+        case 0xB0B20000u: // sfm -> Plrd
+        case 0xBCD80000u: // ygp -> Plrd
+        case 0x81B80000u: // yna -> Plrd
+        case 0xE2580000u: // ysy -> Plrd
+        case 0xC2D80000u: // ywq -> Plrd
+        case 0xD2D80000u: // ywu -> Plrd
+            return SCRIPT_CODES[86u];
+        case 0xC5F70000u: // xpr -> Prti
+            return SCRIPT_CODES[87u];
+        case 0xAC200000u: // abl -> Rjng
+            return SCRIPT_CODES[88u];
+        case 0x98F10000u: // rhg -> Rohg
+            return SCRIPT_CODES[89u];
+        case 0xB5CD0000u: // non -> Runr
+        case 0xB62D0000u: // nrn -> Runr
+        case 0xD2F20000u: // sxu -> Runr
+            return SCRIPT_CODES[90u];
+        case 0xB0120000u: // sam -> Samr
+        case 0xBD920000u: // smp -> Samr
+            return SCRIPT_CODES[91u];
+        case 0x82570000u: // xsa -> Sarb
+            return SCRIPT_CODES[92u];
+        case 0xE4120000u: // saz -> Saur
+            return SCRIPT_CODES[93u];
+        case 0x92400000u: // ase -> Sgnw
+            return SCRIPT_CODES[94u];
+        case 0x70690000u: // pi -> Sinh
+        case 0x73690000u: // si -> Sinh
+            return SCRIPT_CODES[95u];
+        case 0x99D20000u: // sog -> Sogd
+            return SCRIPT_CODES[96u];
+        case 0x86320000u: // srb -> Sora
+            return SCRIPT_CODES[97u];
+        case 0x99820000u: // cmg -> Soyo
+            return SCRIPT_CODES[98u];
+        case 0xE6920000u: // suz -> Sunu
+            return SCRIPT_CODES[99u];
+        case 0xA1000000u: // aii -> Syrc
+        case 0xD9800000u: // amw -> Syrc
+        case 0xB4E10000u: // bhn -> Syrc
+        case 0x95210000u: // bjf -> Syrc
+        case 0xCE270000u: // hrt -> Syrc
+        case 0x8E0A0000u: // kqd -> Syrc
+        case 0xC8EB0000u: // lhs -> Syrc
+        case 0xC40E0000u: // oar -> Syrc
+        case 0x8B120000u: // syc -> Syrc
+        case 0xB7120000u: // syn -> Syrc
+        case 0xC7120000u: // syr -> Syrc
+        case 0xC5930000u: // tmr -> Syrc
+            return SCRIPT_CODES[100u];
+        case 0xA8330000u: // tbk -> Tagb
+            return SCRIPT_CODES[101u];
+        case 0xA1070000u: // hii -> Takr
+        case 0x81A90000u: // jna -> Takr
+        case 0xD0520000u: // scu -> Takr
+            return SCRIPT_CODES[102u];
+        case 0x8C730000u: // tdd -> Tale
+        case 0xA0F30000u: // thi -> Tale
+            return SCRIPT_CODES[103u];
+        case 0x84EA0000u: // khb -> Talu
+            return SCRIPT_CODES[104u];
+        case 0xC0A10000u: // bfq -> Taml
+        case 0xCE620000u: // ctt -> Taml
+        case 0xE2620000u: // cty -> Taml
+        case 0x82240000u: // era -> Taml
+        case 0xD2280000u: // iru -> Taml
+        case 0x90AA0000u: // kfe -> Taml
+        case 0xA0AA0000u: // kfi -> Taml
+        case 0xD68C0000u: // muv -> Taml
+        case 0x74610000u: // ta -> Taml
+        case 0xDC530000u: // tcx -> Taml
+        case 0x80150000u: // vaa -> Taml
+        case 0x86970000u: // xub -> Taml
+        case 0xA6970000u: // xuj -> Taml
+            return SCRIPT_CODES[105u];
+        case 0x9AF30000u: // txg -> Tang
+            return SCRIPT_CODES[106u];
+        case 0xCD610000u: // blt -> Tavt
+        case 0x81D20000u: // soa -> Tavt
+        case 0xC7130000u: // tyr -> Tavt
+            return SCRIPT_CODES[107u];
+        case 0x90620000u: // cde -> Telu
+        case 0xD0060000u: // gau -> Telu
+        case 0xE08A0000u: // key -> Telu
+        case 0x88AA0000u: // kfc -> Telu
+        case 0xB58B0000u: // lmn -> Telu
+        case 0xD12C0000u: // mju -> Telu
+        case 0xCD0D0000u: // nit -> Telu
+        case 0xCE2E0000u: // ort -> Telu
+        case 0xA44F0000u: // pcj -> Telu
+        case 0x74650000u: // te -> Telu
+        case 0xC0360000u: // wbq -> Telu
+        case 0xD0980000u: // yeu -> Telu
+            return SCRIPT_CODES[108u];
+        case 0xB8E60000u: // gho -> Tfng
+        case 0xA0F20000u: // shi -> Tfng
+        case 0x80730000u: // tda -> Tfng
+        case 0xB4990000u: // zen -> Tfng
+        case 0x9CD90000u: // zgh -> Tfng
+            return SCRIPT_CODES[109u];
+        case 0x64760000u: // dv -> Thaa
+            return SCRIPT_CODES[110u];
+        case 0xA3210000u: // bzi -> Thai
+        case 0xB4220000u: // cbn -> Thai
+        case 0x99C20000u: // cog -> Thai
+        case 0xCC6A0000u: // kdt -> Thai
+        case 0x94EA0000u: // khf -> Thai
+        case 0xCD2A0000u: // kjt -> Thai
+        case 0xB2EA0000u: // kxm -> Thai
+        case 0xBC4B0000u: // lcp -> Thai
+        case 0xAECB0000u: // lwl -> Thai
+        case 0xB2CB0000u: // lwm -> Thai
+        case 0xB14C0000u: // mkm -> Thai
+        case 0x956C0000u: // mlf -> Thai
+        case 0xE5EC0000u: // mpz -> Thai
+        case 0x822C0000u: // mra -> Thai
+        case 0xAF0D0000u: // nyl -> Thai
+        case 0xDB0D0000u: // nyw -> Thai
+        case 0xCCEF0000u: // pht -> Thai
+        case 0xD0EF0000u: // phu -> Thai
+        case 0xCE2F0000u: // prt -> Thai
+        case 0xDACF0000u: // pww -> Thai
+        case 0x85520000u: // skb -> Thai
+        case 0xD1D20000u: // sou -> Thai
+        case 0x74680000u: // th -> Thai
+        case 0xB0F30000u: // thm -> Thai
+        case 0xCA730000u: // tts -> Thai
+        case 0xB8D40000u: // ugo -> Thai
+        case 0xAA340000u: // urk -> Thai
+        case 0xE1D80000u: // yoy -> Thai
+            return SCRIPT_CODES[111u];
+        case 0xDC600000u: // adx -> Tibt
+        case 0xD0A10000u: // bfu -> Tibt
+        case 0xA9410000u: // bkk -> Tibt
+        case 0x626F0000u: // bo -> Tibt
+        case 0xBA210000u: // bro -> Tibt
+        case 0xA8C20000u: // cgk -> Tibt
+        case 0x81A20000u: // cna -> Tibt
+        case 0x81430000u: // dka -> Tibt
+        case 0x92230000u: // dre -> Tibt
+        case 0x647A0000u: // dz -> Tibt
+        case 0xAF230000u: // dzl -> Tibt
+        case 0xCCE60000u: // ght -> Tibt
+        case 0x91C60000u: // goe -> Tibt
+        case 0x80690000u: // jda -> Tibt
+        case 0x83090000u: // jya -> Tibt
+        case 0x982A0000u: // kbg -> Tibt
+        case 0x98EA0000u: // khg -> Tibt
+        case 0xE52A0000u: // kjz -> Tibt
+        case 0x954A0000u: // kkf -> Tibt
+        case 0xA42B0000u: // lbj -> Tibt
+        case 0x9D4B0000u: // lkh -> Tibt
+        case 0xAA8B0000u: // luk -> Tibt
+        case 0x830B0000u: // lya -> Tibt
+        case 0xAA8C0000u: // muk -> Tibt
+        case 0x9C8D0000u: // neh -> Tibt
+        case 0x85ED0000u: // npb -> Tibt
+        case 0x916E0000u: // ole -> Tibt
+        case 0x866E0000u: // otb -> Tibt
+        case 0xD0320000u: // sbu -> Tibt
+        case 0xCCD20000u: // sgt -> Tibt
+        case 0xBD120000u: // sip -> Tibt
+        case 0xCDF20000u: // spt -> Tibt
+        case 0xB4530000u: // tcn -> Tibt
+        case 0x94D30000u: // tgf -> Tibt
+        case 0xA6530000u: // tsj -> Tibt
+        case 0x95570000u: // xkf -> Tibt
+        case 0xD0190000u: // zau -> Tibt
+            return SCRIPT_CODES[112u];
+        case 0xCE4D0000u: // nst -> Tnsa
+            return SCRIPT_CODES[113u];
+        case 0xBAF30000u: // txo -> Toto
+            return SCRIPT_CODES[114u];
+        case 0x80D40000u: // uga -> Ugar
+            return SCRIPT_CODES[115u];
+        case 0xA0150000u: // vai -> Vaii
+            return SCRIPT_CODES[116u];
+        case 0xBDAD0000u: // nnp -> Wcho
+            return SCRIPT_CODES[117u];
+        case 0xB88F0000u: // peo -> Xpeo
+            return SCRIPT_CODES[118u];
+        case 0xA9400000u: // akk -> Xsux
+        case 0xCD070000u: // hit -> Xsux
+        case 0xDE670000u: // htx -> Xsux
+        case 0xA08D0000u: // nei -> Xsux
+        case 0xCCEE0000u: // oht -> Xsux
+            return SCRIPT_CODES[119u];
+        case 0x69690000u: // ii -> Yiii
+        case 0xC9CD0000u: // nos -> Yiii
+        case 0x8E4D0000u: // nsd -> Yiii
+        case 0x964D0000u: // nsf -> Yiii
+        case 0xD64D0000u: // nsv -> Yiii
+        case 0xE26D0000u: // nty -> Yiii
+        case 0x9D920000u: // smh -> Yiii
+        case 0x99180000u: // yig -> Yiii
+        case 0xD5180000u: // yiv -> Yiii
+        case 0x8E580000u: // ysd -> Yiii
+        case 0xB6580000u: // ysn -> Yiii
+        case 0xBE580000u: // ysp -> Yiii
+            return SCRIPT_CODES[120u];
+        case 0x656E5841u: // en-XA -> ~~~A
+            return SCRIPT_CODES[121u];
+        case 0x61725842u: // ar-XB -> ~~~B
+            return SCRIPT_CODES[122u];
+        default:
+            return nullptr;
+     }
+}
+
+bool isLocaleRepresentative(uint32_t language_and_region, const char* script) {
+    const uint64_t packed_locale =
+            ((static_cast<uint64_t>(language_and_region)) << 32u) |
+            (static_cast<uint64_t>(packScript(script)));
+    switch(packed_locale) {
+        case 0x616145544C61746ELLU: // aa_Latn_ET
+        case 0x80004E474C61746ELLU: // aaa_Latn_NG
+        case 0x84004E474C61746ELLU: // aab_Latn_NG
+        case 0x880050474C61746ELLU: // aac_Latn_PG
+        case 0x8C0050474C61746ELLU: // aad_Latn_PG
+        case 0x900049544C61746ELLU: // aae_Latn_IT
+        case 0x9400494E4D6C796DLLU: // aaf_Mlym_IN
+        case 0x980050474C61746ELLU: // aag_Latn_PG
+        case 0x9C0050474C61746ELLU: // aah_Latn_PG
+        case 0xA00050474C61746ELLU: // aai_Latn_PG
+        case 0xA80050474C61746ELLU: // aak_Latn_PG
+        case 0xAC00434D4C61746ELLU: // aal_Latn_CM
+        case 0xB40042524C61746ELLU: // aan_Latn_BR
+        case 0xB800445A41726162LLU: // aao_Arab_DZ
+        case 0xBC0042524C61746ELLU: // aap_Latn_BR
+        case 0xC00055534C61746ELLU: // aaq_Latn_US
+        case 0xC800545A4C61746ELLU: // aas_Latn_TZ
+        case 0xCC0047524772656BLLU: // aat_Grek_GR
+        case 0xD00050474C61746ELLU: // aau_Latn_PG
+        case 0xD80050474C61746ELLU: // aaw_Latn_PG
+        case 0xDC0049444C61746ELLU: // aax_Latn_ID
+        case 0xE40049444C61746ELLU: // aaz_Latn_ID
+        case 0x616247454379726CLLU: // ab_Cyrl_GE
+        case 0x802043494C61746ELLU: // aba_Latn_CI
+        case 0x8420434D4C61746ELLU: // abb_Latn_CM
+        case 0x882050484C61746ELLU: // abc_Latn_PH
+        case 0x8C2050484C61746ELLU: // abd_Latn_PH
+        case 0x902043414C61746ELLU: // abe_Latn_CA
+        case 0x94204D594C61746ELLU: // abf_Latn_MY
+        case 0x982050474C61746ELLU: // abg_Latn_PG
+        case 0x9C20544A41726162LLU: // abh_Arab_TJ
+        case 0xA02043494C61746ELLU: // abi_Latn_CI
+        case 0xAC204944526A6E67LLU: // abl_Rjng_ID
+        case 0xB0204E474C61746ELLU: // abm_Latn_NG
+        case 0xB4204E474C61746ELLU: // abn_Latn_NG
+        case 0xB8204E474C61746ELLU: // abo_Latn_NG
+        case 0xBC2050484C61746ELLU: // abp_Latn_PH
+        case 0xC42047484C61746ELLU: // abr_Latn_GH
+        case 0xC82049444C61746ELLU: // abs_Latn_ID
+        case 0xCC2050474C61746ELLU: // abt_Latn_PG
+        case 0xD02043494C61746ELLU: // abu_Latn_CI
+        case 0xD420424841726162LLU: // abv_Arab_BH
+        case 0xD82050474C61746ELLU: // abw_Latn_PG
+        case 0xDC2050484C61746ELLU: // abx_Latn_PH
+        case 0xE02050474C61746ELLU: // aby_Latn_PG
+        case 0xE42049444C61746ELLU: // abz_Latn_ID
+        case 0x8040434F4C61746ELLU: // aca_Latn_CO
+        case 0x84404E474C61746ELLU: // acb_Latn_NG
+        case 0x8C4047484C61746ELLU: // acd_Latn_GH
+        case 0x904049444C61746ELLU: // ace_Latn_ID
+        case 0x94404C434C61746ELLU: // acf_Latn_LC
+        case 0x9C4055474C61746ELLU: // ach_Latn_UG
+        case 0xB040495141726162LLU: // acm_Arab_IQ
+        case 0xB440434E4C61746ELLU: // acn_Latn_CN
+        case 0xBC404E474C61746ELLU: // acp_Latn_NG
+        case 0xC040594541726162LLU: // acq_Arab_YE
+        case 0xC44047544C61746ELLU: // acr_Latn_GT
+        case 0xC84042524C61746ELLU: // acs_Latn_BR
+        case 0xCC404E4C4C61746ELLU: // act_Latn_NL
+        case 0xD04045434C61746ELLU: // acu_Latn_EC
+        case 0xD44055534C61746ELLU: // acv_Latn_US
+        case 0xD840534141726162LLU: // acw_Arab_SA
+        case 0xDC404F4D41726162LLU: // acx_Arab_OM
+        case 0xE04043594C61746ELLU: // acy_Latn_CY
+        case 0xE44053444C61746ELLU: // acz_Latn_SD
+        case 0x806047484C61746ELLU: // ada_Latn_GH
+        case 0x8460544C4C61746ELLU: // adb_Latn_TL
+        case 0x8C60434D4C61746ELLU: // add_Latn_CM
+        case 0x906054474C61746ELLU: // ade_Latn_TG
+        case 0x94604F4D41726162LLU: // adf_Arab_OM
+        case 0x986041554C61746ELLU: // adg_Latn_AU
+        case 0x9C6055474C61746ELLU: // adh_Latn_UG
+        case 0xA060494E4C61746ELLU: // adi_Latn_IN
+        case 0xA46043494C61746ELLU: // adj_Latn_CI
+        case 0xAC60494E4C61746ELLU: // adl_Latn_IN
+        case 0xB46049444C61746ELLU: // adn_Latn_ID
+        case 0xB86050474C61746ELLU: // ado_Latn_PG
+        case 0xC06047484C61746ELLU: // adq_Latn_GH
+        case 0xC46049444C61746ELLU: // adr_Latn_ID
+        case 0xCC6041554C61746ELLU: // adt_Latn_AU
+        case 0xD0604E474C61746ELLU: // adu_Latn_NG
+        case 0xD86042524C61746ELLU: // adw_Latn_BR
+        case 0xDC60434E54696274LLU: // adx_Tibt_CN
+        case 0xE06052554379726CLLU: // ady_Cyrl_RU
+        case 0xE46050474C61746ELLU: // adz_Latn_PG
+        case 0x6165495241767374LLU: // ae_Avst_IR
+        case 0x808041554C61746ELLU: // aea_Latn_AU
+        case 0x8480544E41726162LLU: // aeb_Arab_TN
+        case 0x8880454741726162LLU: // aec_Arab_EG
+        case 0x9080414641726162LLU: // aee_Arab_AF
+        case 0xA8804E434C61746ELLU: // aek_Latn_NC
+        case 0xAC80434D4C61746ELLU: // ael_Latn_CM
+        case 0xB080564E4C61746ELLU: // aem_Latn_VN
+        case 0xC080504B41726162LLU: // aeq_Arab_PK
+        case 0xC48041554C61746ELLU: // aer_Latn_AU
+        case 0xD080434E4C61746ELLU: // aeu_Latn_CN
+        case 0xD88050474C61746ELLU: // aew_Latn_PG
+        case 0xE08050474C61746ELLU: // aey_Latn_PG
+        case 0xE48050474C61746ELLU: // aez_Latn_PG
+        case 0x61665A414C61746ELLU: // af_Latn_ZA
+        case 0x84A04B5741726162LLU: // afb_Arab_KW
+        case 0x8CA050474C61746ELLU: // afd_Latn_PG
+        case 0x90A04E474C61746ELLU: // afe_Latn_NG
+        case 0x9CA047484C61746ELLU: // afh_Latn_GH
+        case 0xA0A050474C61746ELLU: // afi_Latn_PG
+        case 0xA8A050474C61746ELLU: // afk_Latn_PG
+        case 0xB4A04E474C61746ELLU: // afn_Latn_NG
+        case 0xB8A04E474C61746ELLU: // afo_Latn_NG
+        case 0xBCA050474C61746ELLU: // afp_Latn_PG
+        case 0xC8A04D584C61746ELLU: // afs_Latn_MX
+        case 0xD0A047484C61746ELLU: // afu_Latn_GH
+        case 0xE4A049444C61746ELLU: // afz_Latn_ID
+        case 0x80C050454C61746ELLU: // aga_Latn_PE
+        case 0x84C04E474C61746ELLU: // agb_Latn_NG
+        case 0x88C04E474C61746ELLU: // agc_Latn_NG
+        case 0x8CC050474C61746ELLU: // agd_Latn_PG
+        case 0x90C050474C61746ELLU: // age_Latn_PG
+        case 0x94C049444C61746ELLU: // agf_Latn_ID
+        case 0x98C050474C61746ELLU: // agg_Latn_PG
+        case 0x9CC043444C61746ELLU: // agh_Latn_CD
+        case 0xA0C0494E44657661LLU: // agi_Deva_IN
+        case 0xA4C0455445746869LLU: // agj_Ethi_ET
+        case 0xA8C050484C61746ELLU: // agk_Latn_PH
+        case 0xACC050474C61746ELLU: // agl_Latn_PG
+        case 0xB0C050474C61746ELLU: // agm_Latn_PG
+        case 0xB4C050484C61746ELLU: // agn_Latn_PH
+        case 0xB8C050474C61746ELLU: // ago_Latn_PG
+        case 0xC0C0434D4C61746ELLU: // agq_Latn_CM
+        case 0xC4C050454C61746ELLU: // agr_Latn_PE
+        case 0xC8C0434D4C61746ELLU: // ags_Latn_CM
+        case 0xCCC050484C61746ELLU: // agt_Latn_PH
+        case 0xD0C047544C61746ELLU: // agu_Latn_GT
+        case 0xD4C050484C61746ELLU: // agv_Latn_PH
+        case 0xD8C053424C61746ELLU: // agw_Latn_SB
+        case 0xDCC052554379726CLLU: // agx_Cyrl_RU
+        case 0xE0C050484C61746ELLU: // agy_Latn_PH
+        case 0xE4C050484C61746ELLU: // agz_Latn_PH
+        case 0x80E047484C61746ELLU: // aha_Latn_GH
+        case 0x84E056554C61746ELLU: // ahb_Latn_VU
+        case 0x98E0455445746869LLU: // ahg_Ethi_ET
+        case 0x9CE049444C61746ELLU: // ahh_Latn_ID
+        case 0xA0E043494C61746ELLU: // ahi_Latn_CI
+        case 0xA8E04D4D4C61746ELLU: // ahk_Latn_MM
+        case 0xACE054474C61746ELLU: // ahl_Latn_TG
+        case 0xB0E043494C61746ELLU: // ahm_Latn_CI
+        case 0xB4E04E474C61746ELLU: // ahn_Latn_NG
+        case 0xB8E0494E41686F6DLLU: // aho_Ahom_IN
+        case 0xBCE043494C61746ELLU: // ahp_Latn_CI
+        case 0xC4E0494E44657661LLU: // ahr_Deva_IN
+        case 0xC8E04E474C61746ELLU: // ahs_Latn_NG
+        case 0xCCE055534C61746ELLU: // aht_Latn_US
+        case 0x810053424C61746ELLU: // aia_Latn_SB
+        case 0x8500434E41726162LLU: // aib_Arab_CN
+        case 0x890050474C61746ELLU: // aic_Latn_PG
+        case 0x8D0041554C61746ELLU: // aid_Latn_AU
+        case 0x910050474C61746ELLU: // aie_Latn_PG
+        case 0x950050474C61746ELLU: // aif_Latn_PG
+        case 0x990041474C61746ELLU: // aig_Latn_AG
+        case 0xA100495153797263LLU: // aii_Syrc_IQ
+        case 0xA500494C48656272LLU: // aij_Hebr_IL
+        case 0xA9004E474C61746ELLU: // aik_Latn_NG
+        case 0xAD0050474C61746ELLU: // ail_Latn_PG
+        case 0xB100494E4C61746ELLU: // aim_Latn_IN
+        case 0xB5004A504B616E61LLU: // ain_Kana_JP
+        case 0xB900494E4D796D72LLU: // aio_Mymr_IN
+        case 0xBD0049444C61746ELLU: // aip_Latn_ID
+        case 0xC100414641726162LLU: // aiq_Arab_AF
+        case 0xC50049444C61746ELLU: // air_Latn_ID
+        case 0xCD0042524C61746ELLU: // ait_Latn_BR
+        case 0xD90045544C61746ELLU: // aiw_Latn_ET
+        case 0xDD0050474C61746ELLU: // aix_Latn_PG
+        case 0xE10043464C61746ELLU: // aiy_Latn_CF
+        case 0x812053534C61746ELLU: // aja_Latn_SS
+        case 0x9920424A4C61746ELLU: // ajg_Latn_BJ
+        case 0xA1204E434C61746ELLU: // aji_Latn_NC
+        case 0xB52041554C61746ELLU: // ajn_Latn_AU
+        case 0xD9204E474C61746ELLU: // ajw_Latn_NG
+        case 0xE520494E4C61746ELLU: // ajz_Latn_IN
+        case 0x616B47484C61746ELLU: // ak_Latn_GH
+        case 0x854049444C61746ELLU: // akb_Latn_ID
+        case 0x894049444C61746ELLU: // akc_Latn_ID
+        case 0x8D404E474C61746ELLU: // akd_Latn_NG
+        case 0x914047594C61746ELLU: // ake_Latn_GY
+        case 0x95404E474C61746ELLU: // akf_Latn_NG
+        case 0x994049444C61746ELLU: // akg_Latn_ID
+        case 0x9D4050474C61746ELLU: // akh_Latn_PG
+        case 0xA14050474C61746ELLU: // aki_Latn_PG
+        case 0xA940495158737578LLU: // akk_Xsux_IQ
+        case 0xAD4050484C61746ELLU: // akl_Latn_PH
+        case 0xB94053524C61746ELLU: // ako_Latn_SR
+        case 0xBD4047484C61746ELLU: // akp_Latn_GH
+        case 0xC14050474C61746ELLU: // akq_Latn_PG
+        case 0xC54056554C61746ELLU: // akr_Latn_VU
+        case 0xC94054474C61746ELLU: // aks_Latn_TG
+        case 0xCD4050474C61746ELLU: // akt_Latn_PG
+        case 0xD140434D4C61746ELLU: // aku_Latn_CM
+        case 0xD54052554379726CLLU: // akv_Cyrl_RU
+        case 0xD94043474C61746ELLU: // akw_Latn_CG
+        case 0xE54055534C61746ELLU: // akz_Latn_US
+        case 0x81604E474C61746ELLU: // ala_Latn_NG
+        case 0x8960434C4C61746ELLU: // alc_Latn_CL
+        case 0x8D6043494C61746ELLU: // ald_Latn_CI
+        case 0x916055534C61746ELLU: // ale_Latn_US
+        case 0x95604E474C61746ELLU: // alf_Latn_NG
+        case 0x9D6041554C61746ELLU: // alh_Latn_AU
+        case 0xA16050474C61746ELLU: // ali_Latn_PG
+        case 0xA56050484C61746ELLU: // alj_Latn_PH
+        case 0xA9604C414C616F6FLLU: // alk_Laoo_LA
+        case 0xAD60494E4D6C796DLLU: // all_Mlym_IN
+        case 0xB16056554C61746ELLU: // alm_Latn_VU
+        case 0xB560584B4C61746ELLU: // aln_Latn_XK
+        case 0xB96049444C61746ELLU: // alo_Latn_ID
+        case 0xBD6049444C61746ELLU: // alp_Latn_ID
+        case 0xC16043414C61746ELLU: // alq_Latn_CA
+        case 0xC56052554379726CLLU: // alr_Cyrl_RU
+        case 0xCD6052554379726CLLU: // alt_Cyrl_RU
+        case 0xD16053424C61746ELLU: // alu_Latn_SB
+        case 0xD960455445746869LLU: // alw_Ethi_ET
+        case 0xDD6050474C61746ELLU: // alx_Latn_PG
+        case 0xE16041554C61746ELLU: // aly_Latn_AU
+        case 0xE56043444C61746ELLU: // alz_Latn_CD
+        case 0x616D455445746869LLU: // am_Ethi_ET
+        case 0x818042524C61746ELLU: // ama_Latn_BR
+        case 0x85804E474C61746ELLU: // amb_Latn_NG
+        case 0x898050454C61746ELLU: // amc_Latn_PE
+        case 0x918050454C61746ELLU: // ame_Latn_PE
+        case 0x958045544C61746ELLU: // amf_Latn_ET
+        case 0x998041554C61746ELLU: // amg_Latn_AU
+        case 0xA18054574C61746ELLU: // ami_Latn_TW
+        case 0xA58054444C61746ELLU: // amj_Latn_TD
+        case 0xA98049444C61746ELLU: // amk_Latn_ID
+        case 0xB18050474C61746ELLU: // amm_Latn_PG
+        case 0xB58050474C61746ELLU: // amn_Latn_PG
+        case 0xB9804E474C61746ELLU: // amo_Latn_NG
+        case 0xBD8050474C61746ELLU: // amp_Latn_PG
+        case 0xC18049444C61746ELLU: // amq_Latn_ID
+        case 0xC58050454C61746ELLU: // amr_Latn_PE
+        case 0xC9804A504A70616ELLU: // ams_Jpan_JP
+        case 0xCD8050474C61746ELLU: // amt_Latn_PG
+        case 0xD1804D584C61746ELLU: // amu_Latn_MX
+        case 0xD58049444C61746ELLU: // amv_Latn_ID
+        case 0xD980535953797263LLU: // amw_Syrc_SY
+        case 0xDD8041554C61746ELLU: // amx_Latn_AU
+        case 0xE18041554C61746ELLU: // amy_Latn_AU
+        case 0xE58041554C61746ELLU: // amz_Latn_AU
+        case 0x616E45534C61746ELLU: // an_Latn_ES
+        case 0x81A0434F4C61746ELLU: // ana_Latn_CO
+        case 0x85A050454C61746ELLU: // anb_Latn_PE
+        case 0x89A04E474C61746ELLU: // anc_Latn_NG
+        case 0x8DA049444C61746ELLU: // and_Latn_ID
+        case 0x91A04E434C61746ELLU: // ane_Latn_NC
+        case 0x95A047484C61746ELLU: // anf_Latn_GH
+        case 0x99A047424C61746ELLU: // ang_Latn_GB
+        case 0x9DA050474C61746ELLU: // anh_Latn_PG
+        case 0xA1A052554379726CLLU: // ani_Cyrl_RU
+        case 0xA5A050474C61746ELLU: // anj_Latn_PG
+        case 0xA9A04E474C61746ELLU: // ank_Latn_NG
+        case 0xADA04D4D4C61746ELLU: // anl_Latn_MM
+        case 0xB1A0494E4C61746ELLU: // anm_Latn_IN
+        case 0xB5A04E474C61746ELLU: // ann_Latn_NG
+        case 0xB9A0434F4C61746ELLU: // ano_Latn_CO
+        case 0xBDA0494E44657661LLU: // anp_Deva_IN
+        case 0xC1A0494E44657661LLU: // anq_Deva_IN
+        case 0xC5A0494E44657661LLU: // anr_Deva_IN
+        case 0xC9A0434F4C61746ELLU: // ans_Latn_CO
+        case 0xCDA041554C61746ELLU: // ant_Latn_AU
+        case 0xD1A0455445746869LLU: // anu_Ethi_ET
+        case 0xD5A0434D4C61746ELLU: // anv_Latn_CM
+        case 0xD9A04E474C61746ELLU: // anw_Latn_NG
+        case 0xDDA050474C61746ELLU: // anx_Latn_PG
+        case 0xE1A043494C61746ELLU: // any_Latn_CI
+        case 0xE5A050474C61746ELLU: // anz_Latn_PG
+        case 0x81C053544C61746ELLU: // aoa_Latn_ST
+        case 0x85C050474C61746ELLU: // aob_Latn_PG
+        case 0x89C056454C61746ELLU: // aoc_Latn_VE
+        case 0x8DC050474C61746ELLU: // aod_Latn_PG
+        case 0x91C050474C61746ELLU: // aoe_Latn_PG
+        case 0x95C050474C61746ELLU: // aof_Latn_PG
+        case 0x99C050474C61746ELLU: // aog_Latn_PG
+        case 0xA1C041554C61746ELLU: // aoi_Latn_AU
+        case 0xA5C050474C61746ELLU: // aoj_Latn_PG
+        case 0xA9C04E434C61746ELLU: // aok_Latn_NC
+        case 0xADC049444C61746ELLU: // aol_Latn_ID
+        case 0xB1C050474C61746ELLU: // aom_Latn_PG
+        case 0xB5C050474C61746ELLU: // aon_Latn_PG
+        case 0xC5C056554C61746ELLU: // aor_Latn_VU
+        case 0xC9C049444C61746ELLU: // aos_Latn_ID
+        case 0xCDC0424442656E67LLU: // aot_Beng_BD
+        case 0xDDC047594C61746ELLU: // aox_Latn_GY
+        case 0xE5C049444C61746ELLU: // aoz_Latn_ID
+        case 0x85E053424C61746ELLU: // apb_Latn_SB
+        case 0x89E0535941726162LLU: // apc_Arab_SY
+        case 0x8DE0544741726162LLU: // apd_Arab_TG
+        case 0x91E050474C61746ELLU: // ape_Latn_PG
+        case 0x95E050484C61746ELLU: // apf_Latn_PH
+        case 0x99E049444C61746ELLU: // apg_Latn_ID
+        case 0x9DE04E5044657661LLU: // aph_Deva_NP
+        case 0xA1E042524C61746ELLU: // api_Latn_BR
+        case 0xA5E055534C61746ELLU: // apj_Latn_US
+        case 0xA9E055534C61746ELLU: // apk_Latn_US
+        case 0xADE055534C61746ELLU: // apl_Latn_US
+        case 0xB1E055534C61746ELLU: // apm_Latn_US
+        case 0xB5E042524C61746ELLU: // apn_Latn_BR
+        case 0xB9E050474C61746ELLU: // apo_Latn_PG
+        case 0xBDE056554C61746ELLU: // app_Latn_VU
+        case 0xC5E050474C61746ELLU: // apr_Latn_PG
+        case 0xC9E050474C61746ELLU: // aps_Latn_PG
+        case 0xCDE0494E4C61746ELLU: // apt_Latn_IN
+        case 0xD1E042524C61746ELLU: // apu_Latn_BR
+        case 0xD5E042524C61746ELLU: // apv_Latn_BR
+        case 0xD9E055534C61746ELLU: // apw_Latn_US
+        case 0xDDE049444C61746ELLU: // apx_Latn_ID
+        case 0xE1E042524C61746ELLU: // apy_Latn_BR
+        case 0xE5E050474C61746ELLU: // apz_Latn_PG
+        case 0x8A0052554379726CLLU: // aqc_Cyrl_RU
+        case 0x8E004D4C4C61746ELLU: // aqd_Latn_ML
+        case 0x9A004E474C61746ELLU: // aqg_Latn_NG
+        case 0xAA004E474C61746ELLU: // aqk_Latn_NG
+        case 0xB20049444C61746ELLU: // aqm_Latn_ID
+        case 0xB60050484C61746ELLU: // aqn_Latn_PH
+        case 0xC6004E434C61746ELLU: // aqr_Latn_NC
+        case 0xCE0050594C61746ELLU: // aqt_Latn_PY
+        case 0xE60042524C61746ELLU: // aqz_Latn_BR
+        case 0x6172454741726162LLU: // ar_Arab_EG
+        case 0x8A20495241726D69LLU: // arc_Armi_IR
+        case 0x8A20495148617472LLU: // arc_Hatr_IQ
+        case 0x8A204A4F4E626174LLU: // arc_Nbat_JO
+        case 0x8A20535950616C6DLLU: // arc_Palm_SY
+        case 0x8E2041554C61746ELLU: // ard_Latn_AU
+        case 0x922041554C61746ELLU: // are_Latn_AU
+        case 0x9E20434F4C61746ELLU: // arh_Latn_CO
+        case 0xA22055534C61746ELLU: // ari_Latn_US
+        case 0xA62042524C61746ELLU: // arj_Latn_BR
+        case 0xAA2042524C61746ELLU: // ark_Latn_BR
+        case 0xAE2050454C61746ELLU: // arl_Latn_PE
+        case 0xB620434C4C61746ELLU: // arn_Latn_CL
+        case 0xBA20424F4C61746ELLU: // aro_Latn_BO
+        case 0xBE2055534C61746ELLU: // arp_Latn_US
+        case 0xC220445A41726162LLU: // arq_Arab_DZ
+        case 0xC62042524C61746ELLU: // arr_Latn_BR
+        case 0xCA20534141726162LLU: // ars_Arab_SA
+        case 0xD22042524C61746ELLU: // aru_Latn_BR
+        case 0xDA2053524C61746ELLU: // arw_Latn_SR
+        case 0xDE2042524C61746ELLU: // arx_Latn_BR
+        case 0xE2204D4141726162LLU: // ary_Arab_MA
+        case 0xE620454741726162LLU: // arz_Arab_EG
+        case 0x6173494E42656E67LLU: // as_Beng_IN
+        case 0x8240545A4C61746ELLU: // asa_Latn_TZ
+        case 0x864043414C61746ELLU: // asb_Latn_CA
+        case 0x8A4049444C61746ELLU: // asc_Latn_ID
+        case 0x9240555353676E77LLU: // ase_Sgnw_US
+        case 0x9A404E474C61746ELLU: // asg_Latn_NG
+        case 0x9E4050454C61746ELLU: // ash_Latn_PE
+        case 0xA24049444C61746ELLU: // asi_Latn_ID
+        case 0xA640434D4C61746ELLU: // asj_Latn_CM
+        case 0xAA40414641726162LLU: // ask_Arab_AF
+        case 0xAE4049444C61746ELLU: // asl_Latn_ID
+        case 0xB64042524C61746ELLU: // asn_Latn_BR
+        case 0xBA4050474C61746ELLU: // aso_Latn_PG
+        case 0xC640494E44657661LLU: // asr_Deva_IN
+        case 0xCA40434D4C61746ELLU: // ass_Latn_CM
+        case 0xCE4045534C61746ELLU: // ast_Latn_ES
+        case 0xD24042524C61746ELLU: // asu_Latn_BR
+        case 0xD64043444C61746ELLU: // asv_Latn_CD
+        case 0xDE4050474C61746ELLU: // asx_Latn_PG
+        case 0xE24049444C61746ELLU: // asy_Latn_ID
+        case 0xE64049444C61746ELLU: // asz_Latn_ID
+        case 0x826050474C61746ELLU: // ata_Latn_PG
+        case 0x8660434E4C61746ELLU: // atb_Latn_CN
+        case 0x8A6050454C61746ELLU: // atc_Latn_PE
+        case 0x8E6050484C61746ELLU: // atd_Latn_PH
+        case 0x926050474C61746ELLU: // ate_Latn_PG
+        case 0x9A604E474C61746ELLU: // atg_Latn_NG
+        case 0xA26043494C61746ELLU: // ati_Latn_CI
+        case 0xA66043414C61746ELLU: // atj_Latn_CA
+        case 0xAA6050484C61746ELLU: // atk_Latn_PH
+        case 0xAE6050484C61746ELLU: // atl_Latn_PH
+        case 0xB26050484C61746ELLU: // atm_Latn_PH
+        case 0xB660495241726162LLU: // atn_Arab_IR
+        case 0xBA60434D4C61746ELLU: // ato_Latn_CM
+        case 0xBE6050484C61746ELLU: // atp_Latn_PH
+        case 0xC26049444C61746ELLU: // atq_Latn_ID
+        case 0xC66042524C61746ELLU: // atr_Latn_BR
+        case 0xCA6055534C61746ELLU: // ats_Latn_US
+        case 0xCE6050484C61746ELLU: // att_Latn_PH
+        case 0xD26053534C61746ELLU: // atu_Latn_SS
+        case 0xD66052554379726CLLU: // atv_Cyrl_RU
+        case 0xDA6055534C61746ELLU: // atw_Latn_US
+        case 0xDE6042524C61746ELLU: // atx_Latn_BR
+        case 0xE26056554C61746ELLU: // aty_Latn_VU
+        case 0xE66050484C61746ELLU: // atz_Latn_PH
+        case 0x828053424C61746ELLU: // aua_Latn_SB
+        case 0x8A8045434C61746ELLU: // auc_Latn_EC
+        case 0x8E8053424C61746ELLU: // aud_Latn_SB
+        case 0x9A80424A4C61746ELLU: // aug_Latn_BJ
+        case 0x9E805A4D4C61746ELLU: // auh_Latn_ZM
+        case 0xA28050474C61746ELLU: // aui_Latn_PG
+        case 0xA6804C5941726162LLU: // auj_Arab_LY
+        case 0xAA8050474C61746ELLU: // auk_Latn_PG
+        case 0xAE8056554C61746ELLU: // aul_Latn_VU
+        case 0xB2804E474C61746ELLU: // aum_Latn_NG
+        case 0xB68050474C61746ELLU: // aun_Latn_PG
+        case 0xBA804E474C61746ELLU: // auo_Latn_NG
+        case 0xBE8050474C61746ELLU: // aup_Latn_PG
+        case 0xC28049444C61746ELLU: // auq_Latn_ID
+        case 0xC68050474C61746ELLU: // aur_Latn_PG
+        case 0xCE8050464C61746ELLU: // aut_Latn_PF
+        case 0xD28049444C61746ELLU: // auu_Latn_ID
+        case 0xDA8049444C61746ELLU: // auw_Latn_ID
+        case 0xE28050474C61746ELLU: // auy_Latn_PG
+        case 0xE680555A41726162LLU: // auz_Arab_UZ
+        case 0x617652554379726CLLU: // av_Cyrl_RU
+        case 0x86A050474C61746ELLU: // avb_Latn_PG
+        case 0x8EA0495241726162LLU: // avd_Arab_IR
+        case 0xA2A043494C61746ELLU: // avi_Latn_CI
+        case 0xAEA0454741726162LLU: // avl_Arab_EG
+        case 0xB2A041554C61746ELLU: // avm_Latn_AU
+        case 0xB6A047484C61746ELLU: // avn_Latn_GH
+        case 0xBAA042524C61746ELLU: // avo_Latn_BR
+        case 0xCAA050454C61746ELLU: // avs_Latn_PE
+        case 0xCEA050474C61746ELLU: // avt_Latn_PG
+        case 0xD2A053534C61746ELLU: // avu_Latn_SS
+        case 0xD6A042524C61746ELLU: // avv_Latn_BR
+        case 0x82C0494E44657661LLU: // awa_Deva_IN
+        case 0x86C050474C61746ELLU: // awb_Latn_PG
+        case 0x8AC04E474C61746ELLU: // awc_Latn_NG
+        case 0x92C042524C61746ELLU: // awe_Latn_BR
+        case 0x9AC041554C61746ELLU: // awg_Latn_AU
+        case 0x9EC049444C61746ELLU: // awh_Latn_ID
+        case 0xA2C050474C61746ELLU: // awi_Latn_PG
+        case 0xAAC041554C61746ELLU: // awk_Latn_AU
+        case 0xB2C050474C61746ELLU: // awm_Latn_PG
+        case 0xB6C0455445746869LLU: // awn_Ethi_ET
+        case 0xBAC04E474C61746ELLU: // awo_Latn_NG
+        case 0xC6C049444C61746ELLU: // awr_Latn_ID
+        case 0xCAC049444C61746ELLU: // aws_Latn_ID
+        case 0xCEC042524C61746ELLU: // awt_Latn_BR
+        case 0xD2C049444C61746ELLU: // awu_Latn_ID
+        case 0xD6C049444C61746ELLU: // awv_Latn_ID
+        case 0xDAC050474C61746ELLU: // aww_Latn_PG
+        case 0xDEC050474C61746ELLU: // awx_Latn_PG
+        case 0xE2C049444C61746ELLU: // awy_Latn_ID
+        case 0x86E041524C61746ELLU: // axb_Latn_AR
+        case 0x92E041554C61746ELLU: // axe_Latn_AU
+        case 0x9AE042524C61746ELLU: // axg_Latn_BR
+        case 0xAAE043464C61746ELLU: // axk_Latn_CF
+        case 0xAEE041554C61746ELLU: // axl_Latn_AU
+        case 0xB2E0414D41726D6ELLU: // axm_Armn_AM
+        case 0xDEE04E434C61746ELLU: // axx_Latn_NC
+        case 0x6179424F4C61746ELLU: // ay_Latn_BO
+        case 0x830050474C61746ELLU: // aya_Latn_PG
+        case 0x8700424A4C61746ELLU: // ayb_Latn_BJ
+        case 0x8B0050454C61746ELLU: // ayc_Latn_PE
+        case 0x8F0041554C61746ELLU: // ayd_Latn_AU
+        case 0x93004E474C61746ELLU: // aye_Latn_NG
+        case 0x9B0054474C61746ELLU: // ayg_Latn_TG
+        case 0x9F00594541726162LLU: // ayh_Arab_YE
+        case 0xA3004E474C61746ELLU: // ayi_Latn_NG
+        case 0xAB004E474C61746ELLU: // ayk_Latn_NG
+        case 0xAF004C5941726162LLU: // ayl_Arab_LY
+        case 0xB700594541726162LLU: // ayn_Arab_YE
+        case 0xBB0050594C61746ELLU: // ayo_Latn_PY
+        case 0xBF00495141726162LLU: // ayp_Arab_IQ
+        case 0xC30050474C61746ELLU: // ayq_Latn_PG
+        case 0xCB0050484C61746ELLU: // ays_Latn_PH
+        case 0xCF0050484C61746ELLU: // ayt_Latn_PH
+        case 0xD3004E474C61746ELLU: // ayu_Latn_NG
+        case 0xE70049444C61746ELLU: // ayz_Latn_ID
+        case 0x617A495241726162LLU: // az_Arab_IR
+        case 0x617A415A4C61746ELLU: // az_Latn_AZ
+        case 0x8720495241726162LLU: // azb_Arab_IR
+        case 0x8F204D584C61746ELLU: // azd_Latn_MX
+        case 0x9B204D584C61746ELLU: // azg_Latn_MX
+        case 0xB3204D584C61746ELLU: // azm_Latn_MX
+        case 0xB7204D584C61746ELLU: // azn_Latn_MX
+        case 0xBB20434D4C61746ELLU: // azo_Latn_CM
+        case 0xCF2050484C61746ELLU: // azt_Latn_PH
+        case 0xE7204D584C61746ELLU: // azz_Latn_MX
+        case 0x626152554379726CLLU: // ba_Cyrl_RU
+        case 0x800153424C61746ELLU: // baa_Latn_SB
+        case 0x840147574C61746ELLU: // bab_Latn_GW
+        case 0x880149444C61746ELLU: // bac_Latn_ID
+        case 0x900156454C61746ELLU: // bae_Latn_VE
+        case 0x9401434D4C61746ELLU: // baf_Latn_CM
+        case 0x9801434D4C61746ELLU: // bag_Latn_CM
+        case 0x9C0142534C61746ELLU: // bah_Latn_BS
+        case 0xA40149444C61746ELLU: // baj_Latn_ID
+        case 0xAC01504B41726162LLU: // bal_Arab_PK
+        case 0xB40149444C61746ELLU: // ban_Latn_ID
+        case 0xB801434F4C61746ELLU: // bao_Latn_CO
+        case 0xBC014E5044657661LLU: // bap_Deva_NP
+        case 0xBC01494E4B726169LLU: // bap_Krai_IN
+        case 0xC40141544C61746ELLU: // bar_Latn_AT
+        case 0xC801434D4C61746ELLU: // bas_Latn_CM
+        case 0xD0014E474C61746ELLU: // bau_Latn_NG
+        case 0xD401434D4C61746ELLU: // bav_Latn_CM
+        case 0xD801434D4C61746ELLU: // baw_Latn_CM
+        case 0xDC01434D42616D75LLU: // bax_Bamu_CM
+        case 0xE00149444C61746ELLU: // bay_Latn_ID
+        case 0x8021424A4C61746ELLU: // bba_Latn_BJ
+        case 0x842150474C61746ELLU: // bbb_Latn_PG
+        case 0x882149444C61746ELLU: // bbc_Latn_ID
+        case 0x8C2150474C61746ELLU: // bbd_Latn_PG
+        case 0x902143444C61746ELLU: // bbe_Latn_CD
+        case 0x942150474C61746ELLU: // bbf_Latn_PG
+        case 0x982147414C61746ELLU: // bbg_Latn_GA
+        case 0xA021434D4C61746ELLU: // bbi_Latn_CM
+        case 0xA421434D4C61746ELLU: // bbj_Latn_CM
+        case 0xA821434D4C61746ELLU: // bbk_Latn_CM
+        case 0xAC21474547656F72LLU: // bbl_Geor_GE
+        case 0xB02143444C61746ELLU: // bbm_Latn_CD
+        case 0xB42150474C61746ELLU: // bbn_Latn_PG
+        case 0xB82142464C61746ELLU: // bbo_Latn_BF
+        case 0xBC2143464C61746ELLU: // bbp_Latn_CF
+        case 0xC021434D4C61746ELLU: // bbq_Latn_CM
+        case 0xC42150474C61746ELLU: // bbr_Latn_PG
+        case 0xC8214E474C61746ELLU: // bbs_Latn_NG
+        case 0xCC214E474C61746ELLU: // bbt_Latn_NG
+        case 0xD0214E474C61746ELLU: // bbu_Latn_NG
+        case 0xD42150474C61746ELLU: // bbv_Latn_PG
+        case 0xD821434D4C61746ELLU: // bbw_Latn_CM
+        case 0xDC21434D4C61746ELLU: // bbx_Latn_CM
+        case 0xE021434D4C61746ELLU: // bby_Latn_CM
+        case 0x8041434E4C61746ELLU: // bca_Latn_CN
+        case 0x8441534E4C61746ELLU: // bcb_Latn_SN
+        case 0x8C4149444C61746ELLU: // bcd_Latn_ID
+        case 0x9041434D4C61746ELLU: // bce_Latn_CM
+        case 0x944150474C61746ELLU: // bcf_Latn_PG
+        case 0x9841474E4C61746ELLU: // bcg_Latn_GN
+        case 0x9C4150474C61746ELLU: // bch_Latn_PG
+        case 0xA04143494C61746ELLU: // bci_Latn_CI
+        case 0xA44141554C61746ELLU: // bcj_Latn_AU
+        case 0xA84141554C61746ELLU: // bck_Latn_AU
+        case 0xB04150474C61746ELLU: // bcm_Latn_PG
+        case 0xB4414E474C61746ELLU: // bcn_Latn_NG
+        case 0xB84150474C61746ELLU: // bco_Latn_PG
+        case 0xBC4143444C61746ELLU: // bcp_Latn_CD
+        case 0xC041455445746869LLU: // bcq_Ethi_ET
+        case 0xC44143414C61746ELLU: // bcr_Latn_CA
+        case 0xC8414E474C61746ELLU: // bcs_Latn_NG
+        case 0xCC4143444C61746ELLU: // bct_Latn_CD
+        case 0xD04150474C61746ELLU: // bcu_Latn_PG
+        case 0xD4414E474C61746ELLU: // bcv_Latn_NG
+        case 0xD841434D4C61746ELLU: // bcw_Latn_CM
+        case 0xE0414E474C61746ELLU: // bcy_Latn_NG
+        case 0xE441534E4C61746ELLU: // bcz_Latn_SN
+        case 0x8061534E4C61746ELLU: // bda_Latn_SN
+        case 0x846149444C61746ELLU: // bdb_Latn_ID
+        case 0x8861434F4C61746ELLU: // bdc_Latn_CO
+        case 0x8C6150474C61746ELLU: // bdd_Latn_PG
+        case 0x90614E474C61746ELLU: // bde_Latn_NG
+        case 0x946150474C61746ELLU: // bdf_Latn_PG
+        case 0x98614D594C61746ELLU: // bdg_Latn_MY
+        case 0x9C6153534C61746ELLU: // bdh_Latn_SS
+        case 0xA06153444C61746ELLU: // bdi_Latn_SD
+        case 0xA46153534C61746ELLU: // bdj_Latn_SS
+        case 0xA861415A4C61746ELLU: // bdk_Latn_AZ
+        case 0xAC6149444C61746ELLU: // bdl_Latn_ID
+        case 0xB06154444C61746ELLU: // bdm_Latn_TD
+        case 0xB461434D4C61746ELLU: // bdn_Latn_CM
+        case 0xB86154444C61746ELLU: // bdo_Latn_TD
+        case 0xBC61545A4C61746ELLU: // bdp_Latn_TZ
+        case 0xC061564E4C61746ELLU: // bdq_Latn_VN
+        case 0xC4614D594C61746ELLU: // bdr_Latn_MY
+        case 0xC861545A4C61746ELLU: // bds_Latn_TZ
+        case 0xCC6143464C61746ELLU: // bdt_Latn_CF
+        case 0xD061434D4C61746ELLU: // bdu_Latn_CM
+        case 0xD461494E4F727961LLU: // bdv_Orya_IN
+        case 0xD86149444C61746ELLU: // bdw_Latn_ID
+        case 0xDC6149444C61746ELLU: // bdx_Latn_ID
+        case 0xE06141554C61746ELLU: // bdy_Latn_AU
+        case 0xE461504B41726162LLU: // bdz_Arab_PK
+        case 0x626542594379726CLLU: // be_Cyrl_BY
+        case 0x808143414C61746ELLU: // bea_Latn_CA
+        case 0x8481434D4C61746ELLU: // beb_Latn_CM
+        case 0x8881434D4C61746ELLU: // bec_Latn_CM
+        case 0x8C8149444C61746ELLU: // bed_Latn_ID
+        case 0x9081494E44657661LLU: // bee_Deva_IN
+        case 0x948150474C61746ELLU: // bef_Latn_PG
+        case 0x9C81424A4C61746ELLU: // beh_Latn_BJ
+        case 0xA08149444C61746ELLU: // bei_Latn_ID
+        case 0xA481534441726162LLU: // bej_Arab_SD
+        case 0xA88150474C61746ELLU: // bek_Latn_PG
+        case 0xB0815A4D4C61746ELLU: // bem_Latn_ZM
+        case 0xB88150474C61746ELLU: // beo_Latn_PG
+        case 0xBC8149444C61746ELLU: // bep_Latn_ID
+        case 0xC08143474C61746ELLU: // beq_Latn_CG
+        case 0xC88154444C61746ELLU: // bes_Latn_TD
+        case 0xCC8143494C61746ELLU: // bet_Latn_CI
+        case 0xD08149444C61746ELLU: // beu_Latn_ID
+        case 0xD48143494C61746ELLU: // bev_Latn_CI
+        case 0xD88149444C61746ELLU: // bew_Latn_ID
+        case 0xDC8153534C61746ELLU: // bex_Latn_SS
+        case 0xE08150474C61746ELLU: // bey_Latn_PG
+        case 0xE481545A4C61746ELLU: // bez_Latn_TZ
+        case 0x80A153534C61746ELLU: // bfa_Latn_SS
+        case 0x84A1494E44657661LLU: // bfb_Deva_IN
+        case 0x88A1434E4C61746ELLU: // bfc_Latn_CN
+        case 0x8CA1434D4C61746ELLU: // bfd_Latn_CM
+        case 0x90A149444C61746ELLU: // bfe_Latn_ID
+        case 0x94A143464C61746ELLU: // bff_Latn_CF
+        case 0x98A149444C61746ELLU: // bfg_Latn_ID
+        case 0x9CA150474C61746ELLU: // bfh_Latn_PG
+        case 0xA4A1434D4C61746ELLU: // bfj_Latn_CM
+        case 0xACA143464C61746ELLU: // bfl_Latn_CF
+        case 0xB0A1434D4C61746ELLU: // bfm_Latn_CM
+        case 0xB4A1544C4C61746ELLU: // bfn_Latn_TL
+        case 0xB8A142464C61746ELLU: // bfo_Latn_BF
+        case 0xBCA1434D4C61746ELLU: // bfp_Latn_CM
+        case 0xC0A1494E54616D6CLLU: // bfq_Taml_IN
+        case 0xC8A1434E4C61746ELLU: // bfs_Latn_CN
+        case 0xCCA1504B41726162LLU: // bft_Arab_PK
+        case 0xD0A1494E54696274LLU: // bfu_Tibt_IN
+        case 0xD8A1494E4F727961LLU: // bfw_Orya_IN
+        case 0xDCA150484C61746ELLU: // bfx_Latn_PH
+        case 0xE0A1494E44657661LLU: // bfy_Deva_IN
+        case 0xE4A1494E44657661LLU: // bfz_Deva_IN
+        case 0x626742474379726CLLU: // bg_Cyrl_BG
+        case 0x80C14E474C61746ELLU: // bga_Latn_NG
+        case 0x84C149444C61746ELLU: // bgb_Latn_ID
+        case 0x88C1494E44657661LLU: // bgc_Deva_IN
+        case 0x8CC1494E44657661LLU: // bgd_Deva_IN
+        case 0x94C1434D4C61746ELLU: // bgf_Latn_CM
+        case 0x98C1494E4C61746ELLU: // bgg_Latn_IN
+        case 0xA0C150484C61746ELLU: // bgi_Latn_PH
+        case 0xA4C1434D4C61746ELLU: // bgj_Latn_CM
+        case 0xB4C1504B41726162LLU: // bgn_Arab_PK
+        case 0xB8C1474E4C61746ELLU: // bgo_Latn_GN
+        case 0xBCC1504B41726162LLU: // bgp_Arab_PK
+        case 0xC0C1494E44657661LLU: // bgq_Deva_IN
+        case 0xC4C1494E4C61746ELLU: // bgr_Latn_IN
+        case 0xC8C150484C61746ELLU: // bgs_Latn_PH
+        case 0xCCC153424C61746ELLU: // bgt_Latn_SB
+        case 0xD0C14E474C61746ELLU: // bgu_Latn_NG
+        case 0xD4C149444C61746ELLU: // bgv_Latn_ID
+        case 0xD8C1494E44657661LLU: // bgw_Deva_IN
+        case 0xDCC154524772656BLLU: // bgx_Grek_TR
+        case 0xE0C149444C61746ELLU: // bgy_Latn_ID
+        case 0xE4C149444C61746ELLU: // bgz_Latn_ID
+        case 0x80E1494E44657661LLU: // bha_Deva_IN
+        case 0x84E1494E44657661LLU: // bhb_Deva_IN
+        case 0x88E149444C61746ELLU: // bhc_Latn_ID
+        case 0x8CE1494E44657661LLU: // bhd_Deva_IN
+        case 0x90E1504B41726162LLU: // bhe_Arab_PK
+        case 0x94E150474C61746ELLU: // bhf_Latn_PG
+        case 0x98E150474C61746ELLU: // bhg_Latn_PG
+        case 0x9CE1494C4379726CLLU: // bhh_Cyrl_IL
+        case 0xA0E1494E44657661LLU: // bhi_Deva_IN
+        case 0xA4E14E5044657661LLU: // bhj_Deva_NP
+        case 0xACE150474C61746ELLU: // bhl_Latn_PG
+        case 0xB0E14F4D41726162LLU: // bhm_Arab_OM
+        case 0xB4E1474553797263LLU: // bhn_Syrc_GE
+        case 0xB8E1494E44657661LLU: // bho_Deva_IN
+        case 0xBCE149444C61746ELLU: // bhp_Latn_ID
+        case 0xC0E149444C61746ELLU: // bhq_Latn_ID
+        case 0xC4E14D474C61746ELLU: // bhr_Latn_MG
+        case 0xC8E1434D4C61746ELLU: // bhs_Latn_CM
+        case 0xCCE1494E44657661LLU: // bht_Deva_IN
+        case 0xD0E1494E44657661LLU: // bhu_Deva_IN
+        case 0xD4E149444C61746ELLU: // bhv_Latn_ID
+        case 0xD8E149444C61746ELLU: // bhw_Latn_ID
+        case 0xE0E143444C61746ELLU: // bhy_Latn_CD
+        case 0xE4E149444C61746ELLU: // bhz_Latn_ID
+        case 0x626956554C61746ELLU: // bi_Latn_VU
+        case 0x810141554C61746ELLU: // bia_Latn_AU
+        case 0x850142464C61746ELLU: // bib_Latn_BF
+        case 0x8D0154444C61746ELLU: // bid_Latn_TD
+        case 0x910150474C61746ELLU: // bie_Latn_PG
+        case 0x950147574C61746ELLU: // bif_Latn_GW
+        case 0x990150474C61746ELLU: // big_Latn_PG
+        case 0xA90150484C61746ELLU: // bik_Latn_PH
+        case 0xAD014E474C61746ELLU: // bil_Latn_NG
+        case 0xB10147484C61746ELLU: // bim_Latn_GH
+        case 0xB5014E474C61746ELLU: // bin_Latn_NG
+        case 0xB90150474C61746ELLU: // bio_Latn_PG
+        case 0xBD0143444C61746ELLU: // bip_Latn_CD
+        case 0xC10150474C61746ELLU: // biq_Latn_PG
+        case 0xC50150474C61746ELLU: // bir_Latn_PG
+        case 0xCD0150474C61746ELLU: // bit_Latn_PG
+        case 0xD101494E4C61746ELLU: // biu_Latn_IN
+        case 0xD50147484C61746ELLU: // biv_Latn_GH
+        case 0xD901434D4C61746ELLU: // biw_Latn_CM
+        case 0xE101494E44657661LLU: // biy_Deva_IN
+        case 0xE50143444C61746ELLU: // biz_Latn_CD
+        case 0x812143444C61746ELLU: // bja_Latn_CD
+        case 0x852141554C61746ELLU: // bjb_Latn_AU
+        case 0x892150474C61746ELLU: // bjc_Latn_PG
+        case 0x9521494C53797263LLU: // bjf_Syrc_IL
+        case 0x992147574C61746ELLU: // bjg_Latn_GW
+        case 0x9D2150474C61746ELLU: // bjh_Latn_PG
+        case 0xA12145544C61746ELLU: // bji_Latn_ET
+        case 0xA521494E44657661LLU: // bjj_Deva_IN
+        case 0xA92150474C61746ELLU: // bjk_Latn_PG
+        case 0xAD2150474C61746ELLU: // bjl_Latn_PG
+        case 0xB121495141726162LLU: // bjm_Arab_IQ
+        case 0xB52149444C61746ELLU: // bjn_Latn_ID
+        case 0xB92143464C61746ELLU: // bjo_Latn_CF
+        case 0xBD2150474C61746ELLU: // bjp_Latn_PG
+        case 0xC52150474C61746ELLU: // bjr_Latn_PG
+        case 0xC92142424C61746ELLU: // bjs_Latn_BB
+        case 0xCD21534E4C61746ELLU: // bjt_Latn_SN
+        case 0xD121434D4C61746ELLU: // bju_Latn_CM
+        case 0xD52154444C61746ELLU: // bjv_Latn_TD
+        case 0xD92143494C61746ELLU: // bjw_Latn_CI
+        case 0xDD2150484C61746ELLU: // bjx_Latn_PH
+        case 0xE12141554C61746ELLU: // bjy_Latn_AU
+        case 0xE52150474C61746ELLU: // bjz_Latn_PG
+        case 0x81414E474C61746ELLU: // bka_Latn_NG
+        case 0x8941434D4C61746ELLU: // bkc_Latn_CM
+        case 0x8D4150484C61746ELLU: // bkd_Latn_PH
+        case 0x954143444C61746ELLU: // bkf_Latn_CD
+        case 0x994143464C61746ELLU: // bkg_Latn_CF
+        case 0x9D41434D4C61746ELLU: // bkh_Latn_CM
+        case 0xA14156554C61746ELLU: // bki_Latn_VU
+        case 0xA54143464C61746ELLU: // bkj_Latn_CF
+        case 0xA941494E54696274LLU: // bkk_Tibt_IN
+        case 0xAD4149444C61746ELLU: // bkl_Latn_ID
+        case 0xB141434D4C61746ELLU: // bkm_Latn_CM
+        case 0xB54149444C61746ELLU: // bkn_Latn_ID
+        case 0xB941434D4C61746ELLU: // bko_Latn_CM
+        case 0xBD4143444C61746ELLU: // bkp_Latn_CD
+        case 0xC14142524C61746ELLU: // bkq_Latn_BR
+        case 0xC54149444C61746ELLU: // bkr_Latn_ID
+        case 0xC94150484C61746ELLU: // bks_Latn_PH
+        case 0xCD4143444C61746ELLU: // bkt_Latn_CD
+        case 0xD14150484C61746ELLU: // bku_Latn_PH
+        case 0xD5414E474C61746ELLU: // bkv_Latn_NG
+        case 0xD94143474C61746ELLU: // bkw_Latn_CG
+        case 0xDD41544C4C61746ELLU: // bkx_Latn_TL
+        case 0xE1414E474C61746ELLU: // bky_Latn_NG
+        case 0xE54149444C61746ELLU: // bkz_Latn_ID
+        case 0x816143414C61746ELLU: // bla_Latn_CA
+        case 0x856153424C61746ELLU: // blb_Latn_SB
+        case 0x896143414C61746ELLU: // blc_Latn_CA
+        case 0x8D6149444C61746ELLU: // bld_Latn_ID
+        case 0x916147574C61746ELLU: // ble_Latn_GW
+        case 0x956149444C61746ELLU: // blf_Latn_ID
+        case 0x9D614C524C61746ELLU: // blh_Latn_LR
+        case 0xA16143444C61746ELLU: // bli_Latn_CD
+        case 0xA56149444C61746ELLU: // blj_Latn_ID
+        case 0xA9614D4D4D796D72LLU: // blk_Mymr_MM
+        case 0xB16153534C61746ELLU: // blm_Latn_SS
+        case 0xB56150484C61746ELLU: // bln_Latn_PH
+        case 0xB961424A4C61746ELLU: // blo_Latn_BJ
+        case 0xBD6153424C61746ELLU: // blp_Latn_SB
+        case 0xC16150474C61746ELLU: // blq_Latn_PG
+        case 0xC561434E4C61746ELLU: // blr_Latn_CN
+        case 0xC96149444C61746ELLU: // bls_Latn_ID
+        case 0xCD61564E54617674LLU: // blt_Tavt_VN
+        case 0xD561414F4C61746ELLU: // blv_Latn_AO
+        case 0xD96150484C61746ELLU: // blw_Latn_PH
+        case 0xDD6150484C61746ELLU: // blx_Latn_PH
+        case 0xE161424A4C61746ELLU: // bly_Latn_BJ
+        case 0xE56149444C61746ELLU: // blz_Latn_ID
+        case 0x626D4D4C4C61746ELLU: // bm_Latn_ML
+        case 0x81814E474C61746ELLU: // bma_Latn_NG
+        case 0x858143444C61746ELLU: // bmb_Latn_CD
+        case 0x898150474C61746ELLU: // bmc_Latn_PG
+        case 0x8D81474E4C61746ELLU: // bmd_Latn_GN
+        case 0x918143464C61746ELLU: // bme_Latn_CF
+        case 0x9581534C4C61746ELLU: // bmf_Latn_SL
+        case 0x998143444C61746ELLU: // bmg_Latn_CD
+        case 0x9D8150474C61746ELLU: // bmh_Latn_PG
+        case 0xA18154444C61746ELLU: // bmi_Latn_TD
+        case 0xA5814E5044657661LLU: // bmj_Deva_NP
+        case 0xA98150474C61746ELLU: // bmk_Latn_PG
+        case 0xAD8143444C61746ELLU: // bml_Latn_CD
+        case 0xB1814D474C61746ELLU: // bmm_Latn_MG
+        case 0xB58150474C61746ELLU: // bmn_Latn_PG
+        case 0xB981434D4C61746ELLU: // bmo_Latn_CM
+        case 0xBD8150474C61746ELLU: // bmp_Latn_PG
+        case 0xC1814D4C4C61746ELLU: // bmq_Latn_ML
+        case 0xC581434F4C61746ELLU: // bmr_Latn_CO
+        case 0xC9814E454C61746ELLU: // bms_Latn_NE
+        case 0xD18150474C61746ELLU: // bmu_Latn_PG
+        case 0xD581434D4C61746ELLU: // bmv_Latn_CM
+        case 0xD98143474C61746ELLU: // bmw_Latn_CG
+        case 0xDD8150474C61746ELLU: // bmx_Latn_PG
+        case 0xE58150474C61746ELLU: // bmz_Latn_PG
+        case 0x626E424442656E67LLU: // bn_Beng_BD
+        case 0x81A149444C61746ELLU: // bna_Latn_ID
+        case 0x85A14D594C61746ELLU: // bnb_Latn_MY
+        case 0x89A150484C61746ELLU: // bnc_Latn_PH
+        case 0x8DA149444C61746ELLU: // bnd_Latn_ID
+        case 0x91A149444C61746ELLU: // bne_Latn_ID
+        case 0x95A149444C61746ELLU: // bnf_Latn_ID
+        case 0x99A147514C61746ELLU: // bng_Latn_GQ
+        case 0xA1A143444C61746ELLU: // bni_Latn_CD
+        case 0xA5A150484C61746ELLU: // bnj_Latn_PH
+        case 0xA9A156554C61746ELLU: // bnk_Latn_VU
+        case 0xB1A147514C61746ELLU: // bnm_Latn_GQ
+        case 0xB5A154574C61746ELLU: // bnn_Latn_TW
+        case 0xB9A150484C61746ELLU: // bno_Latn_PH
+        case 0xBDA150474C61746ELLU: // bnp_Latn_PG
+        case 0xC1A149444C61746ELLU: // bnq_Latn_ID
+        case 0xC5A156554C61746ELLU: // bnr_Latn_VU
+        case 0xC9A1494E44657661LLU: // bns_Deva_IN
+        case 0xD1A149444C61746ELLU: // bnu_Latn_ID
+        case 0xD5A149444C61746ELLU: // bnv_Latn_ID
+        case 0xD9A150474C61746ELLU: // bnw_Latn_PG
+        case 0xDDA143444C61746ELLU: // bnx_Latn_CD
+        case 0xE1A14D594C61746ELLU: // bny_Latn_MY
+        case 0xE5A1434D4C61746ELLU: // bnz_Latn_CM
+        case 0x626F434E54696274LLU: // bo_Tibt_CN
+        case 0x81C150454C61746ELLU: // boa_Latn_PE
+        case 0x85C14B454C61746ELLU: // bob_Latn_KE
+        case 0x91C1434D4C61746ELLU: // boe_Latn_CM
+        case 0x95C142464C61746ELLU: // bof_Latn_BF
+        case 0x9DC143444C61746ELLU: // boh_Latn_CD
+        case 0xA5C150474C61746ELLU: // boj_Latn_PG
+        case 0xA9C143474C61746ELLU: // bok_Latn_CG
+        case 0xADC14E474C61746ELLU: // bol_Latn_NG
+        case 0xB1C14E474C61746ELLU: // bom_Latn_NG
+        case 0xB5C150474C61746ELLU: // bon_Latn_PG
+        case 0xB9C14D4C4C61746ELLU: // boo_Latn_ML
+        case 0xBDC150474C61746ELLU: // bop_Latn_PG
+        case 0xC1C150474C61746ELLU: // boq_Latn_PG
+        case 0xC5C142524C61746ELLU: // bor_Latn_BR
+        case 0xCDC153534C61746ELLU: // bot_Latn_SS
+        case 0xD1C1545A4C61746ELLU: // bou_Latn_TZ
+        case 0xD5C147484C61746ELLU: // bov_Latn_GH
+        case 0xD9C150474C61746ELLU: // bow_Latn_PG
+        case 0xDDC142464C61746ELLU: // box_Latn_BF
+        case 0xE1C143464C61746ELLU: // boy_Latn_CF
+        case 0xE5C14D4C4C61746ELLU: // boz_Latn_ML
+        case 0x81E156554C61746ELLU: // bpa_Latn_VU
+        case 0x89E1434D4C61746ELLU: // bpc_Latn_CM
+        case 0x8DE143464C61746ELLU: // bpd_Latn_CF
+        case 0x91E150474C61746ELLU: // bpe_Latn_PG
+        case 0x99E149444C61746ELLU: // bpg_Latn_ID
+        case 0x9DE152554379726CLLU: // bph_Cyrl_RU
+        case 0xA1E150474C61746ELLU: // bpi_Latn_PG
+        case 0xA5E143444C61746ELLU: // bpj_Latn_CD
+        case 0xA9E14E434C61746ELLU: // bpk_Latn_NC
+        case 0xADE141554C61746ELLU: // bpl_Latn_AU
+        case 0xB1E150474C61746ELLU: // bpm_Latn_PG
+        case 0xB9E149444C61746ELLU: // bpo_Latn_ID
+        case 0xBDE149444C61746ELLU: // bpp_Latn_ID
+        case 0xC1E149444C61746ELLU: // bpq_Latn_ID
+        case 0xC5E150484C61746ELLU: // bpr_Latn_PH
+        case 0xC9E150484C61746ELLU: // bps_Latn_PH
+        case 0xCDE141554C61746ELLU: // bpt_Latn_AU
+        case 0xD1E150474C61746ELLU: // bpu_Latn_PG
+        case 0xD5E149444C61746ELLU: // bpv_Latn_ID
+        case 0xD9E150474C61746ELLU: // bpw_Latn_PG
+        case 0xDDE1494E44657661LLU: // bpx_Deva_IN
+        case 0xE1E1494E42656E67LLU: // bpy_Beng_IN
+        case 0xE5E149444C61746ELLU: // bpz_Latn_ID
+        case 0x8201424A4C61746ELLU: // bqa_Latn_BJ
+        case 0x860149444C61746ELLU: // bqb_Latn_ID
+        case 0x8A01424A4C61746ELLU: // bqc_Latn_BJ
+        case 0x8E01434D4C61746ELLU: // bqd_Latn_CM
+        case 0x9601474E4C61746ELLU: // bqf_Latn_GN
+        case 0x9A0154474C61746ELLU: // bqg_Latn_TG
+        case 0xA201495241726162LLU: // bqi_Arab_IR
+        case 0xA601534E4C61746ELLU: // bqj_Latn_SN
+        case 0xAA0143464C61746ELLU: // bqk_Latn_CF
+        case 0xAE0150474C61746ELLU: // bql_Latn_PG
+        case 0xB201434D4C61746ELLU: // bqm_Latn_CM
+        case 0xBA01434D4C61746ELLU: // bqo_Latn_CM
+        case 0xBE014E474C61746ELLU: // bqp_Latn_NG
+        case 0xC20149444C61746ELLU: // bqq_Latn_ID
+        case 0xC60149444C61746ELLU: // bqr_Latn_ID
+        case 0xCA0150474C61746ELLU: // bqs_Latn_PG
+        case 0xCE01434D4C61746ELLU: // bqt_Latn_CM
+        case 0xD20143444C61746ELLU: // bqu_Latn_CD
+        case 0xD60143494C61746ELLU: // bqv_Latn_CI
+        case 0xDA014E474C61746ELLU: // bqw_Latn_NG
+        case 0xDE014E474C61746ELLU: // bqx_Latn_NG
+        case 0xE601434D4C61746ELLU: // bqz_Latn_CM
+        case 0x627246524C61746ELLU: // br_Latn_FR
+        case 0x8221494E44657661LLU: // bra_Deva_IN
+        case 0x86214B484B686D72LLU: // brb_Khmr_KH
+        case 0x8A2147594C61746ELLU: // brc_Latn_GY
+        case 0x8E214E5044657661LLU: // brd_Deva_NP
+        case 0x962143444C61746ELLU: // brf_Latn_CD
+        case 0x9A21424F4C61746ELLU: // brg_Latn_BO
+        case 0x9E21504B41726162LLU: // brh_Arab_PK
+        case 0xA221434D4C61746ELLU: // bri_Latn_CM
+        case 0xA62156554C61746ELLU: // brj_Latn_VU
+        case 0xAA21534441726162LLU: // brk_Arab_SD
+        case 0xAE2142574C61746ELLU: // brl_Latn_BW
+        case 0xB22143444C61746ELLU: // brm_Latn_CD
+        case 0xB62143524C61746ELLU: // brn_Latn_CR
+        case 0xBA21425454696274LLU: // bro_Tibt_BT
+        case 0xBE2149444C61746ELLU: // brp_Latn_ID
+        case 0xC22150474C61746ELLU: // brq_Latn_PG
+        case 0xC62153424C61746ELLU: // brr_Latn_SB
+        case 0xCA2149444C61746ELLU: // brs_Latn_ID
+        case 0xCE214E474C61746ELLU: // brt_Latn_NG
+        case 0xD221564E4C61746ELLU: // bru_Latn_VN
+        case 0xD6214C414C616F6FLLU: // brv_Laoo_LA
+        case 0xDA21494E4B6E6461LLU: // brw_Knda_IN
+        case 0xDE21494E44657661LLU: // brx_Deva_IN
+        case 0xE22150474C61746ELLU: // bry_Latn_PG
+        case 0xE62150474C61746ELLU: // brz_Latn_PG
+        case 0x627342414C61746ELLU: // bs_Latn_BA
+        case 0x824149444C61746ELLU: // bsa_Latn_ID
+        case 0x8641424E4C61746ELLU: // bsb_Latn_BN
+        case 0x8A41534E4C61746ELLU: // bsc_Latn_SN
+        case 0x9241434D4C61746ELLU: // bse_Latn_CM
+        case 0x96414E474C61746ELLU: // bsf_Latn_NG
+        case 0x9E41414641726162LLU: // bsh_Arab_AF
+        case 0xA241434D4C61746ELLU: // bsi_Latn_CM
+        case 0xA6414E474C61746ELLU: // bsj_Latn_NG
+        case 0xAA41504B41726162LLU: // bsk_Arab_PK
+        case 0xAE414E474C61746ELLU: // bsl_Latn_NG
+        case 0xB24149444C61746ELLU: // bsm_Latn_ID
+        case 0xB641434F4C61746ELLU: // bsn_Latn_CO
+        case 0xBA4154444C61746ELLU: // bso_Latn_TD
+        case 0xBE41474E4C61746ELLU: // bsp_Latn_GN
+        case 0xC2414C5242617373LLU: // bsq_Bass_LR
+        case 0xC6414E474C61746ELLU: // bsr_Latn_NG
+        case 0xCA41434D4C61746ELLU: // bss_Latn_CM
+        case 0xCE41455445746869LLU: // bst_Ethi_ET
+        case 0xD24149444C61746ELLU: // bsu_Latn_ID
+        case 0xD641474E4C61746ELLU: // bsv_Latn_GN
+        case 0xDA4145544C61746ELLU: // bsw_Latn_ET
+        case 0xDE414E474C61746ELLU: // bsx_Latn_NG
+        case 0xE2414D594C61746ELLU: // bsy_Latn_MY
+        case 0x82614E474C61746ELLU: // bta_Latn_NG
+        case 0x8A61434D4C61746ELLU: // btc_Latn_CM
+        case 0x8E6149444261746BLLU: // btd_Batk_ID
+        case 0x92614E474C61746ELLU: // bte_Latn_NG
+        case 0x966154444C61746ELLU: // btf_Latn_TD
+        case 0x9A6143494C61746ELLU: // btg_Latn_CI
+        case 0x9E614D594C61746ELLU: // bth_Latn_MY
+        case 0xA26149444C61746ELLU: // bti_Latn_ID
+        case 0xA66149444C61746ELLU: // btj_Latn_ID
+        case 0xB26149444261746BLLU: // btm_Batk_ID
+        case 0xB66150484C61746ELLU: // btn_Latn_PH
+        case 0xBA6150484C61746ELLU: // bto_Latn_PH
+        case 0xBE6150474C61746ELLU: // btp_Latn_PG
+        case 0xC2614D594C61746ELLU: // btq_Latn_MY
+        case 0xC66156554C61746ELLU: // btr_Latn_VU
+        case 0xCA6149444C61746ELLU: // bts_Latn_ID
+        case 0xCE614E474C61746ELLU: // btt_Latn_NG
+        case 0xD2614E474C61746ELLU: // btu_Latn_NG
+        case 0xD661504B44657661LLU: // btv_Deva_PK
+        case 0xDA6150484C61746ELLU: // btw_Latn_PH
+        case 0xDE6149444C61746ELLU: // btx_Latn_ID
+        case 0xE26149444C61746ELLU: // bty_Latn_ID
+        case 0xE66149444C61746ELLU: // btz_Latn_ID
+        case 0x828152554379726CLLU: // bua_Cyrl_RU
+        case 0x868154444C61746ELLU: // bub_Latn_TD
+        case 0x8A8159544C61746ELLU: // buc_Latn_YT
+        case 0x8E8154474C61746ELLU: // bud_Latn_TG
+        case 0x928143414C61746ELLU: // bue_Latn_CA
+        case 0x968143444C61746ELLU: // buf_Latn_CD
+        case 0x9A8149444C61746ELLU: // bug_Latn_ID
+        case 0x9E81434E4C61746ELLU: // buh_Latn_CN
+        case 0xA28143474C61746ELLU: // bui_Latn_CG
+        case 0xA6814E474C61746ELLU: // buj_Latn_NG
+        case 0xAA8150474C61746ELLU: // buk_Latn_PG
+        case 0xB281434D4C61746ELLU: // bum_Latn_CM
+        case 0xB681534C4C61746ELLU: // bun_Latn_SL
+        case 0xBA8150474C61746ELLU: // buo_Latn_PG
+        case 0xBE8149444C61746ELLU: // bup_Latn_ID
+        case 0xC28150474C61746ELLU: // buq_Latn_PG
+        case 0xCA814E474C61746ELLU: // bus_Latn_NG
+        case 0xCE8150474C61746ELLU: // but_Latn_PG
+        case 0xD28143444C61746ELLU: // buu_Latn_CD
+        case 0xD68150474C61746ELLU: // buv_Latn_PG
+        case 0xDA8147414C61746ELLU: // buw_Latn_GA
+        case 0xDE814E474C61746ELLU: // bux_Latn_NG
+        case 0xE281534C4C61746ELLU: // buy_Latn_SL
+        case 0xE6814E474C61746ELLU: // buz_Latn_NG
+        case 0x82A154444C61746ELLU: // bva_Latn_TD
+        case 0x86A147514C61746ELLU: // bvb_Latn_GQ
+        case 0x8AA153424C61746ELLU: // bvc_Latn_SB
+        case 0x8EA153424C61746ELLU: // bvd_Latn_SB
+        case 0x92A149444C61746ELLU: // bve_Latn_ID
+        case 0x96A154444C61746ELLU: // bvf_Latn_TD
+        case 0x9AA1434D4C61746ELLU: // bvg_Latn_CM
+        case 0x9EA14E474C61746ELLU: // bvh_Latn_NG
+        case 0xA2A153534C61746ELLU: // bvi_Latn_SS
+        case 0xA6A14E474C61746ELLU: // bvj_Latn_NG
+        case 0xAAA149444C61746ELLU: // bvk_Latn_ID
+        case 0xB2A1434D4C61746ELLU: // bvm_Latn_CM
+        case 0xB6A150474C61746ELLU: // bvn_Latn_PG
+        case 0xBAA154444C61746ELLU: // bvo_Latn_TD
+        case 0xC2A143464C61746ELLU: // bvq_Latn_CF
+        case 0xC6A141554C61746ELLU: // bvr_Latn_AU
+        case 0xCEA149444C61746ELLU: // bvt_Latn_ID
+        case 0xD2A149444C61746ELLU: // bvu_Latn_ID
+        case 0xD6A156454C61746ELLU: // bvv_Latn_VE
+        case 0xDAA14E474C61746ELLU: // bvw_Latn_NG
+        case 0xDEA143474C61746ELLU: // bvx_Latn_CG
+        case 0xE2A150484C61746ELLU: // bvy_Latn_PH
+        case 0xE6A149444C61746ELLU: // bvz_Latn_ID
+        case 0x82C14E434C61746ELLU: // bwa_Latn_NC
+        case 0x86C1464A4C61746ELLU: // bwb_Latn_FJ
+        case 0x8AC15A4D4C61746ELLU: // bwc_Latn_ZM
+        case 0x8EC150474C61746ELLU: // bwd_Latn_PG
+        case 0x92C14D4D4D796D72LLU: // bwe_Mymr_MM
+        case 0x96C150474C61746ELLU: // bwf_Latn_PG
+        case 0x9AC14D5A4C61746ELLU: // bwg_Latn_MZ
+        case 0x9EC1434D4C61746ELLU: // bwh_Latn_CM
+        case 0xA2C156454C61746ELLU: // bwi_Latn_VE
+        case 0xA6C142464C61746ELLU: // bwj_Latn_BF
+        case 0xAAC150474C61746ELLU: // bwk_Latn_PG
+        case 0xAEC143444C61746ELLU: // bwl_Latn_CD
+        case 0xB2C150474C61746ELLU: // bwm_Latn_PG
+        case 0xBAC145544C61746ELLU: // bwo_Latn_ET
+        case 0xBEC149444C61746ELLU: // bwp_Latn_ID
+        case 0xC2C142464C61746ELLU: // bwq_Latn_BF
+        case 0xC6C14E474C61746ELLU: // bwr_Latn_NG
+        case 0xCAC143444C61746ELLU: // bws_Latn_CD
+        case 0xCEC1434D4C61746ELLU: // bwt_Latn_CM
+        case 0xD2C147484C61746ELLU: // bwu_Latn_GH
+        case 0xDAC143444C61746ELLU: // bww_Latn_CD
+        case 0xDEC1434E4C61746ELLU: // bwx_Latn_CN
+        case 0xE2C142464C61746ELLU: // bwy_Latn_BF
+        case 0xE6C143474C61746ELLU: // bwz_Latn_CG
+        case 0x82E153424C61746ELLU: // bxa_Latn_SB
+        case 0x86E153534C61746ELLU: // bxb_Latn_SS
+        case 0x8AE147514C61746ELLU: // bxc_Latn_GQ
+        case 0x96E150474C61746ELLU: // bxf_Latn_PG
+        case 0x9AE143444C61746ELLU: // bxg_Latn_CD
+        case 0x9EE150474C61746ELLU: // bxh_Latn_PG
+        case 0xA2E141554C61746ELLU: // bxi_Latn_AU
+        case 0xA6E141554C61746ELLU: // bxj_Latn_AU
+        case 0xAEE142464C61746ELLU: // bxl_Latn_BF
+        case 0xB2E14D4E4379726CLLU: // bxm_Cyrl_MN
+        case 0xB6E141554C61746ELLU: // bxn_Latn_AU
+        case 0xBAE14E474C61746ELLU: // bxo_Latn_NG
+        case 0xBEE1434D4C61746ELLU: // bxp_Latn_CM
+        case 0xC2E14E474C61746ELLU: // bxq_Latn_NG
+        case 0xCAE1434D4C61746ELLU: // bxs_Latn_CM
+        case 0xD2E1434E4D6F6E67LLU: // bxu_Mong_CN
+        case 0xD6E154444C61746ELLU: // bxv_Latn_TD
+        case 0xDAE14D4C4C61746ELLU: // bxw_Latn_ML
+        case 0xE6E150474C61746ELLU: // bxz_Latn_PG
+        case 0x830150484C61746ELLU: // bya_Latn_PH
+        case 0x8701434D4C61746ELLU: // byb_Latn_CM
+        case 0x8B014E474C61746ELLU: // byc_Latn_NG
+        case 0x8F0149444C61746ELLU: // byd_Latn_ID
+        case 0x930150474C61746ELLU: // bye_Latn_PG
+        case 0x97014E474C61746ELLU: // byf_Latn_NG
+        case 0x9F014E5044657661LLU: // byh_Deva_NP
+        case 0xA30143444C61746ELLU: // byi_Latn_CD
+        case 0xA7014E474C61746ELLU: // byj_Latn_NG
+        case 0xAB01434E4C61746ELLU: // byk_Latn_CN
+        case 0xAF0149444C61746ELLU: // byl_Latn_ID
+        case 0xB30141554C61746ELLU: // bym_Latn_AU
+        case 0xB701455245746869LLU: // byn_Ethi_ER
+        case 0xBF014E474C61746ELLU: // byp_Latn_NG
+        case 0xC70150474C61746ELLU: // byr_Latn_PG
+        case 0xCB014E474C61746ELLU: // bys_Latn_NG
+        case 0xD701434D4C61746ELLU: // byv_Latn_CM
+        case 0xDB014E5044657661LLU: // byw_Deva_NP
+        case 0xDF0150474C61746ELLU: // byx_Latn_PG
+        case 0xE70150474C61746ELLU: // byz_Latn_PG
+        case 0x83214C524C61746ELLU: // bza_Latn_LR
+        case 0x872149444C61746ELLU: // bzb_Latn_ID
+        case 0x8B214D474C61746ELLU: // bzc_Latn_MG
+        case 0x8F2143524C61746ELLU: // bzd_Latn_CR
+        case 0x93214D4C4C61746ELLU: // bze_Latn_ML
+        case 0x972150474C61746ELLU: // bzf_Latn_PG
+        case 0x9F2150474C61746ELLU: // bzh_Latn_PG
+        case 0xA321544854686169LLU: // bzi_Thai_TH
+        case 0xA721425A4C61746ELLU: // bzj_Latn_BZ
+        case 0xAB214E494C61746ELLU: // bzk_Latn_NI
+        case 0xAF2149444C61746ELLU: // bzl_Latn_ID
+        case 0xB32143444C61746ELLU: // bzm_Latn_CD
+        case 0xB72149444C61746ELLU: // bzn_Latn_ID
+        case 0xBB2143444C61746ELLU: // bzo_Latn_CD
+        case 0xBF2149444C61746ELLU: // bzp_Latn_ID
+        case 0xC32149444C61746ELLU: // bzq_Latn_ID
+        case 0xC72141554C61746ELLU: // bzr_Latn_AU
+        case 0xD32149444C61746ELLU: // bzu_Latn_ID
+        case 0xD721434D4C61746ELLU: // bzv_Latn_CM
+        case 0xDB214E474C61746ELLU: // bzw_Latn_NG
+        case 0xDF214D4C4C61746ELLU: // bzx_Latn_ML
+        case 0xE3214E474C61746ELLU: // bzy_Latn_NG
+        case 0xE7214E474C61746ELLU: // bzz_Latn_NG
+        case 0x636145534C61746ELLU: // ca_Latn_ES
+        case 0x800247544C61746ELLU: // caa_Latn_GT
+        case 0x8402484E4C61746ELLU: // cab_Latn_HN
+        case 0x880247544C61746ELLU: // cac_Latn_GT
+        case 0x8C0255534C61746ELLU: // cad_Latn_US
+        case 0x9002534E4C61746ELLU: // cae_Latn_SN
+        case 0x940243414C61746ELLU: // caf_Latn_CA
+        case 0x980250594C61746ELLU: // cag_Latn_PY
+        case 0x9C0250454C61746ELLU: // cah_Latn_PE
+        case 0xA402424F4C61746ELLU: // caj_Latn_BO
+        case 0xA80247544C61746ELLU: // cak_Latn_GT
+        case 0xAC024D504C61746ELLU: // cal_Latn_MP
+        case 0xB0024E434C61746ELLU: // cam_Latn_NC
+        case 0xB40250474C61746ELLU: // can_Latn_PG
+        case 0xB802424F4C61746ELLU: // cao_Latn_BO
+        case 0xBC02424F4C61746ELLU: // cap_Latn_BO
+        case 0xC002494E4C61746ELLU: // caq_Latn_IN
+        case 0xC40256454C61746ELLU: // car_Latn_VE
+        case 0xC802424F4C61746ELLU: // cas_Latn_BO
+        case 0xD402424F4C61746ELLU: // cav_Latn_BO
+        case 0xD802424F4C61746ELLU: // caw_Latn_BO
+        case 0xDC02424F4C61746ELLU: // cax_Latn_BO
+        case 0xE00243414C61746ELLU: // cay_Latn_CA
+        case 0xE402424F4C61746ELLU: // caz_Latn_BO
+        case 0x8422434F4C61746ELLU: // cbb_Latn_CO
+        case 0x8822434F4C61746ELLU: // cbc_Latn_CO
+        case 0x8C22434F4C61746ELLU: // cbd_Latn_CO
+        case 0x9822434F4C61746ELLU: // cbg_Latn_CO
+        case 0xA02245434C61746ELLU: // cbi_Latn_EC
+        case 0xA422424A4C61746ELLU: // cbj_Latn_BJ
+        case 0xA82250484C61746ELLU: // cbk_Latn_PH
+        case 0xAC224D4D4C61746ELLU: // cbl_Latn_MM
+        case 0xB422544854686169LLU: // cbn_Thai_TH
+        case 0xB8224E474C61746ELLU: // cbo_Latn_NG
+        case 0xC0224E474C61746ELLU: // cbq_Latn_NG
+        case 0xC42250454C61746ELLU: // cbr_Latn_PE
+        case 0xC82250454C61746ELLU: // cbs_Latn_PE
+        case 0xCC2250454C61746ELLU: // cbt_Latn_PE
+        case 0xD02250454C61746ELLU: // cbu_Latn_PE
+        case 0xD422434F4C61746ELLU: // cbv_Latn_CO
+        case 0xD82250484C61746ELLU: // cbw_Latn_PH
+        case 0xE022434F4C61746ELLU: // cby_Latn_CO
+        case 0x884250454C61746ELLU: // ccc_Latn_PE
+        case 0x8C4242524C61746ELLU: // ccd_Latn_BR
+        case 0x90424D5A4C61746ELLU: // cce_Latn_MZ
+        case 0x98424E474C61746ELLU: // ccg_Latn_NG
+        case 0x9C424E474C61746ELLU: // cch_Latn_NG
+        case 0xA44247574C61746ELLU: // ccj_Latn_GW
+        case 0xAC42545A4C61746ELLU: // ccl_Latn_TZ
+        case 0xB0424D594C61746ELLU: // ccm_Latn_MY
+        case 0xB8424D584C61746ELLU: // cco_Latn_MX
+        case 0xBC42424443616B6DLLU: // ccp_Cakm_BD
+        case 0xC44253564C61746ELLU: // ccr_Latn_SV
+        case 0x9062494E54656C75LLU: // cde_Telu_IN
+        case 0x9462494E4C61746ELLU: // cdf_Latn_IN
+        case 0x9C62494E44657661LLU: // cdh_Deva_IN
+        case 0xA062494E47756A72LLU: // cdi_Gujr_IN
+        case 0xA462494E44657661LLU: // cdj_Deva_IN
+        case 0xB0624E5044657661LLU: // cdm_Deva_NP
+        case 0xB862434E48616E73LLU: // cdo_Hans_CN
+        case 0xC4624E474C61746ELLU: // cdr_Latn_NG
+        case 0xE462494E42656E67LLU: // cdz_Beng_IN
+        case 0x636552554379726CLLU: // ce_Cyrl_RU
+        case 0x808255534C61746ELLU: // cea_Latn_US
+        case 0x848250484C61746ELLU: // ceb_Latn_PH
+        case 0x988250594C61746ELLU: // ceg_Latn_PY
+        case 0xA8824D4D4C61746ELLU: // cek_Latn_MM
+        case 0xB4824E474C61746ELLU: // cen_Latn_NG
+        case 0xCC824E474C61746ELLU: // cet_Latn_NG
+        case 0xE0824D4D4C61746ELLU: // cey_Latn_MM
+        case 0x80A24E474C61746ELLU: // cfa_Latn_NG
+        case 0x8CA24E474C61746ELLU: // cfd_Latn_NG
+        case 0x98A24E474C61746ELLU: // cfg_Latn_NG
+        case 0xB0A24D4D4C61746ELLU: // cfm_Latn_MM
+        case 0x80C250474C61746ELLU: // cga_Latn_PG
+        case 0x88C250484C61746ELLU: // cgc_Latn_PH
+        case 0x98C255474C61746ELLU: // cgg_Latn_UG
+        case 0xA8C2425454696274LLU: // cgk_Tibt_BT
+        case 0x636847554C61746ELLU: // ch_Latn_GU
+        case 0x84E2434F4C61746ELLU: // chb_Latn_CO
+        case 0x8CE24D584C61746ELLU: // chd_Latn_MX
+        case 0x94E24D584C61746ELLU: // chf_Latn_MX
+        case 0x98E2544D41726162LLU: // chg_Arab_TM
+        case 0x9CE255534C61746ELLU: // chh_Latn_US
+        case 0xA4E24D584C61746ELLU: // chj_Latn_MX
+        case 0xA8E2464D4C61746ELLU: // chk_Latn_FM
+        case 0xACE255534C61746ELLU: // chl_Latn_US
+        case 0xB0E252554379726CLLU: // chm_Cyrl_RU
+        case 0xB4E255534C61746ELLU: // chn_Latn_US
+        case 0xB8E255534C61746ELLU: // cho_Latn_US
+        case 0xBCE243414C61746ELLU: // chp_Latn_CA
+        case 0xC0E24D584C61746ELLU: // chq_Latn_MX
+        case 0xC4E2555343686572LLU: // chr_Cher_US
+        case 0xCCE250454C61746ELLU: // cht_Latn_PE
+        case 0xD8E24D5A4C61746ELLU: // chw_Latn_MZ
+        case 0xDCE24E5044657661LLU: // chx_Deva_NP
+        case 0xE0E255534C61746ELLU: // chy_Latn_US
+        case 0xE4E24D584C61746ELLU: // chz_Latn_MX
+        case 0x810249444C61746ELLU: // cia_Latn_ID
+        case 0x8502424A4C61746ELLU: // cib_Latn_BJ
+        case 0x890255534C61746ELLU: // cic_Latn_US
+        case 0x91024E474C61746ELLU: // cie_Latn_NG
+        case 0x9D02494E44657661LLU: // cih_Deva_IN
+        case 0xB10249544C61746ELLU: // cim_Latn_IT
+        case 0xB50242524C61746ELLU: // cin_Latn_BR
+        case 0xBD024D584C61746ELLU: // cip_Latn_MX
+        case 0xC5024E434C61746ELLU: // cir_Latn_NC
+        case 0xD90255534C61746ELLU: // ciw_Latn_US
+        case 0xE10256454C61746ELLU: // ciy_Latn_VE
+        case 0x81224B4841726162LLU: // cja_Arab_KH
+        case 0x9122564E4C61746ELLU: // cje_Latn_VN
+        case 0x9D2255534C61746ELLU: // cjh_Latn_US
+        case 0xA12252554379726CLLU: // cji_Cyrl_RU
+        case 0xA922414F4C61746ELLU: // cjk_Latn_AO
+        case 0xB122564E4368616DLLU: // cjm_Cham_VN
+        case 0xB52250474C61746ELLU: // cjn_Latn_PG
+        case 0xB92250454C61746ELLU: // cjo_Latn_PE
+        case 0xBD2243524C61746ELLU: // cjp_Latn_CR
+        case 0xC92252554C61746ELLU: // cjs_Latn_RU
+        case 0xD52250474C61746ELLU: // cjv_Latn_PG
+        case 0xE122434E48616E73LLU: // cjy_Hans_CN
+        case 0x8542495141726162LLU: // ckb_Arab_IQ
+        case 0xAD424E474C61746ELLU: // ckl_Latn_NG
+        case 0xB14248524C61746ELLU: // ckm_Latn_HR
+        case 0xB5424D4D4C61746ELLU: // ckn_Latn_MM
+        case 0xB94247484C61746ELLU: // cko_Latn_GH
+        case 0xC14254444C61746ELLU: // ckq_Latn_TD
+        case 0xC54250474C61746ELLU: // ckr_Latn_PG
+        case 0xC9424E434C61746ELLU: // cks_Latn_NC
+        case 0xCD4252554379726CLLU: // ckt_Cyrl_RU
+        case 0xD14255534C61746ELLU: // cku_Latn_US
+        case 0xD54254574C61746ELLU: // ckv_Latn_TW
+        case 0xDD42434D4C61746ELLU: // ckx_Latn_CM
+        case 0xE1424E474C61746ELLU: // cky_Latn_NG
+        case 0xE54247544C61746ELLU: // ckz_Latn_GT
+        case 0x81624E474C61746ELLU: // cla_Latn_NG
+        case 0x896243414C61746ELLU: // clc_Latn_CA
+        case 0x91624D584C61746ELLU: // cle_Latn_MX
+        case 0x9D62504B41726162LLU: // clh_Arab_PK
+        case 0xA16247484C61746ELLU: // cli_Latn_GH
+        case 0xA5624D4D4C61746ELLU: // clj_Latn_MM
+        case 0xA962494E4C61746ELLU: // clk_Latn_IN
+        case 0xAD6247484C61746ELLU: // cll_Latn_GH
+        case 0xB16255534C61746ELLU: // clm_Latn_US
+        case 0xB9624D584C61746ELLU: // clo_Latn_MX
+        case 0xCD624D4D4C61746ELLU: // clt_Latn_MM
+        case 0xD16250484C61746ELLU: // clu_Latn_PH
+        case 0xD96252554379726CLLU: // clw_Cyrl_RU
+        case 0xE1624D584C61746ELLU: // cly_Latn_MX
+        case 0x8182564E4C61746ELLU: // cma_Latn_VN
+        case 0x918242464C61746ELLU: // cme_Latn_BF
+        case 0x99824D4E536F796FLLU: // cmg_Soyo_MN
+        case 0xA182434F4C61746ELLU: // cmi_Latn_CO
+        case 0xAD8249444C61746ELLU: // cml_Latn_ID
+        case 0xB982564E4C61746ELLU: // cmo_Latn_VN
+        case 0xC5824D4D4C61746ELLU: // cmr_Latn_MM
+        case 0xC98249544C61746ELLU: // cms_Latn_IT
+        case 0xCD825A414C61746ELLU: // cmt_Latn_ZA
+        case 0x81A2494E54696274LLU: // cna_Tibt_IN
+        case 0x85A24D4D4C61746ELLU: // cnb_Latn_MM
+        case 0x89A2564E4C61746ELLU: // cnc_Latn_VN
+        case 0x99A2434E4C61746ELLU: // cng_Latn_CN
+        case 0x9DA24D4D4C61746ELLU: // cnh_Latn_MM
+        case 0xA1A250454C61746ELLU: // cni_Latn_PE
+        case 0xA9A24D4D4C61746ELLU: // cnk_Latn_MM
+        case 0xADA24D584C61746ELLU: // cnl_Latn_MX
+        case 0xBDA2434E48616E73LLU: // cnp_Hans_CN
+        case 0xC1A2434D4C61746ELLU: // cnq_Latn_CM
+        case 0xC9A249444C61746ELLU: // cns_Latn_ID
+        case 0xCDA24D584C61746ELLU: // cnt_Latn_MX
+        case 0xD9A24D4D4C61746ELLU: // cnw_Latn_MM
+        case 0xDDA247424C61746ELLU: // cnx_Latn_GB
+        case 0x636F46524C61746ELLU: // co_Latn_FR
+        case 0x81C241554C61746ELLU: // coa_Latn_AU
+        case 0x85C24D584C61746ELLU: // cob_Latn_MX
+        case 0x89C24D584C61746ELLU: // coc_Latn_MX
+        case 0x8DC250454C61746ELLU: // cod_Latn_PE
+        case 0x91C2434F4C61746ELLU: // coe_Latn_CO
+        case 0x95C245434C61746ELLU: // cof_Latn_EC
+        case 0x99C2544854686169LLU: // cog_Thai_TH
+        case 0x9DC24B454C61746ELLU: // coh_Latn_KE
+        case 0xA5C24D584C61746ELLU: // coj_Latn_MX
+        case 0xA9C24D584C61746ELLU: // cok_Latn_MX
+        case 0xADC255534C61746ELLU: // col_Latn_US
+        case 0xB1C255534C61746ELLU: // com_Latn_US
+        case 0xB9C243414C61746ELLU: // coo_Latn_CA
+        case 0xBDC24547436F7074LLU: // cop_Copt_EG
+        case 0xC1C255534C61746ELLU: // coq_Latn_US
+        case 0xCDC250454C61746ELLU: // cot_Latn_PE
+        case 0xD1C2534E4C61746ELLU: // cou_Latn_SN
+        case 0xDDC250454C61746ELLU: // cox_Latn_PE
+        case 0xE5C24D584C61746ELLU: // coz_Latn_MX
+        case 0x81E24D584C61746ELLU: // cpa_Latn_MX
+        case 0x85E250454C61746ELLU: // cpb_Latn_PE
+        case 0x89E250454C61746ELLU: // cpc_Latn_PE
+        case 0x99E247524772656BLLU: // cpg_Grek_GR
+        case 0xA1E24E524C61746ELLU: // cpi_Latn_NR
+        case 0xB5E247484C61746ELLU: // cpn_Latn_GH
+        case 0xB9E242464C61746ELLU: // cpo_Latn_BF
+        case 0xC9E250484C61746ELLU: // cps_Latn_PH
+        case 0xD1E250454C61746ELLU: // cpu_Latn_PE
+        case 0xDDE2434E4C61746ELLU: // cpx_Latn_CN
+        case 0xE1E250454C61746ELLU: // cpy_Latn_PE
+        case 0x8E02434E4C61746ELLU: // cqd_Latn_CN
+        case 0x6372434143616E73LLU: // cr_Cans_CA
+        case 0x822245544C61746ELLU: // cra_Latn_ET
+        case 0x862256434C61746ELLU: // crb_Latn_VC
+        case 0x8A2256554C61746ELLU: // crc_Latn_VU
+        case 0x8E2255534C61746ELLU: // crd_Latn_US
+        case 0x9622434F4C61746ELLU: // crf_Latn_CO
+        case 0x9A2243414C61746ELLU: // crg_Latn_CA
+        case 0x9E2255414379726CLLU: // crh_Cyrl_UA
+        case 0xA22253544C61746ELLU: // cri_Latn_ST
+        case 0xA622434143616E73LLU: // crj_Cans_CA
+        case 0xAA22434143616E73LLU: // crk_Cans_CA
+        case 0xAE22434143616E73LLU: // crl_Cans_CA
+        case 0xB222434143616E73LLU: // crm_Cans_CA
+        case 0xB6224D584C61746ELLU: // crn_Latn_MX
+        case 0xBA2255534C61746ELLU: // cro_Latn_US
+        case 0xC22241524C61746ELLU: // crq_Latn_AR
+        case 0xCA2253434C61746ELLU: // crs_Latn_SC
+        case 0xCE2241524C61746ELLU: // crt_Latn_AR
+        case 0xD622494E4C61746ELLU: // crv_Latn_IN
+        case 0xDA22564E4C61746ELLU: // crw_Latn_VN
+        case 0xDE2243414C61746ELLU: // crx_Latn_CA
+        case 0xE2224E474C61746ELLU: // cry_Latn_NG
+        case 0xE62255534C61746ELLU: // crz_Latn_US
+        case 0x6373435A4C61746ELLU: // cs_Latn_CZ
+        case 0x82424D584C61746ELLU: // csa_Latn_MX
+        case 0x8642504C4C61746ELLU: // csb_Latn_PL
+        case 0x9E424D4D4D796D72LLU: // csh_Mymr_MM
+        case 0xA6424D4D4C61746ELLU: // csj_Latn_MM
+        case 0xAA42534E4C61746ELLU: // csk_Latn_SN
+        case 0xB24255534C61746ELLU: // csm_Latn_US
+        case 0xBA424D584C61746ELLU: // cso_Latn_MX
+        case 0xBE42434E48616E73LLU: // csp_Hans_CN
+        case 0xCA4255534C61746ELLU: // css_Latn_US
+        case 0xCE4255534C61746ELLU: // cst_Latn_US
+        case 0xD6424D4D4C61746ELLU: // csv_Latn_MM
+        case 0xDA42434143616E73LLU: // csw_Cans_CA
+        case 0xE2424D4D4C61746ELLU: // csy_Latn_MM
+        case 0xE64255534C61746ELLU: // csz_Latn_US
+        case 0x82624D584C61746ELLU: // cta_Latn_MX
+        case 0x8A6255534C61746ELLU: // ctc_Latn_US
+        case 0x8E624D4D50617563LLU: // ctd_Pauc_MM
+        case 0x92624D584C61746ELLU: // cte_Latn_MX
+        case 0x9A62424442656E67LLU: // ctg_Beng_BD
+        case 0x9E624D4D4C61746ELLU: // cth_Latn_MM
+        case 0xAE624D584C61746ELLU: // ctl_Latn_MX
+        case 0xB26255534C61746ELLU: // ctm_Latn_US
+        case 0xB6624E5044657661LLU: // ctn_Deva_NP
+        case 0xBA62434F4C61746ELLU: // cto_Latn_CO
+        case 0xBE624D584C61746ELLU: // ctp_Latn_MX
+        case 0xCA6250484C61746ELLU: // cts_Latn_PH
+        case 0xCE62494E54616D6CLLU: // ctt_Taml_IN
+        case 0xD2624D584C61746ELLU: // ctu_Latn_MX
+        case 0xE262494E54616D6CLLU: // cty_Taml_IN
+        case 0xE6624D584C61746ELLU: // ctz_Latn_MX
+        case 0x637552554379726CLLU: // cu_Cyrl_RU
+        case 0x63754247476C6167LLU: // cu_Glag_BG
+        case 0x8282564E4C61746ELLU: // cua_Latn_VN
+        case 0x8682434F4C61746ELLU: // cub_Latn_CO
+        case 0x8A824D584C61746ELLU: // cuc_Latn_MX
+        case 0x9E824B454C61746ELLU: // cuh_Latn_KE
+        case 0xA282434F4C61746ELLU: // cui_Latn_CO
+        case 0xA68250454C61746ELLU: // cuj_Latn_PE
+        case 0xAA8250414C61746ELLU: // cuk_Latn_PA
+        case 0xAE8242524C61746ELLU: // cul_Latn_BR
+        case 0xBA8256454C61746ELLU: // cuo_Latn_VE
+        case 0xBE8255534C61746ELLU: // cup_Latn_US
+        case 0xCE824D584C61746ELLU: // cut_Latn_MX
+        case 0xD282434E4C616E61LLU: // cuu_Lana_CN
+        case 0xD682434D4C61746ELLU: // cuv_Latn_CM
+        case 0xDE824D584C61746ELLU: // cux_Latn_MX
+        case 0xE2824D584C61746ELLU: // cuy_Latn_MX
+        case 0x637652554379726CLLU: // cv_Cyrl_RU
+        case 0x9AA2494E4C61746ELLU: // cvg_Latn_IN
+        case 0xB6A24D584C61746ELLU: // cvn_Latn_MX
+        case 0x82C2545A4C61746ELLU: // cwa_Latn_TZ
+        case 0x86C24D5A4C61746ELLU: // cwb_Latn_MZ
+        case 0x92C2545A4C61746ELLU: // cwe_Latn_TZ
+        case 0x9AC24D594C61746ELLU: // cwg_Latn_MY
+        case 0xCEC2534E4C61746ELLU: // cwt_Latn_SN
+        case 0x9EE24E474C61746ELLU: // cxh_Latn_NG
+        case 0x637947424C61746ELLU: // cy_Latn_GB
+        case 0x83024D584C61746ELLU: // cya_Latn_MX
+        case 0x8702424F4C61746ELLU: // cyb_Latn_BO
+        case 0xBB0250484C61746ELLU: // cyo_Latn_PH
+        case 0x9F22434E48616E73LLU: // czh_Hans_CN
+        case 0xAB22435A48656272LLU: // czk_Hebr_CZ
+        case 0xB7224D584C61746ELLU: // czn_Latn_MX
+        case 0xCF224D4D4C61746ELLU: // czt_Latn_MM
+        case 0x6461444B4C61746ELLU: // da_Latn_DK
+        case 0x800354444C61746ELLU: // daa_Latn_TD
+        case 0x880350474C61746ELLU: // dac_Latn_PG
+        case 0x8C0350474C61746ELLU: // dad_Latn_PG
+        case 0x9003434D4C61746ELLU: // dae_Latn_CM
+        case 0x980347484C61746ELLU: // dag_Latn_GH
+        case 0x9C0350474C61746ELLU: // dah_Latn_PG
+        case 0xA00354444C61746ELLU: // dai_Latn_TD
+        case 0xA40353444C61746ELLU: // daj_Latn_SD
+        case 0xA80355534C61746ELLU: // dak_Latn_US
+        case 0xAC034B454C61746ELLU: // dal_Latn_KE
+        case 0xB0034E474C61746ELLU: // dam_Latn_NG
+        case 0xB8034D4D4C61746ELLU: // dao_Latn_MM
+        case 0xC003494E44657661LLU: // daq_Deva_IN
+        case 0xC40352554379726CLLU: // dar_Cyrl_RU
+        case 0xC80343494C61746ELLU: // das_Latn_CI
+        case 0xD00354444C61746ELLU: // dau_Latn_TD
+        case 0xD4034B454C61746ELLU: // dav_Latn_KE
+        case 0xD80350484C61746ELLU: // daw_Latn_PH
+        case 0xDC0341554C61746ELLU: // dax_Latn_AU
+        case 0xE40349444C61746ELLU: // daz_Latn_ID
+        case 0x80234D4C4C61746ELLU: // dba_Latn_ML
+        case 0x84234E474C61746ELLU: // dbb_Latn_NG
+        case 0x8C234E474C61746ELLU: // dbd_Latn_NG
+        case 0x902349444C61746ELLU: // dbe_Latn_ID
+        case 0x942349444C61746ELLU: // dbf_Latn_ID
+        case 0x98234D4C4C61746ELLU: // dbg_Latn_ML
+        case 0xA0234E474C61746ELLU: // dbi_Latn_NG
+        case 0xA4234D594C61746ELLU: // dbj_Latn_MY
+        case 0xAC2341554C61746ELLU: // dbl_Latn_AU
+        case 0xB0234E474C61746ELLU: // dbm_Latn_NG
+        case 0xB42349444C61746ELLU: // dbn_Latn_ID
+        case 0xB8234E474C61746ELLU: // dbo_Latn_NG
+        case 0xBC234E474C61746ELLU: // dbp_Latn_NG
+        case 0xC023434D4C61746ELLU: // dbq_Latn_CM
+        case 0xCC234D4C4C61746ELLU: // dbt_Latn_ML
+        case 0xD0234D4C4C61746ELLU: // dbu_Latn_ML
+        case 0xD4234E474C61746ELLU: // dbv_Latn_NG
+        case 0xD8234D4C4C61746ELLU: // dbw_Latn_ML
+        case 0xE02350474C61746ELLU: // dby_Latn_PG
+        case 0x8843494E41726162LLU: // dcc_Arab_IN
+        case 0xC44356494C61746ELLU: // dcr_Latn_VI
+        case 0x806341554C61746ELLU: // dda_Latn_AU
+        case 0x8C6353534C61746ELLU: // ddd_Latn_SS
+        case 0x906343474C61746ELLU: // dde_Latn_CG
+        case 0x9863544C4C61746ELLU: // ddg_Latn_TL
+        case 0xA06350474C61746ELLU: // ddi_Latn_PG
+        case 0xA46341554C61746ELLU: // ddj_Latn_AU
+        case 0xB463424A4C61746ELLU: // ddn_Latn_BJ
+        case 0xB86352554379726CLLU: // ddo_Cyrl_RU
+        case 0xC46341554C61746ELLU: // ddr_Latn_AU
+        case 0xC8634D4C4C61746ELLU: // dds_Latn_ML
+        case 0xD86349444C61746ELLU: // ddw_Latn_ID
+        case 0x646544454C61746ELLU: // de_Latn_DE
+        case 0x888353444C61746ELLU: // dec_Latn_SD
+        case 0x8C8350474C61746ELLU: // ded_Latn_PG
+        case 0x90834C524C61746ELLU: // dee_Latn_LR
+        case 0x9483495241726162LLU: // def_Arab_IR
+        case 0x98834E474C61746ELLU: // deg_Latn_NG
+        case 0x9C83504B41726162LLU: // deh_Arab_PK
+        case 0xA08349444C61746ELLU: // dei_Latn_ID
+        case 0xA883434D4C61746ELLU: // dek_Latn_CM
+        case 0xAC8355534C61746ELLU: // del_Latn_US
+        case 0xB08349444C61746ELLU: // dem_Latn_ID
+        case 0xB48343414C61746ELLU: // den_Latn_CA
+        case 0xC08343464C61746ELLU: // deq_Latn_CF
+        case 0xC483494E42656E67LLU: // der_Beng_IN
+        case 0xC88342524C61746ELLU: // des_Latn_BR
+        case 0xD48350474C61746ELLU: // dev_Latn_PG
+        case 0xE48343444C61746ELLU: // dez_Latn_CD
+        case 0x80C347484C61746ELLU: // dga_Latn_GH
+        case 0x84C34D4C4C61746ELLU: // dgb_Latn_ML
+        case 0x88C350484C61746ELLU: // dgc_Latn_PH
+        case 0x8CC342464C61746ELLU: // dgd_Latn_BF
+        case 0x90C350474C61746ELLU: // dge_Latn_PG
+        case 0x98C350474C61746ELLU: // dgg_Latn_PG
+        case 0x9CC34E474C61746ELLU: // dgh_Latn_NG
+        case 0xA0C342464C61746ELLU: // dgi_Latn_BF
+        case 0xA8C343464C61746ELLU: // dgk_Latn_CF
+        case 0xACC3534441726162LLU: // dgl_Arab_SD
+        case 0xB4C341554C61746ELLU: // dgn_Latn_AU
+        case 0xC4C343414C61746ELLU: // dgr_Latn_CA
+        case 0xC8C342464C61746ELLU: // dgs_Latn_BF
+        case 0xCCC341554C61746ELLU: // dgt_Latn_AU
+        case 0xD8C341554C61746ELLU: // dgw_Latn_AU
+        case 0xDCC350474C61746ELLU: // dgx_Latn_PG
+        case 0xE4C350474C61746ELLU: // dgz_Latn_PG
+        case 0x98E341554C61746ELLU: // dhg_Latn_AU
+        case 0xA0E34E5044657661LLU: // dhi_Deva_NP
+        case 0xACE341554C61746ELLU: // dhl_Latn_AU
+        case 0xB0E3414F4C61746ELLU: // dhm_Latn_AO
+        case 0xB4E3494E47756A72LLU: // dhn_Gujr_IN
+        case 0xB8E3494E44657661LLU: // dho_Deva_IN
+        case 0xC4E341554C61746ELLU: // dhr_Latn_AU
+        case 0xC8E3545A4C61746ELLU: // dhs_Latn_TZ
+        case 0xD0E341554C61746ELLU: // dhu_Latn_AU
+        case 0xD4E34E434C61746ELLU: // dhv_Latn_NC
+        case 0xD8E34E5044657661LLU: // dhw_Deva_NP
+        case 0xDCE341554C61746ELLU: // dhx_Latn_AU
+        case 0x810350474C61746ELLU: // dia_Latn_PG
+        case 0x850353534C61746ELLU: // dib_Latn_SS
+        case 0x890343494C61746ELLU: // dic_Latn_CI
+        case 0x8D0353534C61746ELLU: // did_Latn_SS
+        case 0x950341554C61746ELLU: // dif_Latn_AU
+        case 0x99034B454C61746ELLU: // dig_Latn_KE
+        case 0x9D034D584C61746ELLU: // dih_Latn_MX
+        case 0xA103434D4C61746ELLU: // dii_Latn_CM
+        case 0xA50349444C61746ELLU: // dij_Latn_ID
+        case 0xAD0353444C61746ELLU: // dil_Latn_SD
+        case 0xB50353534C61746ELLU: // din_Latn_SS
+        case 0xB9034E474C61746ELLU: // dio_Latn_NG
+        case 0xBD0353534C61746ELLU: // dip_Latn_SS
+        case 0xC5034E474C61746ELLU: // dir_Latn_NG
+        case 0xC903494E4C61746ELLU: // dis_Latn_IN
+        case 0xD1034E414C61746ELLU: // diu_Latn_NA
+        case 0xD90353534C61746ELLU: // diw_Latn_SS
+        case 0xDD0356554C61746ELLU: // dix_Latn_VU
+        case 0xE10349444C61746ELLU: // diy_Latn_ID
+        case 0xE50343444C61746ELLU: // diz_Latn_CD
+        case 0x812341554C61746ELLU: // dja_Latn_AU
+        case 0x852341554C61746ELLU: // djb_Latn_AU
+        case 0x892354444C61746ELLU: // djc_Latn_TD
+        case 0x8D2341554C61746ELLU: // djd_Latn_AU
+        case 0x91234E454C61746ELLU: // dje_Latn_NE
+        case 0x952341554C61746ELLU: // djf_Latn_AU
+        case 0xA12341554C61746ELLU: // dji_Latn_AU
+        case 0xA52341554C61746ELLU: // djj_Latn_AU
+        case 0xA92353524C61746ELLU: // djk_Latn_SR
+        case 0xB1234D4C4C61746ELLU: // djm_Latn_ML
+        case 0xB52341554C61746ELLU: // djn_Latn_AU
+        case 0xB92349444C61746ELLU: // djo_Latn_ID
+        case 0xC52341554C61746ELLU: // djr_Latn_AU
+        case 0xD12350474C61746ELLU: // dju_Latn_PG
+        case 0xD92341554C61746ELLU: // djw_Latn_AU
+        case 0x8143425454696274LLU: // dka_Tibt_BT
+        case 0x99434E474C61746ELLU: // dkg_Latn_NG
+        case 0xA94349444C61746ELLU: // dkk_Latn_ID
+        case 0xC5434D594C61746ELLU: // dkr_Latn_MY
+        case 0xC94353534C61746ELLU: // dks_Latn_SS
+        case 0xDD43434D4C61746ELLU: // dkx_Latn_CM
+        case 0x996352554379726CLLU: // dlg_Cyrl_RU
+        case 0xB16348524C61746ELLU: // dlm_Latn_HR
+        case 0xB563494E4C61746ELLU: // dln_Latn_IN
+        case 0x818347414C61746ELLU: // dma_Latn_GA
+        case 0x85834D4C4C61746ELLU: // dmb_Latn_ML
+        case 0x898350474C61746ELLU: // dmc_Latn_PG
+        case 0x8D8341554C61746ELLU: // dmd_Latn_AU
+        case 0x9183434D4C61746ELLU: // dme_Latn_CM
+        case 0x95834E474D656466LLU: // dmf_Medf_NG
+        case 0x99834D594C61746ELLU: // dmg_Latn_MY
+        case 0xA983504B41726162LLU: // dmk_Arab_PK
+        case 0xAD83504B41726162LLU: // dml_Arab_PK
+        case 0xB183434D4C61746ELLU: // dmm_Latn_CM
+        case 0xB983434D4C61746ELLU: // dmo_Latn_CM
+        case 0xC58349444C61746ELLU: // dmr_Latn_ID
+        case 0xC98349444C61746ELLU: // dms_Latn_ID
+        case 0xD18349444C61746ELLU: // dmu_Latn_ID
+        case 0xD5834D594C61746ELLU: // dmv_Latn_MY
+        case 0xD98341554C61746ELLU: // dmw_Latn_AU
+        case 0xDD834D5A4C61746ELLU: // dmx_Latn_MZ
+        case 0xE18349444C61746ELLU: // dmy_Latn_ID
+        case 0x81A349444C61746ELLU: // dna_Latn_ID
+        case 0x8DA350474C61746ELLU: // dnd_Latn_PG
+        case 0x91A3545A4C61746ELLU: // dne_Latn_TZ
+        case 0x99A34B474379726CLLU: // dng_Cyrl_KG
+        case 0xA1A349444C61746ELLU: // dni_Latn_ID
+        case 0xA5A343494C61746ELLU: // dnj_Latn_CI
+        case 0xA9A349444C61746ELLU: // dnk_Latn_ID
+        case 0xB5A342464C61746ELLU: // dnn_Latn_BF
+        case 0xB9A343444C61746ELLU: // dno_Latn_CD
+        case 0xC5A350474C61746ELLU: // dnr_Latn_PG
+        case 0xCDA349444C61746ELLU: // dnt_Latn_ID
+        case 0xD1A34D4D4D796D72LLU: // dnu_Mymr_MM
+        case 0xD5A34D4D4D796D72LLU: // dnv_Mymr_MM
+        case 0xD9A349444C61746ELLU: // dnw_Latn_ID
+        case 0xE1A342524C61746ELLU: // dny_Latn_BR
+        case 0x81C350474C61746ELLU: // doa_Latn_PG
+        case 0x85C350474C61746ELLU: // dob_Latn_PG
+        case 0x89C3434E4C61746ELLU: // doc_Latn_CN
+        case 0x91C3545A4C61746ELLU: // doe_Latn_TZ
+        case 0x95C350474C61746ELLU: // dof_Latn_PG
+        case 0x9DC34E474C61746ELLU: // doh_Latn_NG
+        case 0xA1C3494E44657661LLU: // doi_Deva_IN
+        case 0xA9C349444C61746ELLU: // dok_Latn_ID
+        case 0xADC350474C61746ELLU: // dol_Latn_PG
+        case 0xB5C350474C61746ELLU: // don_Latn_PG
+        case 0xB9C343444C61746ELLU: // doo_Latn_CD
+        case 0xBDC3424A4C61746ELLU: // dop_Latn_BJ
+        case 0xC5C353424C61746ELLU: // dor_Latn_SB
+        case 0xC9C342464C61746ELLU: // dos_Latn_BF
+        case 0xCDC34E474C61746ELLU: // dot_Latn_NG
+        case 0xD5C35A574C61746ELLU: // dov_Latn_ZW
+        case 0xD9C3434D4C61746ELLU: // dow_Latn_CM
+        case 0xDDC3455445746869LLU: // dox_Ethi_ET
+        case 0xE1C347484C61746ELLU: // doy_Latn_GH
+        case 0xBDE34D594C61746ELLU: // dpp_Latn_MY
+        case 0x8A2350544C61746ELLU: // drc_Latn_PT
+        case 0x92234E5054696274LLU: // dre_Tibt_NP
+        case 0x9A234D594C61746ELLU: // drg_Latn_MY
+        case 0xA2234E474C61746ELLU: // dri_Latn_NG
+        case 0xAE2341554C61746ELLU: // drl_Latn_AU
+        case 0xB62349444C61746ELLU: // drn_Latn_ID
+        case 0xBA234D594C61746ELLU: // dro_Latn_MY
+        case 0xC2234E5044657661LLU: // drq_Deva_NP
+        case 0xCA23455445746869LLU: // drs_Ethi_ET
+        case 0xCE234E4C4C61746ELLU: // drt_Latn_NL
+        case 0xD22354574C61746ELLU: // dru_Latn_TW
+        case 0xE2234E5044657661LLU: // dry_Deva_NP
+        case 0x864344454C61746ELLU: // dsb_Latn_DE
+        case 0x9E434B454C61746ELLU: // dsh_Latn_KE
+        case 0xA24354444C61746ELLU: // dsi_Latn_TD
+        case 0xAA434E474C61746ELLU: // dsk_Latn_NG
+        case 0xB64349444C61746ELLU: // dsn_Latn_ID
+        case 0xBA43494E4F727961LLU: // dso_Orya_IN
+        case 0xC2434D4C4C61746ELLU: // dsq_Latn_ML
+        case 0x8263434E4C61746ELLU: // dta_Latn_CN
+        case 0x86634D594C61746ELLU: // dtb_Latn_MY
+        case 0x8E6343414C61746ELLU: // dtd_Latn_CA
+        case 0x9E6341554C61746ELLU: // dth_Latn_AU
+        case 0xA2634D4C4C61746ELLU: // dti_Latn_ML
+        case 0xAA634D4C4C61746ELLU: // dtk_Latn_ML
+        case 0xB2634D4C4C61746ELLU: // dtm_Latn_ML
+        case 0xBA634D4C4C61746ELLU: // dto_Latn_ML
+        case 0xBE634D594C61746ELLU: // dtp_Latn_MY
+        case 0xC6634D594C61746ELLU: // dtr_Latn_MY
+        case 0xCA634D4C4C61746ELLU: // dts_Latn_ML
+        case 0xCE634D4C4C61746ELLU: // dtt_Latn_ML
+        case 0xD2634D4C4C61746ELLU: // dtu_Latn_ML
+        case 0xE2634E5044657661LLU: // dty_Deva_NP
+        case 0x8283434D4C61746ELLU: // dua_Latn_CM
+        case 0x8683494E47756A72LLU: // dub_Gujr_IN
+        case 0x8A8350474C61746ELLU: // duc_Latn_PG
+        case 0x928350484C61746ELLU: // due_Latn_PH
+        case 0x96834E434C61746ELLU: // duf_Latn_NC
+        case 0x9A834B454C61746ELLU: // dug_Latn_KE
+        case 0x9E83494E44657661LLU: // duh_Deva_IN
+        case 0xA28350474C61746ELLU: // dui_Latn_PG
+        case 0xAA8350474C61746ELLU: // duk_Latn_PG
+        case 0xAE8350484C61746ELLU: // dul_Latn_PH
+        case 0xB2834E4C4C61746ELLU: // dum_Latn_NL
+        case 0xB68349444C61746ELLU: // dun_Latn_ID
+        case 0xBA8350484C61746ELLU: // duo_Latn_PH
+        case 0xBE8349444C61746ELLU: // dup_Latn_ID
+        case 0xC28349444C61746ELLU: // duq_Latn_ID
+        case 0xC683434D4C61746ELLU: // dur_Latn_CM
+        case 0xCA834E5044657661LLU: // dus_Deva_NP
+        case 0xD283434E4C61746ELLU: // duu_Latn_CN
+        case 0xD68349444C61746ELLU: // duv_Latn_ID
+        case 0xDA8349444C61746ELLU: // duw_Latn_ID
+        case 0xDE834D4C4C61746ELLU: // dux_Latn_ML
+        case 0xE28350484C61746ELLU: // duy_Latn_PH
+        case 0xE683434D4C61746ELLU: // duz_Latn_CM
+        case 0x64764D5654686161LLU: // dv_Thaa_MV
+        case 0x82A350474C61746ELLU: // dva_Latn_PG
+        case 0x82C34E474C61746ELLU: // dwa_Latn_NG
+        case 0xAAC3494E4F727961LLU: // dwk_Orya_IN
+        case 0xC6C345544C61746ELLU: // dwr_Latn_ET
+        case 0xD2C341554C61746ELLU: // dwu_Latn_AU
+        case 0xDAC350474C61746ELLU: // dww_Latn_PG
+        case 0xE2C341554C61746ELLU: // dwy_Latn_AU
+        case 0xE6C34E5044657661LLU: // dwz_Deva_NP
+        case 0x830342464C61746ELLU: // dya_Latn_BF
+        case 0x870341554C61746ELLU: // dyb_Latn_AU
+        case 0x8F0341554C61746ELLU: // dyd_Latn_AU
+        case 0x9B0350484C61746ELLU: // dyg_Latn_PH
+        case 0xA30343494C61746ELLU: // dyi_Latn_CI
+        case 0xB3034D4C4C61746ELLU: // dym_Latn_ML
+        case 0xB70341554C61746ELLU: // dyn_Latn_AU
+        case 0xBB03534E4C61746ELLU: // dyo_Latn_SN
+        case 0xC7034E474C61746ELLU: // dyr_Latn_NG
+        case 0xD30342464C61746ELLU: // dyu_Latn_BF
+        case 0xE30341554C61746ELLU: // dyy_Latn_AU
+        case 0x647A425454696274LLU: // dz_Tibt_BT
+        case 0x83234E474C61746ELLU: // dza_Latn_NG
+        case 0x8F234E474C61746ELLU: // dzd_Latn_NG
+        case 0x932341554C61746ELLU: // dze_Latn_AU
+        case 0x9B2354444C61746ELLU: // dzg_Latn_TD
+        case 0xAF23425454696274LLU: // dzl_Tibt_BT
+        case 0xB72343444C61746ELLU: // dzn_Latn_CD
+        case 0x800441554C61746ELLU: // eaa_Latn_AU
+        case 0x882449444C61746ELLU: // ebc_Latn_ID
+        case 0x98244E474C61746ELLU: // ebg_Latn_NG
+        case 0xA82450484C61746ELLU: // ebk_Latn_PH
+        case 0xB82443474C61746ELLU: // ebo_Latn_CG
+        case 0xC42443494C61746ELLU: // ebr_Latn_CI
+        case 0xD0244B454C61746ELLU: // ebu_Latn_KE
+        case 0xC44447524772656BLLU: // ecr_Grek_GR
+        case 0xE044435943707274LLU: // ecy_Cprt_CY
+        case 0x656547484C61746ELLU: // ee_Latn_GH
+        case 0x80A44E474C61746ELLU: // efa_Latn_NG
+        case 0x90A443444C61746ELLU: // efe_Latn_CD
+        case 0xA0A44E474C61746ELLU: // efi_Latn_NG
+        case 0x80C443494C61746ELLU: // ega_Latn_CI
+        case 0xACC449544C61746ELLU: // egl_Latn_IT
+        case 0xB0C4545A4C61746ELLU: // egm_Latn_TZ
+        case 0xB8C44E474C61746ELLU: // ego_Latn_NG
+        case 0xE0C4454745677970LLU: // egy_Egyp_EG
+        case 0xD0E44E474C61746ELLU: // ehu_Latn_NG
+        case 0xBD0449444C61746ELLU: // eip_Latn_ID
+        case 0xCD0450474C61746ELLU: // eit_Latn_PG
+        case 0xD50450474C61746ELLU: // eiv_Latn_PG
+        case 0x812447574C61746ELLU: // eja_Latn_GW
+        case 0x81444E474C61746ELLU: // eka_Latn_NG
+        case 0x91444E474C61746ELLU: // eke_Latn_NG
+        case 0x994449444C61746ELLU: // ekg_Latn_ID
+        case 0xA1444E474C61746ELLU: // eki_Latn_NG
+        case 0xAD4442444C61746ELLU: // ekl_Latn_BD
+        case 0xB144434D4C61746ELLU: // ekm_Latn_CM
+        case 0xB9444D5A4C61746ELLU: // eko_Latn_MZ
+        case 0xBD444E474C61746ELLU: // ekp_Latn_NG
+        case 0xC5444E474C61746ELLU: // ekr_Latn_NG
+        case 0xE1444D4D4B616C69LLU: // eky_Kali_MM
+        case 0x656C47524772656BLLU: // el_Grek_GR
+        case 0x916450474C61746ELLU: // ele_Latn_PG
+        case 0xA96450474C61746ELLU: // elk_Latn_PG
+        case 0xB1644E474C61746ELLU: // elm_Latn_NG
+        case 0xB9644B454C61746ELLU: // elo_Latn_KE
+        case 0xD16450474C61746ELLU: // elu_Latn_PG
+        case 0x81844E474C61746ELLU: // ema_Latn_NG
+        case 0x858449444C61746ELLU: // emb_Latn_ID
+        case 0x918447464C61746ELLU: // eme_Latn_GF
+        case 0x99844E5044657661LLU: // emg_Deva_NP
+        case 0xA18450474C61746ELLU: // emi_Latn_PG
+        case 0xB1844D584C61746ELLU: // emm_Latn_MX
+        case 0xB584434D4C61746ELLU: // emn_Latn_CM
+        case 0xBD8450414C61746ELLU: // emp_Latn_PA
+        case 0xC98455534C61746ELLU: // ems_Latn_US
+        case 0xD184494E44657661LLU: // emu_Deva_IN
+        case 0xD98449444C61746ELLU: // emw_Latn_ID
+        case 0xDD8446524C61746ELLU: // emx_Latn_FR
+        case 0xE584434D4C61746ELLU: // emz_Latn_CM
+        case 0x656E47424C61746ELLU: // en_Latn_GB
+        case 0x656E55534C61746ELLU: // en_Latn_US
+        case 0x656E474253686177LLU: // en_Shaw_GB
+        case 0x81A450474C61746ELLU: // ena_Latn_PG
+        case 0x85A44B454C61746ELLU: // enb_Latn_KE
+        case 0x89A4564E4C61746ELLU: // enc_Latn_VN
+        case 0x8DA449444C61746ELLU: // end_Latn_ID
+        case 0x95A452554379726CLLU: // enf_Cyrl_RU
+        case 0x9DA452554379726CLLU: // enh_Cyrl_RU
+        case 0xADA450594C61746ELLU: // enl_Latn_PY
+        case 0xB1A447424C61746ELLU: // enm_Latn_GB
+        case 0xB5A44E474C61746ELLU: // enn_Latn_NG
+        case 0xB9A449444C61746ELLU: // eno_Latn_ID
+        case 0xC1A450474C61746ELLU: // enq_Latn_PG
+        case 0xC5A449444C61746ELLU: // enr_Latn_ID
+        case 0xD5A44E474C61746ELLU: // env_Latn_NG
+        case 0xD9A44E474C61746ELLU: // enw_Latn_NG
+        case 0xDDA450594C61746ELLU: // enx_Latn_PY
+        case 0xCDC443494C61746ELLU: // eot_Latn_CI
+        case 0xA1E44E474C61746ELLU: // epi_Latn_NG
+        case 0x8224494E54616D6CLLU: // era_Taml_IN
+        case 0x9A2456554C61746ELLU: // erg_Latn_VU
+        case 0x9E244E474C61746ELLU: // erh_Latn_NG
+        case 0xA22450474C61746ELLU: // eri_Latn_PG
+        case 0xAA2456554C61746ELLU: // erk_Latn_VU
+        case 0xC62441554C61746ELLU: // err_Latn_AU
+        case 0xCA24434E4C61746ELLU: // ers_Latn_CN
+        case 0xCE2449444C61746ELLU: // ert_Latn_ID
+        case 0xDA2449444C61746ELLU: // erw_Latn_ID
+        case 0x657345534C61746ELLU: // es_Latn_ES
+        case 0x65734D584C61746ELLU: // es_Latn_MX
+        case 0x657355534C61746ELLU: // es_Latn_US
+        case 0x9244424F4C61746ELLU: // ese_Latn_BO
+        case 0x9A44494E476F6E6DLLU: // esg_Gonm_IN
+        case 0x9E44495241726162LLU: // esh_Arab_IR
+        case 0xA24455534C61746ELLU: // esi_Latn_US
+        case 0xB24443494C61746ELLU: // esm_Latn_CI
+        case 0xCA4455534C61746ELLU: // ess_Latn_US
+        case 0xD24455534C61746ELLU: // esu_Latn_US
+        case 0xE24450484C61746ELLU: // esy_Latn_PH
+        case 0x657445454C61746ELLU: // et_Latn_EE
+        case 0x86644E474C61746ELLU: // etb_Latn_NG
+        case 0xB66456554C61746ELLU: // etn_Latn_VU
+        case 0xBA64434D4C61746ELLU: // eto_Latn_CM
+        case 0xC66450474C61746ELLU: // etr_Latn_PG
+        case 0xCA644E474C61746ELLU: // ets_Latn_NG
+        case 0xCE6449544974616CLLU: // ett_Ital_IT
+        case 0xD2644E474C61746ELLU: // etu_Latn_NG
+        case 0xDE644E474C61746ELLU: // etx_Latn_NG
+        case 0xE66449444C61746ELLU: // etz_Latn_ID
+        case 0x657545534C61746ELLU: // eu_Latn_ES
+        case 0x8E844D584C61746ELLU: // eud_Latn_MX
+        case 0x92A452554379726CLLU: // eve_Cyrl_RU
+        case 0x9EA44E474C61746ELLU: // evh_Latn_NG
+        case 0xB6A452554379726CLLU: // evn_Cyrl_RU
+        case 0xBAC4434D4C61746ELLU: // ewo_Latn_CM
+        case 0xCEE445534C61746ELLU: // ext_Latn_ES
+        case 0x830455534C61746ELLU: // eya_Latn_US
+        case 0xBB044B454C61746ELLU: // eyo_Latn_KE
+        case 0x83244E474C61746ELLU: // eza_Latn_NG
+        case 0x93244E474C61746ELLU: // eze_Latn_NG
+        case 0x6661495241726162LLU: // fa_Arab_IR
+        case 0x800550474C61746ELLU: // faa_Latn_PG
+        case 0x840547514C61746ELLU: // fab_Latn_GQ
+        case 0x8C0550474C61746ELLU: // fad_Latn_PG
+        case 0x940553424C61746ELLU: // faf_Latn_SB
+        case 0x980550474C61746ELLU: // fag_Latn_PG
+        case 0x9C054E474C61746ELLU: // fah_Latn_NG
+        case 0xA00550474C61746ELLU: // fai_Latn_PG
+        case 0xA40550474C61746ELLU: // faj_Latn_PG
+        case 0xA805434D4C61746ELLU: // fak_Latn_CM
+        case 0xAC05434D4C61746ELLU: // fal_Latn_CM
+        case 0xB0054E474C61746ELLU: // fam_Latn_NG
+        case 0xB40547514C61746ELLU: // fan_Latn_GQ
+        case 0xBC05534E4C61746ELLU: // fap_Latn_SN
+        case 0xC40553424C61746ELLU: // far_Latn_SB
+        case 0xD00549444C61746ELLU: // fau_Latn_ID
+        case 0xDC0545534C61746ELLU: // fax_Latn_ES
+        case 0xE005495241726162LLU: // fay_Arab_IR
+        case 0xE405495241726162LLU: // faz_Arab_IR
+        case 0xAC2550484C61746ELLU: // fbl_Latn_PH
+        case 0xC48553534C61746ELLU: // fer_Latn_SS
+        case 0x6666474E41646C6DLLU: // ff_Adlm_GN
+        case 0x6666534E4C61746ELLU: // ff_Latn_SN
+        case 0xA0A550474C61746ELLU: // ffi_Latn_PG
+        case 0xB0A54D4C4C61746ELLU: // ffm_Latn_ML
+        case 0xC4C554444C61746ELLU: // fgr_Latn_TD
+        case 0x666946494C61746ELLU: // fi_Latn_FI
+        case 0x8105534441726162LLU: // fia_Arab_SD
+        case 0x91054E474C61746ELLU: // fie_Latn_NG
+        case 0x950553414C61746ELLU: // fif_Latn_SA
+        case 0xAD0550484C61746ELLU: // fil_Latn_PH
+        case 0xBD05545A4C61746ELLU: // fip_Latn_TZ
+        case 0xC5054E474C61746ELLU: // fir_Latn_NG
+        case 0xCD0553454C61746ELLU: // fit_Latn_SE
+        case 0xD90550474C61746ELLU: // fiw_Latn_PG
+        case 0x666A464A4C61746ELLU: // fj_Latn_FJ
+        case 0xA9454E474C61746ELLU: // fkk_Latn_NG
+        case 0xD5454E4F4C61746ELLU: // fkv_Latn_NO
+        case 0x816555534C61746ELLU: // fla_Latn_US
+        case 0x9D6549444C61746ELLU: // flh_Latn_ID
+        case 0xA1654E474C61746ELLU: // fli_Latn_NG
+        case 0xAD65434D4C61746ELLU: // fll_Latn_CM
+        case 0xB56541554C61746ELLU: // fln_Latn_AU
+        case 0xC56543444C61746ELLU: // flr_Latn_CD
+        case 0xE1655A414C61746ELLU: // fly_Latn_ZA
+        case 0xBD85434D4C61746ELLU: // fmp_Latn_CM
+        case 0xD185494E44657661LLU: // fmu_Deva_IN
+        case 0x85A556554C61746ELLU: // fnb_Latn_VU
+        case 0x99A55A414C61746ELLU: // fng_Latn_ZA
+        case 0xA1A554444C61746ELLU: // fni_Latn_TD
+        case 0x666F464F4C61746ELLU: // fo_Latn_FO
+        case 0x8DC5424A4C61746ELLU: // fod_Latn_BJ
+        case 0xA1C550474C61746ELLU: // foi_Latn_PG
+        case 0xB1C543444C61746ELLU: // fom_Latn_CD
+        case 0xB5C5424A4C61746ELLU: // fon_Latn_BJ
+        case 0xC5C550474C61746ELLU: // for_Latn_PG
+        case 0xC9C554574C61746ELLU: // fos_Latn_TW
+        case 0x91E547514C61746ELLU: // fpe_Latn_GQ
+        case 0xCA0550474C61746ELLU: // fqs_Latn_PG
+        case 0x667246524C61746ELLU: // fr_Latn_FR
+        case 0x8A2555534C61746ELLU: // frc_Latn_US
+        case 0x8E2549444C61746ELLU: // frd_Latn_ID
+        case 0xAA2544454C61746ELLU: // frk_Latn_DE
+        case 0xB22546524C61746ELLU: // frm_Latn_FR
+        case 0xBA2546524C61746ELLU: // fro_Latn_FR
+        case 0xBE2546524C61746ELLU: // frp_Latn_FR
+        case 0xC22550474C61746ELLU: // frq_Latn_PG
+        case 0xC62544454C61746ELLU: // frr_Latn_DE
+        case 0xCA2544454C61746ELLU: // frs_Latn_DE
+        case 0xCE2556554C61746ELLU: // frt_Latn_VU
+        case 0x8685434D41726162LLU: // fub_Arab_CM
+        case 0x8E8557464C61746ELLU: // fud_Latn_WF
+        case 0x9285424A4C61746ELLU: // fue_Latn_BJ
+        case 0x9685474E4C61746ELLU: // fuf_Latn_GN
+        case 0x9E854E454C61746ELLU: // fuh_Latn_NE
+        case 0xA28554444C61746ELLU: // fui_Latn_TD
+        case 0xB2854E474C61746ELLU: // fum_Latn_NG
+        case 0xB68542524C61746ELLU: // fun_Latn_BR
+        case 0xC2854E454C61746ELLU: // fuq_Latn_NE
+        case 0xC68549544C61746ELLU: // fur_Latn_IT
+        case 0xCE8556554C61746ELLU: // fut_Latn_VU
+        case 0xD28543444C61746ELLU: // fuu_Latn_CD
+        case 0xD6854E474C61746ELLU: // fuv_Latn_NG
+        case 0xE28550474C61746ELLU: // fuy_Latn_PG
+        case 0xC6A553444C61746ELLU: // fvr_Latn_SD
+        case 0x82C54E434C61746ELLU: // fwa_Latn_NC
+        case 0x92C54E414C61746ELLU: // fwe_Latn_NA
+        case 0x66794E4C4C61746ELLU: // fy_Latn_NL
+        case 0x676149454C61746ELLU: // ga_Latn_IE
+        case 0x800647484C61746ELLU: // gaa_Latn_GH
+        case 0x840654444C61746ELLU: // gab_Latn_TD
+        case 0x8806494E4C61746ELLU: // gac_Latn_IN
+        case 0x8C0650484C61746ELLU: // gad_Latn_PH
+        case 0x900656454C61746ELLU: // gae_Latn_VE
+        case 0x940650474C61746ELLU: // gaf_Latn_PG
+        case 0x98064D444C61746ELLU: // gag_Latn_MD
+        case 0x9C0650474C61746ELLU: // gah_Latn_PG
+        case 0xA00650474C61746ELLU: // gai_Latn_PG
+        case 0xA40650474C61746ELLU: // gaj_Latn_PG
+        case 0xA80649444C61746ELLU: // gak_Latn_ID
+        case 0xAC06544C4C61746ELLU: // gal_Latn_TL
+        case 0xB00650474C61746ELLU: // gam_Latn_PG
+        case 0xB406434E48616E73LLU: // gan_Hans_CN
+        case 0xB80650474C61746ELLU: // gao_Latn_PG
+        case 0xBC0650474C61746ELLU: // gap_Latn_PG
+        case 0xC006494E4F727961LLU: // gaq_Orya_IN
+        case 0xC40650474C61746ELLU: // gar_Latn_PG
+        case 0xC806494E47756A72LLU: // gas_Gujr_IN
+        case 0xCC0650474C61746ELLU: // gat_Latn_PG
+        case 0xD006494E54656C75LLU: // gau_Telu_IN
+        case 0xD80650474C61746ELLU: // gaw_Latn_PG
+        case 0xDC0645544C61746ELLU: // gax_Latn_ET
+        case 0xE00649444C61746ELLU: // gay_Latn_ID
+        case 0x802643464C61746ELLU: // gba_Latn_CF
+        case 0x842641554C61746ELLU: // gbb_Latn_AU
+        case 0x8C2641554C61746ELLU: // gbd_Latn_AU
+        case 0x902650474C61746ELLU: // gbe_Latn_PG
+        case 0x942650474C61746ELLU: // gbf_Latn_PG
+        case 0x982643464C61746ELLU: // gbg_Latn_CF
+        case 0x9C26424A4C61746ELLU: // gbh_Latn_BJ
+        case 0xA02649444C61746ELLU: // gbi_Latn_ID
+        case 0xA426494E4F727961LLU: // gbj_Orya_IN
+        case 0xA826494E44657661LLU: // gbk_Deva_IN
+        case 0xAC26494E47756A72LLU: // gbl_Gujr_IN
+        case 0xB026494E44657661LLU: // gbm_Deva_IN
+        case 0xB42653534C61746ELLU: // gbn_Latn_SS
+        case 0xBC2643464C61746ELLU: // gbp_Latn_CF
+        case 0xC02643464C61746ELLU: // gbq_Latn_CF
+        case 0xC4264E474C61746ELLU: // gbr_Latn_NG
+        case 0xC826424A4C61746ELLU: // gbs_Latn_BJ
+        case 0xD02641554C61746ELLU: // gbu_Latn_AU
+        case 0xD42643464C61746ELLU: // gbv_Latn_CF
+        case 0xD82641554C61746ELLU: // gbw_Latn_AU
+        case 0xDC26424A4C61746ELLU: // gbx_Latn_BJ
+        case 0xE0264E474C61746ELLU: // gby_Latn_NG
+        case 0xE426495241726162LLU: // gbz_Arab_IR
+        case 0x884650474C61746ELLU: // gcc_Latn_PG
+        case 0x8C4641554C61746ELLU: // gcd_Latn_AU
+        case 0x944647504C61746ELLU: // gcf_Latn_GP
+        case 0xAC4647444C61746ELLU: // gcl_Latn_GD
+        case 0xB44650474C61746ELLU: // gcn_Latn_PG
+        case 0xC44647464C61746ELLU: // gcr_Latn_GF
+        case 0xCC4656454C61746ELLU: // gct_Latn_VE
+        case 0x676447424C61746ELLU: // gd_Latn_GB
+        case 0x8466494E4F727961LLU: // gdb_Orya_IN
+        case 0x886641554C61746ELLU: // gdc_Latn_AU
+        case 0x8C6650474C61746ELLU: // gdd_Latn_PG
+        case 0x90664E474C61746ELLU: // gde_Latn_NG
+        case 0x94664E474C61746ELLU: // gdf_Latn_NG
+        case 0x986650484C61746ELLU: // gdg_Latn_PH
+        case 0x9C6641554C61746ELLU: // gdh_Latn_AU
+        case 0xA06643464C61746ELLU: // gdi_Latn_CF
+        case 0xA46641554C61746ELLU: // gdj_Latn_AU
+        case 0xA86654444C61746ELLU: // gdk_Latn_TD
+        case 0xAC6645544C61746ELLU: // gdl_Latn_ET
+        case 0xB06654444C61746ELLU: // gdm_Latn_TD
+        case 0xB46650474C61746ELLU: // gdn_Latn_PG
+        case 0xB86652554379726CLLU: // gdo_Cyrl_RU
+        case 0xC06659454C61746ELLU: // gdq_Latn_YE
+        case 0xC46650474C61746ELLU: // gdr_Latn_PG
+        case 0xCC6641554C61746ELLU: // gdt_Latn_AU
+        case 0xD0664E474C61746ELLU: // gdu_Latn_NG
+        case 0xDC66494E44657661LLU: // gdx_Deva_IN
+        case 0x80864E474C61746ELLU: // gea_Latn_NG
+        case 0x848650474C61746ELLU: // geb_Latn_PG
+        case 0x88864C524C61746ELLU: // gec_Latn_LR
+        case 0x8C864E474C61746ELLU: // ged_Latn_NG
+        case 0x948649444C61746ELLU: // gef_Latn_ID
+        case 0x98864E474C61746ELLU: // geg_Latn_NG
+        case 0x9C8643414C61746ELLU: // geh_Latn_CA
+        case 0xA08649444C61746ELLU: // gei_Latn_ID
+        case 0xA48654474C61746ELLU: // gej_Latn_TG
+        case 0xA8864E474C61746ELLU: // gek_Latn_NG
+        case 0xAC864E474C61746ELLU: // gel_Latn_NG
+        case 0xC08643464C61746ELLU: // geq_Latn_CF
+        case 0xC88649444C61746ELLU: // ges_Latn_ID
+        case 0xD48647414C61746ELLU: // gev_Latn_GA
+        case 0xD8864E474C61746ELLU: // gew_Latn_NG
+        case 0xDC86534F4C61746ELLU: // gex_Latn_SO
+        case 0xE08643444C61746ELLU: // gey_Latn_CD
+        case 0xE486455445746869LLU: // gez_Ethi_ET
+        case 0xA8A650474C61746ELLU: // gfk_Latn_PG
+        case 0x80C653424C61746ELLU: // gga_Latn_SB
+        case 0x84C64C524C61746ELLU: // ggb_Latn_LR
+        case 0x8CC641554C61746ELLU: // ggd_Latn_AU
+        case 0x90C641554C61746ELLU: // gge_Latn_AU
+        case 0x98C6504B41726162LLU: // ggg_Arab_PK
+        case 0xA8C641554C61746ELLU: // ggk_Latn_AU
+        case 0xACC650474C61746ELLU: // ggl_Latn_PG
+        case 0xCCC650474C61746ELLU: // ggt_Latn_PG
+        case 0xD0C643494C61746ELLU: // ggu_Latn_CI
+        case 0xD8C650474C61746ELLU: // ggw_Latn_PG
+        case 0x80E64C5941726162LLU: // gha_Arab_LY
+        case 0x88E647424C61746ELLU: // ghc_Latn_GB
+        case 0x90E64E5044657661LLU: // ghe_Deva_NP
+        case 0xA8E64D4D4C61746ELLU: // ghk_Latn_MM
+        case 0xB4E653424C61746ELLU: // ghn_Latn_SB
+        case 0xB8E64D4154666E67LLU: // gho_Tfng_MA
+        case 0xC4E6504B41726162LLU: // ghr_Arab_PK
+        case 0xC8E650474C61746ELLU: // ghs_Latn_PG
+        case 0xCCE64E5054696274LLU: // ght_Tibt_NP
+        case 0x810641554C61746ELLU: // gia_Latn_AU
+        case 0x85064E474C61746ELLU: // gib_Latn_NG
+        case 0x89065A414C61746ELLU: // gic_Latn_ZA
+        case 0x8D06434D4C61746ELLU: // gid_Latn_CM
+        case 0x910643494C61746ELLU: // gie_Latn_CI
+        case 0x9906504B41726162LLU: // gig_Arab_PK
+        case 0x9D0641554C61746ELLU: // gih_Latn_AU
+        case 0xAD064B494C61746ELLU: // gil_Latn_KI
+        case 0xB10650474C61746ELLU: // gim_Latn_PG
+        case 0xB50652554379726CLLU: // gin_Cyrl_RU
+        case 0xBD0650474C61746ELLU: // gip_Latn_PG
+        case 0xC106564E4C61746ELLU: // giq_Latn_VN
+        case 0xC506564E4C61746ELLU: // gir_Latn_VN
+        case 0xC906434D4C61746ELLU: // gis_Latn_CM
+        case 0xCD0643414C61746ELLU: // git_Latn_CA
+        case 0xDD0643444C61746ELLU: // gix_Latn_CD
+        case 0xE10641554C61746ELLU: // giy_Latn_AU
+        case 0xE506434D4C61746ELLU: // giz_Latn_CM
+        case 0xA926504B41726162LLU: // gjk_Arab_PK
+        case 0xB12641554C61746ELLU: // gjm_Latn_AU
+        case 0xB52647484C61746ELLU: // gjn_Latn_GH
+        case 0xC52641554C61746ELLU: // gjr_Latn_AU
+        case 0xD126504B41726162LLU: // gju_Arab_PK
+        case 0x814650474C61746ELLU: // gka_Latn_PG
+        case 0x8D4650474C61746ELLU: // gkd_Latn_PG
+        case 0x9146434D4C61746ELLU: // gke_Latn_CM
+        case 0xB5464E474C61746ELLU: // gkn_Latn_NG
+        case 0xB94641554C61746ELLU: // gko_Latn_AU
+        case 0xBD46474E4C61746ELLU: // gkp_Latn_GN
+        case 0xD1465A414C61746ELLU: // gku_Latn_ZA
+        case 0x676C45534C61746ELLU: // gl_Latn_ES
+        case 0x85664E474C61746ELLU: // glb_Latn_NG
+        case 0x896654444C61746ELLU: // glc_Latn_TD
+        case 0x8D6652554379726CLLU: // gld_Cyrl_RU
+        case 0x9D66414641726162LLU: // glh_Arab_AF
+        case 0xA56654444C61746ELLU: // glj_Latn_TD
+        case 0xA966495241726162LLU: // glk_Arab_IR
+        case 0xAD6641554C61746ELLU: // gll_Latn_AU
+        case 0xB9664E474C61746ELLU: // glo_Latn_NG
+        case 0xC5664C524C61746ELLU: // glr_Latn_LR
+        case 0xD16654444C61746ELLU: // glu_Latn_TD
+        case 0xD9664E474C61746ELLU: // glw_Latn_NG
+        case 0x818641554C61746ELLU: // gma_Latn_AU
+        case 0x858653424C61746ELLU: // gmb_Latn_SB
+        case 0x8D864E474C61746ELLU: // gmd_Latn_NG
+        case 0x998650474C61746ELLU: // gmg_Latn_PG
+        case 0x9D8644454C61746ELLU: // gmh_Latn_DE
+        case 0xAD8644454C617466LLU: // gml_Latf_DE
+        case 0xB186434D4C61746ELLU: // gmm_Latn_CM
+        case 0xB586434D4C61746ELLU: // gmn_Latn_CM
+        case 0xC58641554C61746ELLU: // gmr_Latn_AU
+        case 0xD18650474C61746ELLU: // gmu_Latn_PG
+        case 0xD586455445746869LLU: // gmv_Ethi_ET
+        case 0xDD86545A4C61746ELLU: // gmx_Latn_TZ
+        case 0xE18647524C696E62LLU: // gmy_Linb_GR
+        case 0xE5864E474C61746ELLU: // gmz_Latn_NG
+        case 0x676E50594C61746ELLU: // gn_Latn_PY
+        case 0x81A642464C61746ELLU: // gna_Latn_BF
+        case 0x85A6494E4C61746ELLU: // gnb_Latn_IN
+        case 0x89A645534C61746ELLU: // gnc_Latn_ES
+        case 0x8DA6434D4C61746ELLU: // gnd_Latn_CM
+        case 0x91A64E474C61746ELLU: // gne_Latn_NG
+        case 0x99A654474C61746ELLU: // gng_Latn_TG
+        case 0x9DA64E474C61746ELLU: // gnh_Latn_NG
+        case 0xA1A641554C61746ELLU: // gni_Latn_AU
+        case 0xA5A643494C61746ELLU: // gnj_Latn_CI
+        case 0xA9A642574C61746ELLU: // gnk_Latn_BW
+        case 0xADA641554C61746ELLU: // gnl_Latn_AU
+        case 0xB1A650474C61746ELLU: // gnm_Latn_PG
+        case 0xB5A641554C61746ELLU: // gnn_Latn_AU
+        case 0xC1A64D594C61746ELLU: // gnq_Latn_MY
+        case 0xC5A641554C61746ELLU: // gnr_Latn_AU
+        case 0xCDA650474C61746ELLU: // gnt_Latn_PG
+        case 0xD1A650474C61746ELLU: // gnu_Latn_PG
+        case 0xD9A6424F4C61746ELLU: // gnw_Latn_BO
+        case 0xE5A643464C61746ELLU: // gnz_Latn_CF
+        case 0x81C643494C61746ELLU: // goa_Latn_CI
+        case 0x85C6434F4C61746ELLU: // gob_Latn_CO
+        case 0x89C650474C61746ELLU: // goc_Latn_PG
+        case 0x8DC643494C61746ELLU: // god_Latn_CI
+        case 0x91C6425454696274LLU: // goe_Tibt_BT
+        case 0x95C6455445746869LLU: // gof_Ethi_ET
+        case 0x99C6545A4C61746ELLU: // gog_Latn_TZ
+        case 0x9DC644454C61746ELLU: // goh_Latn_DE
+        case 0xA1C650474C61746ELLU: // goi_Latn_PG
+        case 0xA5C6494E44657661LLU: // goj_Deva_IN
+        case 0xA9C6494E44657661LLU: // gok_Deva_IN
+        case 0xADC64C524C61746ELLU: // gol_Latn_LR
+        case 0xB5C6494E44657661LLU: // gon_Deva_IN
+        case 0xB9C6464A4C61746ELLU: // goo_Latn_FJ
+        case 0xBDC649444C61746ELLU: // gop_Latn_ID
+        case 0xC1C649444C61746ELLU: // goq_Latn_ID
+        case 0xC5C649444C61746ELLU: // gor_Latn_ID
+        case 0xC9C64E4C4C61746ELLU: // gos_Latn_NL
+        case 0xCDC65541476F7468LLU: // got_Goth_UA
+        case 0xD1C6434D4C61746ELLU: // gou_Latn_CM
+        case 0xD5C643494C61746ELLU: // gov_Latn_CI
+        case 0xD9C6545A4C61746ELLU: // gow_Latn_TZ
+        case 0xDDC643444C61746ELLU: // gox_Latn_CD
+        case 0xE1C654444C61746ELLU: // goy_Latn_TD
+        case 0x81E64E474C61746ELLU: // gpa_Latn_NG
+        case 0x91E647484C61746ELLU: // gpe_Latn_GH
+        case 0xB5E650474C61746ELLU: // gpn_Latn_PG
+        case 0x82064E474C61746ELLU: // gqa_Latn_NG
+        case 0xB60642524C61746ELLU: // gqn_Latn_BR
+        case 0xC60654444C61746ELLU: // gqr_Latn_TD
+        case 0x8226494E44657661LLU: // gra_Deva_IN
+        case 0x86264C524C61746ELLU: // grb_Latn_LR
+        case 0x8A26435943707274LLU: // grc_Cprt_CY
+        case 0x8A2647524C696E62LLU: // grc_Linb_GR
+        case 0x8E264E474C61746ELLU: // grd_Latn_NG
+        case 0x9A2650474C61746ELLU: // grg_Latn_PG
+        case 0x9E264E474C61746ELLU: // grh_Latn_NG
+        case 0xA22653424C61746ELLU: // gri_Latn_SB
+        case 0xA6264C524C61746ELLU: // grj_Latn_LR
+        case 0xB2264D594C61746ELLU: // grm_Latn_MY
+        case 0xC22650474C61746ELLU: // grq_Latn_PG
+        case 0xCA2649444C61746ELLU: // grs_Latn_ID
+        case 0xCE26494E42656E67LLU: // grt_Beng_IN
+        case 0xD226455445746869LLU: // gru_Ethi_ET
+        case 0xD6264C524C61746ELLU: // grv_Latn_LR
+        case 0xDA2650474C61746ELLU: // grw_Latn_PG
+        case 0xDE2650474C61746ELLU: // grx_Latn_PG
+        case 0xE2264C524C61746ELLU: // gry_Latn_LR
+        case 0xE62650474C61746ELLU: // grz_Latn_PG
+        case 0xAE46534E4C61746ELLU: // gsl_Latn_SN
+        case 0xB64650474C61746ELLU: // gsn_Latn_PG
+        case 0xBA4643464C61746ELLU: // gso_Latn_CF
+        case 0xBE4650474C61746ELLU: // gsp_Latn_PG
+        case 0xDA4643484C61746ELLU: // gsw_Latn_CH
+        case 0x826642524C61746ELLU: // gta_Latn_BR
+        case 0xD26641554C61746ELLU: // gtu_Latn_AU
+        case 0x6775494E47756A72LLU: // gu_Gujr_IN
+        case 0x82864E474C61746ELLU: // gua_Latn_NG
+        case 0x868642524C61746ELLU: // gub_Latn_BR
+        case 0x8A86434F4C61746ELLU: // guc_Latn_CO
+        case 0x8E8643494C61746ELLU: // gud_Latn_CI
+        case 0x928641554C61746ELLU: // gue_Latn_AU
+        case 0x968641554C61746ELLU: // guf_Latn_AU
+        case 0x9E86434F4C61746ELLU: // guh_Latn_CO
+        case 0xA286424F4C61746ELLU: // gui_Latn_BO
+        case 0xAA8645544C61746ELLU: // guk_Latn_ET
+        case 0xAE8655534C61746ELLU: // gul_Latn_US
+        case 0xB286434F4C61746ELLU: // gum_Latn_CO
+        case 0xB68642524C61746ELLU: // gun_Latn_BR
+        case 0xBA86434F4C61746ELLU: // guo_Latn_CO
+        case 0xBE8641554C61746ELLU: // gup_Latn_AU
+        case 0xC28650594C61746ELLU: // guq_Latn_PY
+        case 0xC68647484C61746ELLU: // gur_Latn_GH
+        case 0xCE8643524C61746ELLU: // gut_Latn_CR
+        case 0xD28656454C61746ELLU: // guu_Latn_VE
+        case 0xDA86424A4C61746ELLU: // guw_Latn_BJ
+        case 0xDE8642464C61746ELLU: // gux_Latn_BF
+        case 0xE6864B454C61746ELLU: // guz_Latn_KE
+        case 0x6776494D4C61746ELLU: // gv_Latn_IM
+        case 0x82A650594C61746ELLU: // gva_Latn_PY
+        case 0x8AA642524C61746ELLU: // gvc_Latn_BR
+        case 0x92A650474C61746ELLU: // gve_Latn_PG
+        case 0x96A650474C61746ELLU: // gvf_Latn_PG
+        case 0xA6A642524C61746ELLU: // gvj_Latn_BR
+        case 0xAEA654444C61746ELLU: // gvl_Latn_TD
+        case 0xB2A64E474C61746ELLU: // gvm_Latn_NG
+        case 0xB6A641554C61746ELLU: // gvn_Latn_AU
+        case 0xBAA642524C61746ELLU: // gvo_Latn_BR
+        case 0xBEA642524C61746ELLU: // gvp_Latn_BR
+        case 0xC6A64E5044657661LLU: // gvr_Deva_NP
+        case 0xCAA650474C61746ELLU: // gvs_Latn_PG
+        case 0xE2A641554C61746ELLU: // gvy_Latn_AU
+        case 0x82C643494C61746ELLU: // gwa_Latn_CI
+        case 0x86C64E474C61746ELLU: // gwb_Latn_NG
+        case 0x8AC6504B41726162LLU: // gwc_Arab_PK
+        case 0x8EC645544C61746ELLU: // gwd_Latn_ET
+        case 0x92C6545A4C61746ELLU: // gwe_Latn_TZ
+        case 0x96C6504B41726162LLU: // gwf_Arab_PK
+        case 0x9AC64E474C61746ELLU: // gwg_Latn_NG
+        case 0xA2C643414C61746ELLU: // gwi_Latn_CA
+        case 0xA6C642574C61746ELLU: // gwj_Latn_BW
+        case 0xB2C641554C61746ELLU: // gwm_Latn_AU
+        case 0xB6C64E474C61746ELLU: // gwn_Latn_NG
+        case 0xC6C655474C61746ELLU: // gwr_Latn_UG
+        case 0xCEC6414641726162LLU: // gwt_Arab_AF
+        case 0xD2C641554C61746ELLU: // gwu_Latn_AU
+        case 0xDAC641554C61746ELLU: // gww_Latn_AU
+        case 0xDEC647484C61746ELLU: // gwx_Latn_GH
+        case 0xDEE643494C61746ELLU: // gxx_Latn_CI
+        case 0x870650474C61746ELLU: // gyb_Latn_PG
+        case 0x8F0641554C61746ELLU: // gyd_Latn_AU
+        case 0x93064E474C61746ELLU: // gye_Latn_NG
+        case 0x970641554C61746ELLU: // gyf_Latn_AU
+        case 0x9B0643464C61746ELLU: // gyg_Latn_CF
+        case 0xA306434D4C61746ELLU: // gyi_Latn_CM
+        case 0xAF0645544C61746ELLU: // gyl_Latn_ET
+        case 0xB30650414C61746ELLU: // gym_Latn_PA
+        case 0xB70647594C61746ELLU: // gyn_Latn_GY
+        case 0xBB064E5044657661LLU: // gyo_Deva_NP
+        case 0xC706424F4C61746ELLU: // gyr_Latn_BO
+        case 0xE30641554C61746ELLU: // gyy_Latn_AU
+        case 0xE7064E474C61746ELLU: // gyz_Latn_NG
+        case 0x832653444C61746ELLU: // gza_Latn_SD
+        case 0xA326495241726162LLU: // gzi_Arab_IR
+        case 0xB72649444C61746ELLU: // gzn_Latn_ID
+        case 0x68614E474C61746ELLU: // ha_Latn_NG
+        case 0x800755534C61746ELLU: // haa_Latn_US
+        case 0x8807495241726162LLU: // hac_Arab_IR
+        case 0x8C0749444C61746ELLU: // had_Latn_ID
+        case 0x900745544C61746ELLU: // hae_Latn_ET
+        case 0x980747484C61746ELLU: // hag_Latn_GH
+        case 0x9C0750474C61746ELLU: // hah_Latn_PG
+        case 0xA00743414C61746ELLU: // hai_Latn_CA
+        case 0xA407494E4C61746ELLU: // haj_Latn_IN
+        case 0xA807434E48616E73LLU: // hak_Hans_CN
+        case 0xAC07564E4C61746ELLU: // hal_Latn_VN
+        case 0xB00750474C61746ELLU: // ham_Latn_PG
+        case 0xB407545A4C61746ELLU: // han_Latn_TZ
+        case 0xB80750474C61746ELLU: // hao_Latn_PG
+        case 0xBC0749444C61746ELLU: // hap_Latn_ID
+        case 0xC007545A4C61746ELLU: // haq_Latn_TZ
+        case 0xC407455445746869LLU: // har_Ethi_ET
+        case 0xC80743414C61746ELLU: // has_Latn_CA
+        case 0xD40743444C61746ELLU: // hav_Latn_CD
+        case 0xD80755534C61746ELLU: // haw_Latn_US
+        case 0xDC0743414C61746ELLU: // hax_Latn_CA
+        case 0xE007545A4C61746ELLU: // hay_Latn_TZ
+        case 0xE407414641726162LLU: // haz_Arab_AF
+        case 0x802743444C61746ELLU: // hba_Latn_CD
+        case 0x84274E474C61746ELLU: // hbb_Latn_NG
+        case 0xB42753444C61746ELLU: // hbn_Latn_SD
+        case 0xB827494C48656272LLU: // hbo_Hebr_IL
+        case 0xD027544C4C61746ELLU: // hbu_Latn_TL
+        case 0x9C474D584C61746ELLU: // hch_Latn_MX
+        case 0xE067455445746869LLU: // hdy_Ethi_ET
+        case 0x6865494C48656272LLU: // he_Hebr_IL
+        case 0x8C8754444C61746ELLU: // hed_Latn_TD
+        case 0x988749444C61746ELLU: // heg_Latn_ID
+        case 0x9C87545A4C61746ELLU: // heh_Latn_TZ
+        case 0xA08743414C61746ELLU: // hei_Latn_CA
+        case 0xB08743444C61746ELLU: // hem_Latn_CD
+        case 0xB0C74E414C61746ELLU: // hgm_Latn_NA
+        case 0xD8C750474C61746ELLU: // hgw_Latn_PG
+        case 0xA0E750474C61746ELLU: // hhi_Latn_PG
+        case 0xC4E7534E4C61746ELLU: // hhr_Latn_SN
+        case 0xE0E750474C61746ELLU: // hhy_Latn_PG
+        case 0x6869494E44657661LLU: // hi_Deva_IN
+        case 0x81074E474C61746ELLU: // hia_Latn_NG
+        case 0x850750454C61746ELLU: // hib_Latn_PE
+        case 0x8D0755534C61746ELLU: // hid_Latn_US
+        case 0x9507464A44657661LLU: // hif_Deva_FJ
+        case 0x99074E474C61746ELLU: // hig_Latn_NG
+        case 0x9D0750474C61746ELLU: // hih_Latn_PG
+        case 0xA107494E54616B72LLU: // hii_Takr_IN
+        case 0xA507434D4C61746ELLU: // hij_Latn_CM
+        case 0xA90749444C61746ELLU: // hik_Latn_ID
+        case 0xAD0750484C61746ELLU: // hil_Latn_PH
+        case 0xB90742574C61746ELLU: // hio_Latn_BW
+        case 0xC50742524C61746ELLU: // hir_Latn_BR
+        case 0xCD07545258737578LLU: // hit_Xsux_TR
+        case 0xD90756554C61746ELLU: // hiw_Latn_VU
+        case 0xDD0742524C61746ELLU: // hix_Latn_BR
+        case 0xA12749444C61746ELLU: // hji_Latn_ID
+        case 0x8147545A4C61746ELLU: // hka_Latn_TZ
+        case 0x914743444C61746ELLU: // hke_Latn_CD
+        case 0x9D47494E41726162LLU: // hkh_Arab_IN
+        case 0xA94750474C61746ELLU: // hkk_Latn_PG
+        case 0x816750474C61746ELLU: // hla_Latn_PG
+        case 0x8567494E44657661LLU: // hlb_Deva_IN
+        case 0x8D67564E4C61746ELLU: // hld_Latn_VN
+        case 0xCD674D4D4C61746ELLU: // hlt_Latn_MM
+        case 0xD1675452486C7577LLU: // hlu_Hluw_TR
+        case 0x8187434E4C61746ELLU: // hma_Latn_CN
+        case 0x85874D4C4C61746ELLU: // hmb_Latn_ML
+        case 0x8D87434E506C7264LLU: // hmd_Plrd_CN
+        case 0x9587564E4C61746ELLU: // hmf_Latn_VN
+        case 0xA587434E426F706FLLU: // hmj_Bopo_CN
+        case 0xB187434E4C61746ELLU: // hmm_Latn_CN
+        case 0xB587434E4C61746ELLU: // hmn_Latn_CN
+        case 0xBD87434E4C61746ELLU: // hmp_Latn_CN
+        case 0xC187434E426F706FLLU: // hmq_Bopo_CN
+        case 0xC587494E4C61746ELLU: // hmr_Latn_IN
+        case 0xC987434E4C61746ELLU: // hms_Latn_CN
+        case 0xCD8750474C61746ELLU: // hmt_Latn_PG
+        case 0xD18749444C61746ELLU: // hmu_Latn_ID
+        case 0xD587564E4C61746ELLU: // hmv_Latn_VN
+        case 0xD987434E4C61746ELLU: // hmw_Latn_CN
+        case 0xE187434E4C61746ELLU: // hmy_Latn_CN
+        case 0xE587434E4C61746ELLU: // hmz_Latn_CN
+        case 0x81A7434D4C61746ELLU: // hna_Latn_CM
+        case 0x8DA7504B41726162LLU: // hnd_Arab_PK
+        case 0x91A7494E44657661LLU: // hne_Deva_IN
+        case 0x99A7414F4C61746ELLU: // hng_Latn_AO
+        case 0x9DA742574C61746ELLU: // hnh_Latn_BW
+        case 0xA1A7434E4C61746ELLU: // hni_Latn_CN
+        case 0xA5A74C41486D6E67LLU: // hnj_Hmng_LA
+        case 0xA5A75553486D6E70LLU: // hnj_Hmnp_US
+        case 0xB5A750484C61746ELLU: // hnn_Latn_PH
+        case 0xB9A7504B41726162LLU: // hno_Arab_PK
+        case 0xC9A753524C61746ELLU: // hns_Latn_SR
+        case 0x686F50474C61746ELLU: // ho_Latn_PG
+        case 0x81C753424C61746ELLU: // hoa_Latn_SB
+        case 0x85C750474C61746ELLU: // hob_Latn_PG
+        case 0x89C7494E44657661LLU: // hoc_Deva_IN
+        case 0x8DC74E474C61746ELLU: // hod_Latn_NG
+        case 0x91C74E474C61746ELLU: // hoe_Latn_NG
+        case 0x9DC74F4D41726162LLU: // hoh_Arab_OM
+        case 0xA1C755534C61746ELLU: // hoi_Latn_US
+        case 0xA5C7494E44657661LLU: // hoj_Deva_IN
+        case 0xADC7414F4C61746ELLU: // hol_Latn_AO
+        case 0xB1C753534C61746ELLU: // hom_Latn_SS
+        case 0xB9C743444C61746ELLU: // hoo_Latn_CD
+        case 0xBDC755534C61746ELLU: // hop_Latn_US
+        case 0xC5C754444C61746ELLU: // hor_Latn_TD
+        case 0xCDC750474C61746ELLU: // hot_Latn_PG
+        case 0xD5C749444C61746ELLU: // hov_Latn_ID
+        case 0xD9C7434E48616E69LLU: // how_Hani_CN
+        case 0xE1C7494E44657661LLU: // hoy_Deva_IN
+        case 0xB9E74D4D4D796D72LLU: // hpo_Mymr_MM
+        case 0x687248524C61746ELLU: // hr_Latn_HR
+        case 0x8227494E4C61746ELLU: // hra_Latn_IN
+        case 0x8A2750474C61746ELLU: // hrc_Latn_PG
+        case 0x9227564E4C61746ELLU: // hre_Latn_VN
+        case 0xAA2749444C61746ELLU: // hrk_Latn_ID
+        case 0xB227434E4C61746ELLU: // hrm_Latn_CN
+        case 0xBA27564E4C61746ELLU: // hro_Latn_VN
+        case 0xBE2741554C61746ELLU: // hrp_Latn_AU
+        case 0xCE27545253797263LLU: // hrt_Syrc_TR
+        case 0xD227494E4C61746ELLU: // hru_Latn_IN
+        case 0xDA2750474C61746ELLU: // hrw_Latn_PG
+        case 0xDE2742524C61746ELLU: // hrx_Latn_BR
+        case 0xE627495241726162LLU: // hrz_Arab_IR
+        case 0x864744454C61746ELLU: // hsb_Latn_DE
+        case 0xB647434E48616E73LLU: // hsn_Hans_CN
+        case 0xCA474F4D41726162LLU: // hss_Arab_OM
+        case 0x687448544C61746ELLU: // ht_Latn_HT
+        case 0xA26749444C61746ELLU: // hti_Latn_ID
+        case 0xBA67434F4C61746ELLU: // hto_Latn_CO
+        case 0xCA67545A4C61746ELLU: // hts_Latn_TZ
+        case 0xD26749444C61746ELLU: // htu_Latn_ID
+        case 0xDE67545258737578LLU: // htx_Xsux_TR
+        case 0x687548554C61746ELLU: // hu_Latn_HU
+        case 0x868750454C61746ELLU: // hub_Latn_PE
+        case 0x8A8742574C61746ELLU: // huc_Latn_BW
+        case 0x8E8749444C61746ELLU: // hud_Latn_ID
+        case 0x92874D584C61746ELLU: // hue_Latn_MX
+        case 0x968750474C61746ELLU: // huf_Latn_PG
+        case 0x9A8750454C61746ELLU: // hug_Latn_PE
+        case 0x9E87434C4C61746ELLU: // huh_Latn_CL
+        case 0xA28750474C61746ELLU: // hui_Latn_PG
+        case 0xAA8749444C61746ELLU: // huk_Latn_ID
+        case 0xAE8750474C61746ELLU: // hul_Latn_PG
+        case 0xB28743444C61746ELLU: // hum_Latn_CD
+        case 0xBE8755534C61746ELLU: // hup_Latn_US
+        case 0xC68743414C61746ELLU: // hur_Latn_CA
+        case 0xCA874D584C61746ELLU: // hus_Latn_MX
+        case 0xCE874E5044657661LLU: // hut_Deva_NP
+        case 0xD28750454C61746ELLU: // huu_Latn_PE
+        case 0xD6874D584C61746ELLU: // huv_Latn_MX
+        case 0xDA8749444C61746ELLU: // huw_Latn_ID
+        case 0xDE8750454C61746ELLU: // hux_Latn_PE
+        case 0xE287494C48656272LLU: // huy_Hebr_IL
+        case 0xE68752554379726CLLU: // huz_Cyrl_RU
+        case 0x8AA748544C61746ELLU: // hvc_Latn_HT
+        case 0x92A74D584C61746ELLU: // hve_Latn_MX
+        case 0xAAA74E434C61746ELLU: // hvk_Latn_NC
+        case 0xB6A749444C61746ELLU: // hvn_Latn_ID
+        case 0xD6A74D584C61746ELLU: // hvv_Latn_MX
+        case 0x82C743494C61746ELLU: // hwa_Latn_CI
+        case 0x8AC755534C61746ELLU: // hwc_Latn_US
+        case 0xBAC74E474C61746ELLU: // hwo_Latn_NG
+        case 0x6879414D41726D6ELLU: // hy_Armn_AM
+        case 0x8307434D4C61746ELLU: // hya_Latn_CM
+        case 0xDB07414D41726D6ELLU: // hyw_Armn_AM
+        case 0x687A4E414C61746ELLU: // hz_Latn_NA
+        case 0xA0084E434C61746ELLU: // iai_Latn_NC
+        case 0xB40850474C61746ELLU: // ian_Latn_PG
+        case 0xC40850474C61746ELLU: // iar_Latn_PG
+        case 0x80284D594C61746ELLU: // iba_Latn_MY
+        case 0x84284E474C61746ELLU: // ibb_Latn_NG
+        case 0x8C2841554C61746ELLU: // ibd_Latn_AU
+        case 0x90284E474C61746ELLU: // ibe_Latn_NG
+        case 0x982850484C61746ELLU: // ibg_Latn_PH
+        case 0x9C28564E4C61746ELLU: // ibh_Latn_VN
+        case 0xAC2850484C61746ELLU: // ibl_Latn_PH
+        case 0xB0284E474C61746ELLU: // ibm_Latn_NG
+        case 0xB4284E474C61746ELLU: // ibn_Latn_NG
+        case 0xC4284E474C61746ELLU: // ibr_Latn_NG
+        case 0xD02849444C61746ELLU: // ibu_Latn_ID
+        case 0xE0284E474C61746ELLU: // iby_Latn_NG
+        case 0x8048424A4C61746ELLU: // ica_Latn_BJ
+        case 0x9C484E474C61746ELLU: // ich_Latn_NG
+        case 0xC448434F4C61746ELLU: // icr_Latn_CO
+        case 0x696449444C61746ELLU: // id_Latn_ID
+        case 0x80684B454C61746ELLU: // ida_Latn_KE
+        case 0x8468494E4C61746ELLU: // idb_Latn_IN
+        case 0x88684E474C61746ELLU: // idc_Latn_NG
+        case 0x8C68424A4C61746ELLU: // idd_Latn_BJ
+        case 0x90684E474C61746ELLU: // ide_Latn_NG
+        case 0xA06850474C61746ELLU: // idi_Latn_PG
+        case 0xC46853534C61746ELLU: // idr_Latn_SS
+        case 0xC8684E474C61746ELLU: // ids_Latn_NG
+        case 0xCC68544C4C61746ELLU: // idt_Latn_TL
+        case 0xD0684E474C61746ELLU: // idu_Latn_NG
+        case 0x696545454C61746ELLU: // ie_Latn_EE
+        case 0x80A850484C61746ELLU: // ifa_Latn_PH
+        case 0x84A850484C61746ELLU: // ifb_Latn_PH
+        case 0x90A854474C61746ELLU: // ife_Latn_TG
+        case 0x94A856554C61746ELLU: // iff_Latn_VU
+        case 0xA8A850484C61746ELLU: // ifk_Latn_PH
+        case 0xB0A843474C61746ELLU: // ifm_Latn_CG
+        case 0xD0A850484C61746ELLU: // ifu_Latn_PH
+        case 0xE0A850484C61746ELLU: // ify_Latn_PH
+        case 0x69674E474C61746ELLU: // ig_Latn_NG
+        case 0x84C84E474C61746ELLU: // igb_Latn_NG
+        case 0x90C84E474C61746ELLU: // ige_Latn_NG
+        case 0x98C850474C61746ELLU: // igg_Latn_PG
+        case 0xACC84E474C61746ELLU: // igl_Latn_NG
+        case 0xB0C850474C61746ELLU: // igm_Latn_PG
+        case 0xB4C8424F4C61746ELLU: // ign_Latn_BO
+        case 0xB8C850474C61746ELLU: // igo_Latn_PG
+        case 0xD8C84E474C61746ELLU: // igw_Latn_NG
+        case 0x84E849444C61746ELLU: // ihb_Latn_ID
+        case 0xA0E84E474C61746ELLU: // ihi_Latn_NG
+        case 0xBCE849444C61746ELLU: // ihp_Latn_ID
+        case 0xD8E841554C61746ELLU: // ihw_Latn_AU
+        case 0x6969434E59696969LLU: // ii_Yiii_CN
+        case 0xB50841554C61746ELLU: // iin_Latn_AU
+        case 0x89284E474C61746ELLU: // ijc_Latn_NG
+        case 0x91284E474C61746ELLU: // ije_Latn_NG
+        case 0xA528424A4C61746ELLU: // ijj_Latn_BJ
+        case 0xB5284E474C61746ELLU: // ijn_Latn_NG
+        case 0xC9284E474C61746ELLU: // ijs_Latn_NG
+        case 0x696B55534C61746ELLU: // ik_Latn_US
+        case 0x9D484E474C61746ELLU: // ikh_Latn_NG
+        case 0xA1484E474C61746ELLU: // iki_Latn_NG
+        case 0xA9484E474C61746ELLU: // ikk_Latn_NG
+        case 0xAD484E474C61746ELLU: // ikl_Latn_NG
+        case 0xB9484E474C61746ELLU: // iko_Latn_NG
+        case 0xBD484E474C61746ELLU: // ikp_Latn_NG
+        case 0xC54841554C61746ELLU: // ikr_Latn_AU
+        case 0xCD4843414C61746ELLU: // ikt_Latn_CA
+        case 0xD5484E474C61746ELLU: // ikv_Latn_NG
+        case 0xD9484E474C61746ELLU: // ikw_Latn_NG
+        case 0xDD4855474C61746ELLU: // ikx_Latn_UG
+        case 0xE548545A4C61746ELLU: // ikz_Latn_TZ
+        case 0x816849444C61746ELLU: // ila_Latn_ID
+        case 0x85685A4D4C61746ELLU: // ilb_Latn_ZM
+        case 0x996841554C61746ELLU: // ilg_Latn_AU
+        case 0xA168434E4C61746ELLU: // ili_Latn_CN
+        case 0xA96850484C61746ELLU: // ilk_Latn_PH
+        case 0xB1684D594C61746ELLU: // ilm_Latn_MY
+        case 0xB96850484C61746ELLU: // ilo_Latn_PH
+        case 0xBD6850484C61746ELLU: // ilp_Latn_PH
+        case 0xD16849444C61746ELLU: // ilu_Latn_ID
+        case 0xD5684E474C61746ELLU: // ilv_Latn_NG
+        case 0xA18850474C61746ELLU: // imi_Latn_PG
+        case 0xAD8855534C61746ELLU: // iml_Latn_US
+        case 0xB58850474C61746ELLU: // imn_Latn_PG
+        case 0xB98850474C61746ELLU: // imo_Latn_PG
+        case 0xC58849444C61746ELLU: // imr_Latn_ID
+        case 0xC98849544C61746ELLU: // ims_Latn_IT
+        case 0xCD8853534C61746ELLU: // imt_Latn_SS
+        case 0xE18854524C796369LLU: // imy_Lyci_TR
+        case 0x696E49444C61746ELLU: // in_Latn_ID
+        case 0x85A8434F4C61746ELLU: // inb_Latn_CO
+        case 0x99A855534C61746ELLU: // ing_Latn_US
+        case 0x9DA852554379726CLLU: // inh_Cyrl_RU
+        case 0xA5A8434F4C61746ELLU: // inj_Latn_CO
+        case 0xB5A850484C61746ELLU: // inn_Latn_PH
+        case 0xB9A850474C61746ELLU: // ino_Latn_PG
+        case 0xBDA850454C61746ELLU: // inp_Latn_PE
+        case 0xCDA84D4D4D796D72LLU: // int_Mymr_MM
+        case 0xC5C8455445746869LLU: // ior_Ethi_ET
+        case 0xD1C850474C61746ELLU: // iou_Latn_PG
+        case 0xD9C855534C61746ELLU: // iow_Latn_US
+        case 0xA1E850474C61746ELLU: // ipi_Latn_PG
+        case 0xB9E850474C61746ELLU: // ipo_Latn_PG
+        case 0xD20850454C61746ELLU: // iqu_Latn_PE
+        case 0xDA084E474C61746ELLU: // iqw_Latn_NG
+        case 0x922849444C61746ELLU: // ire_Latn_ID
+        case 0x9E2849444C61746ELLU: // irh_Latn_ID
+        case 0xA2284E474C61746ELLU: // iri_Latn_NG
+        case 0xAA28545A4C61746ELLU: // irk_Latn_TZ
+        case 0xB62842524C61746ELLU: // irn_Latn_BR
+        case 0xD228494E54616D6CLLU: // iru_Taml_IN
+        case 0xDE2849444C61746ELLU: // irx_Latn_ID
+        case 0xE22850484C61746ELLU: // iry_Latn_PH
+        case 0x697349534C61746ELLU: // is_Latn_IS
+        case 0x824850474C61746ELLU: // isa_Latn_PG
+        case 0x8A4850454C61746ELLU: // isc_Latn_PE
+        case 0x8E4850484C61746ELLU: // isd_Latn_PH
+        case 0x9E484E474C61746ELLU: // ish_Latn_NG
+        case 0xA2484E474C61746ELLU: // isi_Latn_NG
+        case 0xAA48414641726162LLU: // isk_Arab_AF
+        case 0xB24849444C61746ELLU: // ism_Latn_ID
+        case 0xB648545A4C61746ELLU: // isn_Latn_TZ
+        case 0xBA484E474C61746ELLU: // iso_Latn_NG
+        case 0xCE4848524C61746ELLU: // ist_Latn_HR
+        case 0xD248434D4C61746ELLU: // isu_Latn_CM
+        case 0x697449544C61746ELLU: // it_Latn_IT
+        case 0x866850484C61746ELLU: // itb_Latn_PH
+        case 0x8E6849444C61746ELLU: // itd_Latn_ID
+        case 0x9268424F4C61746ELLU: // ite_Latn_BO
+        case 0xA26850484C61746ELLU: // iti_Latn_PH
+        case 0xAA68495448656272LLU: // itk_Hebr_IT
+        case 0xAE6852554379726CLLU: // itl_Cyrl_RU
+        case 0xB2684E474C61746ELLU: // itm_Latn_NG
+        case 0xBA68424F4C61746ELLU: // ito_Latn_BO
+        case 0xC66850474C61746ELLU: // itr_Latn_PG
+        case 0xCA684E474C61746ELLU: // its_Latn_NG
+        case 0xCE6850484C61746ELLU: // itt_Latn_PH
+        case 0xD66850484C61746ELLU: // itv_Latn_PH
+        case 0xDA684E474C61746ELLU: // itw_Latn_NG
+        case 0xDE6849444C61746ELLU: // itx_Latn_ID
+        case 0xE26850484C61746ELLU: // ity_Latn_PH
+        case 0xE66847544C61746ELLU: // itz_Latn_GT
+        case 0x6975434143616E73LLU: // iu_Cans_CA
+        case 0xB288434E4C61746ELLU: // ium_Latn_CN
+        case 0x86A850484C61746ELLU: // ivb_Latn_PH
+        case 0xD6A850484C61746ELLU: // ivv_Latn_PH
+        case 0x6977494C48656272LLU: // iw_Hebr_IL
+        case 0xAAC850484C61746ELLU: // iwk_Latn_PH
+        case 0xB2C850474C61746ELLU: // iwm_Latn_PG
+        case 0xBAC849444C61746ELLU: // iwo_Latn_ID
+        case 0xCAC850474C61746ELLU: // iws_Latn_PG
+        case 0x8AE84D584C61746ELLU: // ixc_Latn_MX
+        case 0xAEE847544C61746ELLU: // ixl_Latn_GT
+        case 0x83084E474C61746ELLU: // iya_Latn_NG
+        case 0xBB08434D4C61746ELLU: // iyo_Latn_CM
+        case 0xDF0843474C61746ELLU: // iyx_Latn_CG
+        case 0x9F2852554C61746ELLU: // izh_Latn_RU
+        case 0xB3284E474C61746ELLU: // izm_Latn_NG
+        case 0xC7284E474C61746ELLU: // izr_Latn_NG
+        case 0xE7284E474C61746ELLU: // izz_Latn_NG
+        case 0x6A614A504A70616ELLU: // ja_Jpan_JP
+        case 0x800942524C61746ELLU: // jaa_Latn_BR
+        case 0x84094E474C61746ELLU: // jab_Latn_NG
+        case 0x880947544C61746ELLU: // jac_Latn_GT
+        case 0x8C09474E41726162LLU: // jad_Arab_GN
+        case 0x900950474C61746ELLU: // jae_Latn_PG
+        case 0x94094E474C61746ELLU: // jaf_Latn_NG
+        case 0x9C094D594C61746ELLU: // jah_Latn_MY
+        case 0xA40953424C61746ELLU: // jaj_Latn_SB
+        case 0xA8094D594C61746ELLU: // jak_Latn_MY
+        case 0xAC0949444C61746ELLU: // jal_Latn_ID
+        case 0xB0094A4D4C61746ELLU: // jam_Latn_JM
+        case 0xB40941554C61746ELLU: // jan_Latn_AU
+        case 0xB80941554C61746ELLU: // jao_Latn_AU
+        case 0xC00949444C61746ELLU: // jaq_Latn_ID
+        case 0xC8094E434C61746ELLU: // jas_Latn_NC
+        case 0xCC09414641726162LLU: // jat_Arab_AF
+        case 0xD00949444C61746ELLU: // jau_Latn_ID
+        case 0xDC0949444C61746ELLU: // jax_Latn_ID
+        case 0xE00941554C61746ELLU: // jay_Latn_AU
+        case 0xE4094E434C61746ELLU: // jaz_Latn_NC
+        case 0x9029494C48656272LLU: // jbe_Hebr_IL
+        case 0xA02941554C61746ELLU: // jbi_Latn_AU
+        case 0xA42949444C61746ELLU: // jbj_Latn_ID
+        case 0xA82950474C61746ELLU: // jbk_Latn_PG
+        case 0xB0294E474C61746ELLU: // jbm_Latn_NG
+        case 0xB4294C5941726162LLU: // jbn_Arab_LY
+        case 0xC42949444C61746ELLU: // jbr_Latn_ID
+        case 0xCC2942524C61746ELLU: // jbt_Latn_BR
+        case 0xD029434D4C61746ELLU: // jbu_Latn_CM
+        case 0xD82941554C61746ELLU: // jbw_Latn_AU
+        case 0xCC4955414379726CLLU: // jct_Cyrl_UA
+        case 0x8069494E54696274LLU: // jda_Tibt_IN
+        case 0x9869504B41726162LLU: // jdg_Arab_PK
+        case 0xCC6952554379726CLLU: // jdt_Cyrl_RU
+        case 0x848950454C61746ELLU: // jeb_Latn_PE
+        case 0x90894E5044657661LLU: // jee_Deva_NP
+        case 0x9C89564E4C61746ELLU: // jeh_Latn_VN
+        case 0xA08949444C61746ELLU: // jei_Latn_ID
+        case 0xA88943494C61746ELLU: // jek_Latn_CI
+        case 0xAC8949444C61746ELLU: // jel_Latn_ID
+        case 0xB4894E474C61746ELLU: // jen_Latn_NG
+        case 0xC4894E474C61746ELLU: // jer_Latn_NG
+        case 0xCC8950474C61746ELLU: // jet_Latn_PG
+        case 0xD08954444C61746ELLU: // jeu_Latn_TD
+        case 0x84C943444C61746ELLU: // jgb_Latn_CD
+        case 0x90C9474547656F72LLU: // jge_Geor_GE
+        case 0xA8C94E474C61746ELLU: // jgk_Latn_NG
+        case 0xB8C9434D4C61746ELLU: // jgo_Latn_CM
+        case 0xA0E94D594C61746ELLU: // jhi_Latn_MY
+        case 0x6A69554148656272LLU: // ji_Hebr_UA
+        case 0x8109434D4C61746ELLU: // jia_Latn_CM
+        case 0x85094E474C61746ELLU: // jib_Latn_NG
+        case 0x8909484E4C61746ELLU: // jic_Latn_HN
+        case 0x8D094E474C61746ELLU: // jid_Latn_NG
+        case 0x91094E474C61746ELLU: // jie_Latn_NG
+        case 0x990941554C61746ELLU: // jig_Latn_AU
+        case 0xAD0950474C61746ELLU: // jil_Latn_PG
+        case 0xB109434D4C61746ELLU: // jim_Latn_CM
+        case 0xCD09545A4C61746ELLU: // jit_Latn_TZ
+        case 0xD109434E4C61746ELLU: // jiu_Latn_CN
+        case 0xD50945434C61746ELLU: // jiv_Latn_EC
+        case 0xE109434E4C61746ELLU: // jiy_Latn_CN
+        case 0x91294B5248616E67LLU: // jje_Hang_KR
+        case 0xC5294E474C61746ELLU: // jjr_Latn_NG
+        case 0x814949444C61746ELLU: // jka_Latn_ID
+        case 0xB1494D4D4D796D72LLU: // jkm_Mymr_MM
+        case 0xB94950474C61746ELLU: // jko_Latn_PG
+        case 0xD1494E474C61746ELLU: // jku_Latn_NG
+        case 0x916953444C61746ELLU: // jle_Latn_SD
+        case 0x818950474C61746ELLU: // jma_Latn_PG
+        case 0x85894E474C61746ELLU: // jmb_Latn_NG
+        case 0x8989545A4C61746ELLU: // jmc_Latn_TZ
+        case 0x8D8949444C61746ELLU: // jmd_Latn_ID
+        case 0xA1894E474C61746ELLU: // jmi_Latn_NG
+        case 0xAD894E5044657661LLU: // jml_Deva_NP
+        case 0xB5894D4D4C61746ELLU: // jmn_Latn_MM
+        case 0xC58947484C61746ELLU: // jmr_Latn_GH
+        case 0xC9894E474C61746ELLU: // jms_Latn_NG
+        case 0xD98950474C61746ELLU: // jmw_Latn_PG
+        case 0xDD894D584C61746ELLU: // jmx_Latn_MX
+        case 0x81A9494E54616B72LLU: // jna_Takr_IN
+        case 0x8DA9504B41726162LLU: // jnd_Arab_PK
+        case 0x99A941554C61746ELLU: // jng_Latn_AU
+        case 0xA1A94E474C61746ELLU: // jni_Latn_NG
+        case 0xA5A945544C61746ELLU: // jnj_Latn_ET
+        case 0xADA9494E44657661LLU: // jnl_Deva_IN
+        case 0xC9A9494E44657661LLU: // jns_Deva_IN
+        case 0x85C943444C61746ELLU: // job_Latn_CD
+        case 0x8DC943494C61746ELLU: // jod_Latn_CI
+        case 0x99C9504B41726162LLU: // jog_Arab_PK
+        case 0xC5C9424F4C61746ELLU: // jor_Latn_BO
+        case 0xD9C94D4C4C61746ELLU: // jow_Latn_ML
+        case 0x81E9505348656272LLU: // jpa_Hebr_PS
+        case 0xC5E9494C48656272LLU: // jpr_Hebr_IL
+        case 0xC60950454C61746ELLU: // jqr_Latn_PE
+        case 0x8229564E4C61746ELLU: // jra_Latn_VN
+        case 0x8629494C48656272LLU: // jrb_Hebr_IL
+        case 0xC6294E474C61746ELLU: // jrr_Latn_NG
+        case 0xCE294E474C61746ELLU: // jrt_Latn_NG
+        case 0xD22956454C61746ELLU: // jru_Latn_VE
+        case 0x828942524C61746ELLU: // jua_Latn_BR
+        case 0x86894E474C61746ELLU: // jub_Latn_NG
+        case 0x8E8943494C61746ELLU: // jud_Latn_CI
+        case 0x9E894E474C61746ELLU: // juh_Latn_NG
+        case 0xA28941554C61746ELLU: // jui_Latn_AU
+        case 0xAA894E474C61746ELLU: // juk_Latn_NG
+        case 0xAE894E5044657661LLU: // jul_Deva_NP
+        case 0xB28953444C61746ELLU: // jum_Latn_SD
+        case 0xB689494E4F727961LLU: // jun_Orya_IN
+        case 0xBA894E474C61746ELLU: // juo_Latn_NG
+        case 0xBE8942524C61746ELLU: // jup_Latn_BR
+        case 0xC68942524C61746ELLU: // jur_Latn_BR
+        case 0xCE89444B4C61746ELLU: // jut_Latn_DK
+        case 0xD2894E474C61746ELLU: // juu_Latn_NG
+        case 0xDA894E474C61746ELLU: // juw_Latn_NG
+        case 0xE289494E4F727961LLU: // juy_Orya_IN
+        case 0x6A7649444C61746ELLU: // jv_Latn_ID
+        case 0x8EA949444C61746ELLU: // jvd_Latn_ID
+        case 0xB6A953524C61746ELLU: // jvn_Latn_SR
+        case 0x6A7749444C61746ELLU: // jw_Latn_ID
+        case 0xA2C947484C61746ELLU: // jwi_Latn_GH
+        case 0x8309434E54696274LLU: // jya_Tibt_CN
+        case 0x9309494C48656272LLU: // jye_Hebr_IL
+        case 0xE30954444C61746ELLU: // jyy_Latn_TD
+        case 0x6B61474547656F72LLU: // ka_Geor_GE
+        case 0x800A555A4379726CLLU: // kaa_Cyrl_UZ
+        case 0x840A445A4C61746ELLU: // kab_Latn_DZ
+        case 0x880A4D4D4C61746ELLU: // kac_Latn_MM
+        case 0x8C0A4E474C61746ELLU: // kad_Latn_NG
+        case 0x980A4D594C61746ELLU: // kag_Latn_MY
+        case 0x9C0A43464C61746ELLU: // kah_Latn_CF
+        case 0xA00A4E474C61746ELLU: // kai_Latn_NG
+        case 0xA40A4E474C61746ELLU: // kaj_Latn_NG
+        case 0xA80A50484C61746ELLU: // kak_Latn_PH
+        case 0xB00A4B454C61746ELLU: // kam_Latn_KE
+        case 0xB80A4D4C4C61746ELLU: // kao_Latn_ML
+        case 0xBC0A52554379726CLLU: // kap_Cyrl_RU
+        case 0xC00A50454C61746ELLU: // kaq_Latn_PE
+        case 0xD40A42524C61746ELLU: // kav_Latn_BR
+        case 0xD80A49444B617769LLU: // kaw_Kawi_ID
+        case 0xDC0A49444C61746ELLU: // kax_Latn_ID
+        case 0xE00A42524C61746ELLU: // kay_Latn_BR
+        case 0x802A41554C61746ELLU: // kba_Latn_AU
+        case 0x842A42524C61746ELLU: // kbb_Latn_BR
+        case 0x882A42524C61746ELLU: // kbc_Latn_BR
+        case 0x8C2A52554379726CLLU: // kbd_Cyrl_RU
+        case 0x902A41554C61746ELLU: // kbe_Latn_AU
+        case 0x982A494E54696274LLU: // kbg_Tibt_IN
+        case 0x9C2A434F4C61746ELLU: // kbh_Latn_CO
+        case 0xA02A49444C61746ELLU: // kbi_Latn_ID
+        case 0xA42A43444C61746ELLU: // kbj_Latn_CD
+        case 0xA82A50474C61746ELLU: // kbk_Latn_PG
+        case 0xAC2A54444C61746ELLU: // kbl_Latn_TD
+        case 0xB02A50474C61746ELLU: // kbm_Latn_PG
+        case 0xB42A43464C61746ELLU: // kbn_Latn_CF
+        case 0xB82A53534C61746ELLU: // kbo_Latn_SS
+        case 0xBC2A54474C61746ELLU: // kbp_Latn_TG
+        case 0xC02A50474C61746ELLU: // kbq_Latn_PG
+        case 0xC42A45544C61746ELLU: // kbr_Latn_ET
+        case 0xC82A47414C61746ELLU: // kbs_Latn_GA
+        case 0xCC2A50474C61746ELLU: // kbt_Latn_PG
+        case 0xD02A504B41726162LLU: // kbu_Arab_PK
+        case 0xD42A49444C61746ELLU: // kbv_Latn_ID
+        case 0xD82A50474C61746ELLU: // kbw_Latn_PG
+        case 0xDC2A50474C61746ELLU: // kbx_Latn_PG
+        case 0xE02A4E4541726162LLU: // kby_Arab_NE
+        case 0xE42A4E474C61746ELLU: // kbz_Latn_NG
+        case 0x804A52554379726CLLU: // kca_Cyrl_RU
+        case 0x844A50474C61746ELLU: // kcb_Latn_PG
+        case 0x884A4E474C61746ELLU: // kcc_Latn_NG
+        case 0x8C4A49444C61746ELLU: // kcd_Latn_ID
+        case 0x904A4E474C61746ELLU: // kce_Latn_NG
+        case 0x944A4E474C61746ELLU: // kcf_Latn_NG
+        case 0x984A4E474C61746ELLU: // kcg_Latn_NG
+        case 0x9C4A4E474C61746ELLU: // kch_Latn_NG
+        case 0xA04A4E474C61746ELLU: // kci_Latn_NG
+        case 0xA44A47574C61746ELLU: // kcj_Latn_GW
+        case 0xA84A5A574C61746ELLU: // kck_Latn_ZW
+        case 0xAC4A50474C61746ELLU: // kcl_Latn_PG
+        case 0xB04A43464C61746ELLU: // kcm_Latn_CF
+        case 0xB44A55474C61746ELLU: // kcn_Latn_UG
+        case 0xB84A50474C61746ELLU: // kco_Latn_PG
+        case 0xBC4A53444C61746ELLU: // kcp_Latn_SD
+        case 0xC04A4E474C61746ELLU: // kcq_Latn_NG
+        case 0xC84A4E474C61746ELLU: // kcs_Latn_NG
+        case 0xCC4A50474C61746ELLU: // kct_Latn_PG
+        case 0xD04A545A4C61746ELLU: // kcu_Latn_TZ
+        case 0xD44A43444C61746ELLU: // kcv_Latn_CD
+        case 0xD84A43444C61746ELLU: // kcw_Latn_CD
+        case 0xE04A445A41726162LLU: // kcy_Arab_DZ
+        case 0xE44A545A4C61746ELLU: // kcz_Latn_TZ
+        case 0x806A41554C61746ELLU: // kda_Latn_AU
+        case 0x886A545A4C61746ELLU: // kdc_Latn_TZ
+        case 0x8C6A41554C61746ELLU: // kdd_Latn_AU
+        case 0x906A545A4C61746ELLU: // kde_Latn_TZ
+        case 0x946A50474C61746ELLU: // kdf_Latn_PG
+        case 0x986A43444C61746ELLU: // kdg_Latn_CD
+        case 0x9C6A54474C61746ELLU: // kdh_Latn_TG
+        case 0xA06A55474C61746ELLU: // kdi_Latn_UG
+        case 0xA46A55474C61746ELLU: // kdj_Latn_UG
+        case 0xA86A4E434C61746ELLU: // kdk_Latn_NC
+        case 0xAC6A4E474C61746ELLU: // kdl_Latn_NG
+        case 0xB06A4E474C61746ELLU: // kdm_Latn_NG
+        case 0xB46A5A574C61746ELLU: // kdn_Latn_ZW
+        case 0xBC6A4E474C61746ELLU: // kdp_Latn_NG
+        case 0xC06A494E42656E67LLU: // kdq_Beng_IN
+        case 0xC46A4C544C61746ELLU: // kdr_Latn_LT
+        case 0xCC6A544854686169LLU: // kdt_Thai_TH
+        case 0xD86A49444C61746ELLU: // kdw_Latn_ID
+        case 0xDC6A4E474C61746ELLU: // kdx_Latn_NG
+        case 0xE06A49444C61746ELLU: // kdy_Latn_ID
+        case 0xE46A434D4C61746ELLU: // kdz_Latn_CM
+        case 0x808A43564C61746ELLU: // kea_Latn_CV
+        case 0x848A47414C61746ELLU: // keb_Latn_GA
+        case 0x888A53444C61746ELLU: // kec_Latn_SD
+        case 0x8C8A545A4C61746ELLU: // ked_Latn_TZ
+        case 0x908A55534C61746ELLU: // kee_Latn_US
+        case 0x948A54474C61746ELLU: // kef_Latn_TG
+        case 0x988A53444C61746ELLU: // keg_Latn_SD
+        case 0x9C8A50474C61746ELLU: // keh_Latn_PG
+        case 0xA08A49444C61746ELLU: // kei_Latn_ID
+        case 0xA88A47544C61746ELLU: // kek_Latn_GT
+        case 0xAC8A43444C61746ELLU: // kel_Latn_CD
+        case 0xB08A544C4C61746ELLU: // kem_Latn_TL
+        case 0xB48A434D4C61746ELLU: // ken_Latn_CM
+        case 0xB88A55474C61746ELLU: // keo_Latn_UG
+        case 0xC48A54444C61746ELLU: // ker_Latn_TD
+        case 0xC88A4E474C61746ELLU: // kes_Latn_NG
+        case 0xCC8A52554379726CLLU: // ket_Cyrl_RU
+        case 0xD08A54474C61746ELLU: // keu_Latn_TG
+        case 0xD48A494E4D6C796DLLU: // kev_Mlym_IN
+        case 0xD88A50474C61746ELLU: // kew_Latn_PG
+        case 0xDC8A494E44657661LLU: // kex_Deva_IN
+        case 0xE08A494E54656C75LLU: // key_Telu_IN
+        case 0xE48A4E474C61746ELLU: // kez_Latn_NG
+        case 0x80AA494E4B6E6461LLU: // kfa_Knda_IN
+        case 0x84AA494E44657661LLU: // kfb_Deva_IN
+        case 0x88AA494E54656C75LLU: // kfc_Telu_IN
+        case 0x8CAA494E4B6E6461LLU: // kfd_Knda_IN
+        case 0x90AA494E54616D6CLLU: // kfe_Taml_IN
+        case 0x94AA494E4C61746ELLU: // kff_Latn_IN
+        case 0x98AA494E4B6E6461LLU: // kfg_Knda_IN
+        case 0x9CAA494E4D6C796DLLU: // kfh_Mlym_IN
+        case 0xA0AA494E54616D6CLLU: // kfi_Taml_IN
+        case 0xA8AA494E44657661LLU: // kfk_Deva_IN
+        case 0xACAA434D4C61746ELLU: // kfl_Latn_CM
+        case 0xB0AA495241726162LLU: // kfm_Arab_IR
+        case 0xB4AA434D4C61746ELLU: // kfn_Latn_CM
+        case 0xB8AA43494C61746ELLU: // kfo_Latn_CI
+        case 0xBCAA494E44657661LLU: // kfp_Deva_IN
+        case 0xC0AA494E44657661LLU: // kfq_Deva_IN
+        case 0xC4AA494E44657661LLU: // kfr_Deva_IN
+        case 0xC8AA494E44657661LLU: // kfs_Deva_IN
+        case 0xD0AA494E44657661LLU: // kfu_Deva_IN
+        case 0xD4AA494E4C61746ELLU: // kfv_Latn_IN
+        case 0xD8AA494E4C61746ELLU: // kfw_Latn_IN
+        case 0xDCAA494E44657661LLU: // kfx_Deva_IN
+        case 0xE0AA494E44657661LLU: // kfy_Deva_IN
+        case 0xE4AA42464C61746ELLU: // kfz_Latn_BF
+        case 0x6B6743444C61746ELLU: // kg_Latn_CD
+        case 0x80CA43494C61746ELLU: // kga_Latn_CI
+        case 0x84CA49444C61746ELLU: // kgb_Latn_ID
+        case 0x90CA49444C61746ELLU: // kge_Latn_ID
+        case 0x94CA50474C61746ELLU: // kgf_Latn_PG
+        case 0xA4CA4E5044657661LLU: // kgj_Deva_NP
+        case 0xA8CA42524C61746ELLU: // kgk_Latn_BR
+        case 0xACCA41554C61746ELLU: // kgl_Latn_AU
+        case 0xB8CA53444C61746ELLU: // kgo_Latn_SD
+        case 0xBCCA42524C61746ELLU: // kgp_Latn_BR
+        case 0xC0CA49444C61746ELLU: // kgq_Latn_ID
+        case 0xC4CA49444C61746ELLU: // kgr_Latn_ID
+        case 0xC8CA41554C61746ELLU: // kgs_Latn_AU
+        case 0xCCCA4E474C61746ELLU: // kgt_Latn_NG
+        case 0xD0CA50474C61746ELLU: // kgu_Latn_PG
+        case 0xD4CA49444C61746ELLU: // kgv_Latn_ID
+        case 0xD8CA49444C61746ELLU: // kgw_Latn_ID
+        case 0xDCCA49444C61746ELLU: // kgx_Latn_ID
+        case 0xE0CA4E5044657661LLU: // kgy_Deva_NP
+        case 0x80EA494E4C61746ELLU: // kha_Latn_IN
+        case 0x84EA434E54616C75LLU: // khb_Talu_CN
+        case 0x88EA49444C61746ELLU: // khc_Latn_ID
+        case 0x8CEA49444C61746ELLU: // khd_Latn_ID
+        case 0x90EA49444C61746ELLU: // khe_Latn_ID
+        case 0x94EA4C4154686169LLU: // khf_Thai_LA
+        case 0x98EA434E54696274LLU: // khg_Tibt_CN
+        case 0x9CEA49444C61746ELLU: // khh_Latn_ID
+        case 0xA4EA4E474C61746ELLU: // khj_Latn_NG
+        case 0xACEA50474C61746ELLU: // khl_Latn_PG
+        case 0xB4EA494E44657661LLU: // khn_Deva_IN
+        case 0xB8EA495242726168LLU: // kho_Brah_IR
+        case 0xBCEA49444C61746ELLU: // khp_Latn_ID
+        case 0xC0EA4D4C4C61746ELLU: // khq_Latn_ML
+        case 0xC4EA494E4C61746ELLU: // khr_Latn_IN
+        case 0xC8EA50474C61746ELLU: // khs_Latn_PG
+        case 0xCCEA494E4D796D72LLU: // kht_Mymr_IN
+        case 0xD0EA414F4C61746ELLU: // khu_Latn_AO
+        case 0xD4EA52554379726CLLU: // khv_Cyrl_RU
+        case 0xD8EA504B41726162LLU: // khw_Arab_PK
+        case 0xDCEA43444C61746ELLU: // khx_Latn_CD
+        case 0xE0EA43444C61746ELLU: // khy_Latn_CD
+        case 0xE4EA50474C61746ELLU: // khz_Latn_PG
+        case 0x6B694B454C61746ELLU: // ki_Latn_KE
+        case 0x810A54444C61746ELLU: // kia_Latn_TD
+        case 0x850A53444C61746ELLU: // kib_Latn_SD
+        case 0x890A55534C61746ELLU: // kic_Latn_US
+        case 0x8D0A434D4C61746ELLU: // kid_Latn_CM
+        case 0x910A54444C61746ELLU: // kie_Latn_TD
+        case 0x950A4E5044657661LLU: // kif_Deva_NP
+        case 0x990A49444C61746ELLU: // kig_Latn_ID
+        case 0x9D0A50474C61746ELLU: // kih_Latn_PG
+        case 0xA50A50474C61746ELLU: // kij_Latn_PG
+        case 0xAD0A4E474C61746ELLU: // kil_Latn_NG
+        case 0xB10A52554379726CLLU: // kim_Cyrl_RU
+        case 0xB90A55534C61746ELLU: // kio_Latn_US
+        case 0xBD0A4E5044657661LLU: // kip_Deva_NP
+        case 0xC10A49444C61746ELLU: // kiq_Latn_ID
+        case 0xC90A50474C61746ELLU: // kis_Latn_PG
+        case 0xCD0A50474C61746ELLU: // kit_Latn_PG
+        case 0xD10A54524C61746ELLU: // kiu_Latn_TR
+        case 0xD50A545A4C61746ELLU: // kiv_Latn_TZ
+        case 0xD90A50474C61746ELLU: // kiw_Latn_PG
+        case 0xDD0A494E4C61746ELLU: // kix_Latn_IN
+        case 0xE10A49444C61746ELLU: // kiy_Latn_ID
+        case 0xE50A545A4C61746ELLU: // kiz_Latn_TZ
+        case 0x6B6A4E414C61746ELLU: // kj_Latn_NA
+        case 0x812A49444C61746ELLU: // kja_Latn_ID
+        case 0x852A47544C61746ELLU: // kjb_Latn_GT
+        case 0x892A49444C61746ELLU: // kjc_Latn_ID
+        case 0x8D2A50474C61746ELLU: // kjd_Latn_PG
+        case 0x912A49444C61746ELLU: // kje_Latn_ID
+        case 0x992A4C414C616F6FLLU: // kjg_Laoo_LA
+        case 0x9D2A52554379726CLLU: // kjh_Cyrl_RU
+        case 0xA12A53424C61746ELLU: // kji_Latn_SB
+        case 0xA52A415A4C61746ELLU: // kjj_Latn_AZ
+        case 0xA92A49444C61746ELLU: // kjk_Latn_ID
+        case 0xAD2A4E5044657661LLU: // kjl_Deva_NP
+        case 0xB12A564E4C61746ELLU: // kjm_Latn_VN
+        case 0xB52A41554C61746ELLU: // kjn_Latn_AU
+        case 0xB92A494E44657661LLU: // kjo_Deva_IN
+        case 0xBD2A4D4D4D796D72LLU: // kjp_Mymr_MM
+        case 0xC12A55534C61746ELLU: // kjq_Latn_US
+        case 0xC52A49444C61746ELLU: // kjr_Latn_ID
+        case 0xC92A50474C61746ELLU: // kjs_Latn_PG
+        case 0xCD2A544854686169LLU: // kjt_Thai_TH
+        case 0xD12A55534C61746ELLU: // kju_Latn_US
+        case 0xDD2A50474C61746ELLU: // kjx_Latn_PG
+        case 0xE12A50474C61746ELLU: // kjy_Latn_PG
+        case 0xE52A425454696274LLU: // kjz_Tibt_BT
+        case 0x6B6B434E41726162LLU: // kk_Arab_CN
+        case 0x6B6B4B5A4379726CLLU: // kk_Cyrl_KZ
+        case 0x814A4E474C61746ELLU: // kka_Latn_NG
+        case 0x854A49444C61746ELLU: // kkb_Latn_ID
+        case 0x894A50474C61746ELLU: // kkc_Latn_PG
+        case 0x8D4A4E474C61746ELLU: // kkd_Latn_NG
+        case 0x914A474E4C61746ELLU: // kke_Latn_GN
+        case 0x954A494E54696274LLU: // kkf_Tibt_IN
+        case 0x994A50484C61746ELLU: // kkg_Latn_PH
+        case 0x9D4A4D4D4C616E61LLU: // kkh_Lana_MM
+        case 0xA14A545A4C61746ELLU: // kki_Latn_TZ
+        case 0xA54A434D4C61746ELLU: // kkj_Latn_CM
+        case 0xA94A53424C61746ELLU: // kkk_Latn_SB
+        case 0xAD4A49444C61746ELLU: // kkl_Latn_ID
+        case 0xB14A4E474C61746ELLU: // kkm_Latn_NG
+        case 0xB94A53444C61746ELLU: // kko_Latn_SD
+        case 0xBD4A41554C61746ELLU: // kkp_Latn_AU
+        case 0xC14A43444C61746ELLU: // kkq_Latn_CD
+        case 0xC54A4E474C61746ELLU: // kkr_Latn_NG
+        case 0xC94A4E474C61746ELLU: // kks_Latn_NG
+        case 0xCD4A4E5044657661LLU: // kkt_Deva_NP
+        case 0xD14A4E474C61746ELLU: // kku_Latn_NG
+        case 0xD54A49444C61746ELLU: // kkv_Latn_ID
+        case 0xD94A43474C61746ELLU: // kkw_Latn_CG
+        case 0xDD4A49444C61746ELLU: // kkx_Latn_ID
+        case 0xE14A41554C61746ELLU: // kky_Latn_AU
+        case 0xE54A43414C61746ELLU: // kkz_Latn_CA
+        case 0x6B6C474C4C61746ELLU: // kl_Latn_GL
+        case 0x816A55534C61746ELLU: // kla_Latn_US
+        case 0x856A4D584C61746ELLU: // klb_Latn_MX
+        case 0x896A434D4C61746ELLU: // klc_Latn_CM
+        case 0x8D6A41554C61746ELLU: // kld_Latn_AU
+        case 0x916A4E5044657661LLU: // kle_Deva_NP
+        case 0x956A54444C61746ELLU: // klf_Latn_TD
+        case 0x996A50484C61746ELLU: // klg_Latn_PH
+        case 0x9D6A50474C61746ELLU: // klh_Latn_PG
+        case 0xA16A49444C61746ELLU: // kli_Latn_ID
+        case 0xA56A495241726162LLU: // klj_Arab_IR
+        case 0xA96A4E474C61746ELLU: // klk_Latn_NG
+        case 0xAD6A50484C61746ELLU: // kll_Latn_PH
+        case 0xB16A50474C61746ELLU: // klm_Latn_PG
+        case 0xB56A4B454C61746ELLU: // kln_Latn_KE
+        case 0xB96A4E474C61746ELLU: // klo_Latn_NG
+        case 0xBD6A50474C61746ELLU: // klp_Latn_PG
+        case 0xC16A50474C61746ELLU: // klq_Latn_PG
+        case 0xC56A4E5044657661LLU: // klr_Deva_NP
+        case 0xC96A504B4C61746ELLU: // kls_Latn_PK
+        case 0xCD6A50474C61746ELLU: // klt_Latn_PG
+        case 0xD16A4C524C61746ELLU: // klu_Latn_LR
+        case 0xD56A56554C61746ELLU: // klv_Latn_VU
+        case 0xD96A49444C61746ELLU: // klw_Latn_ID
+        case 0xDD6A50474C61746ELLU: // klx_Latn_PG
+        case 0xE16A49444C61746ELLU: // kly_Latn_ID
+        case 0xE56A49444C61746ELLU: // klz_Latn_ID
+        case 0x6B6D4B484B686D72LLU: // km_Khmr_KH
+        case 0x818A47484C61746ELLU: // kma_Latn_GH
+        case 0x858A414F4C61746ELLU: // kmb_Latn_AO
+        case 0x898A434E4C61746ELLU: // kmc_Latn_CN
+        case 0x8D8A50484C61746ELLU: // kmd_Latn_PH
+        case 0x918A434D4C61746ELLU: // kme_Latn_CM
+        case 0x958A50474C61746ELLU: // kmf_Latn_PG
+        case 0x998A50474C61746ELLU: // kmg_Latn_PG
+        case 0x9D8A50474C61746ELLU: // kmh_Latn_PG
+        case 0xA18A4E474C61746ELLU: // kmi_Latn_NG
+        case 0xA58A494E44657661LLU: // kmj_Deva_IN
+        case 0xA98A50484C61746ELLU: // kmk_Latn_PH
+        case 0xAD8A50484C61746ELLU: // kml_Latn_PH
+        case 0xB18A494E4C61746ELLU: // kmm_Latn_IN
+        case 0xB58A50474C61746ELLU: // kmn_Latn_PG
+        case 0xB98A50474C61746ELLU: // kmo_Latn_PG
+        case 0xBD8A434D4C61746ELLU: // kmp_Latn_CM
+        case 0xC18A45544C61746ELLU: // kmq_Latn_ET
+        case 0xC98A50474C61746ELLU: // kms_Latn_PG
+        case 0xCD8A49444C61746ELLU: // kmt_Latn_ID
+        case 0xD18A50474C61746ELLU: // kmu_Latn_PG
+        case 0xD58A42524C61746ELLU: // kmv_Latn_BR
+        case 0xD98A43444C61746ELLU: // kmw_Latn_CD
+        case 0xDD8A50474C61746ELLU: // kmx_Latn_PG
+        case 0xE18A4E474C61746ELLU: // kmy_Latn_NG
+        case 0xE58A495241726162LLU: // kmz_Arab_IR
+        case 0x6B6E494E4B6E6461LLU: // kn_Knda_IN
+        case 0x81AA4E474C61746ELLU: // kna_Latn_NG
+        case 0x85AA50484C61746ELLU: // knb_Latn_PH
+        case 0x8DAA49444C61746ELLU: // knd_Latn_ID
+        case 0x91AA50484C61746ELLU: // kne_Latn_PH
+        case 0x95AA47574C61746ELLU: // knf_Latn_GW
+        case 0xA1AA4E474C61746ELLU: // kni_Latn_NG
+        case 0xA5AA47544C61746ELLU: // knj_Latn_GT
+        case 0xA9AA534C4C61746ELLU: // knk_Latn_SL
+        case 0xADAA49444C61746ELLU: // knl_Latn_ID
+        case 0xB1AA42524C61746ELLU: // knm_Latn_BR
+        case 0xB5AA494E44657661LLU: // knn_Deva_IN
+        case 0xB9AA534C4C61746ELLU: // kno_Latn_SL
+        case 0xBDAA434D4C61746ELLU: // knp_Latn_CM
+        case 0xC1AA4D594C61746ELLU: // knq_Latn_MY
+        case 0xC5AA50474C61746ELLU: // knr_Latn_PG
+        case 0xC9AA4D594C61746ELLU: // kns_Latn_MY
+        case 0xCDAA42524C61746ELLU: // knt_Latn_BR
+        case 0xD1AA474E4C61746ELLU: // knu_Latn_GN
+        case 0xD5AA50474C61746ELLU: // knv_Latn_PG
+        case 0xD9AA4E414C61746ELLU: // knw_Latn_NA
+        case 0xDDAA49444C61746ELLU: // knx_Latn_ID
+        case 0xE1AA43444C61746ELLU: // kny_Latn_CD
+        case 0xE5AA42464C61746ELLU: // knz_Latn_BF
+        case 0x6B6F4B524B6F7265LLU: // ko_Kore_KR
+        case 0x81CA50474C61746ELLU: // koa_Latn_PG
+        case 0x89CA4E474C61746ELLU: // koc_Latn_NG
+        case 0x8DCA49444C61746ELLU: // kod_Latn_ID
+        case 0x91CA53534C61746ELLU: // koe_Latn_SS
+        case 0x95CA4E474C61746ELLU: // kof_Latn_NG
+        case 0x99CA434F4C61746ELLU: // kog_Latn_CO
+        case 0x9DCA43474C61746ELLU: // koh_Latn_CG
+        case 0xA1CA52554379726CLLU: // koi_Cyrl_RU
+        case 0xA9CA494E44657661LLU: // kok_Deva_IN
+        case 0xADCA50474C61746ELLU: // kol_Latn_PG
+        case 0xB9CA55474C61746ELLU: // koo_Latn_UG
+        case 0xBDCA50474C61746ELLU: // kop_Latn_PG
+        case 0xC1CA47414C61746ELLU: // koq_Latn_GA
+        case 0xC9CA464D4C61746ELLU: // kos_Latn_FM
+        case 0xCDCA434D4C61746ELLU: // kot_Latn_CM
+        case 0xD1CA54444C61746ELLU: // kou_Latn_TD
+        case 0xD5CA4E474C61746ELLU: // kov_Latn_NG
+        case 0xD9CA4E474C61746ELLU: // kow_Latn_NG
+        case 0xE1CA55534C61746ELLU: // koy_Latn_US
+        case 0xE5CA50474C61746ELLU: // koz_Latn_PG
+        case 0x81EA4E474C61746ELLU: // kpa_Latn_NG
+        case 0x89EA434F4C61746ELLU: // kpc_Latn_CO
+        case 0x8DEA49444C61746ELLU: // kpd_Latn_ID
+        case 0x91EA4C524C61746ELLU: // kpe_Latn_LR
+        case 0x95EA50474C61746ELLU: // kpf_Latn_PG
+        case 0x99EA464D4C61746ELLU: // kpg_Latn_FM
+        case 0x9DEA47484C61746ELLU: // kph_Latn_GH
+        case 0xA1EA49444C61746ELLU: // kpi_Latn_ID
+        case 0xA5EA42524C61746ELLU: // kpj_Latn_BR
+        case 0xA9EA4E474C61746ELLU: // kpk_Latn_NG
+        case 0xADEA43444C61746ELLU: // kpl_Latn_CD
+        case 0xB1EA564E4C61746ELLU: // kpm_Latn_VN
+        case 0xB5EA42524C61746ELLU: // kpn_Latn_BR
+        case 0xB9EA54474C61746ELLU: // kpo_Latn_TG
+        case 0xC1EA49444C61746ELLU: // kpq_Latn_ID
+        case 0xC5EA50474C61746ELLU: // kpr_Latn_PG
+        case 0xC9EA49444C61746ELLU: // kps_Latn_ID
+        case 0xCDEA52554379726CLLU: // kpt_Cyrl_RU
+        case 0xD1EA49444C61746ELLU: // kpu_Latn_ID
+        case 0xD9EA50474C61746ELLU: // kpw_Latn_PG
+        case 0xDDEA50474C61746ELLU: // kpx_Latn_PG
+        case 0xE1EA52554379726CLLU: // kpy_Cyrl_RU
+        case 0xE5EA55474C61746ELLU: // kpz_Latn_UG
+        case 0x820A50474C61746ELLU: // kqa_Latn_PG
+        case 0x860A50474C61746ELLU: // kqb_Latn_PG
+        case 0x8A0A50474C61746ELLU: // kqc_Latn_PG
+        case 0x8E0A495153797263LLU: // kqd_Syrc_IQ
+        case 0x920A50484C61746ELLU: // kqe_Latn_PH
+        case 0x960A50474C61746ELLU: // kqf_Latn_PG
+        case 0x9A0A42464C61746ELLU: // kqg_Latn_BF
+        case 0x9E0A545A4C61746ELLU: // kqh_Latn_TZ
+        case 0xA20A50474C61746ELLU: // kqi_Latn_PG
+        case 0xA60A50474C61746ELLU: // kqj_Latn_PG
+        case 0xAA0A424A4C61746ELLU: // kqk_Latn_BJ
+        case 0xAE0A50474C61746ELLU: // kql_Latn_PG
+        case 0xB20A43494C61746ELLU: // kqm_Latn_CI
+        case 0xB60A5A4D4C61746ELLU: // kqn_Latn_ZM
+        case 0xBA0A4C524C61746ELLU: // kqo_Latn_LR
+        case 0xBE0A54444C61746ELLU: // kqp_Latn_TD
+        case 0xC20A42524C61746ELLU: // kqq_Latn_BR
+        case 0xC60A4D594C61746ELLU: // kqr_Latn_MY
+        case 0xCA0A474E4C61746ELLU: // kqs_Latn_GN
+        case 0xCE0A4D594C61746ELLU: // kqt_Latn_MY
+        case 0xD20A5A414C61746ELLU: // kqu_Latn_ZA
+        case 0xD60A49444C61746ELLU: // kqv_Latn_ID
+        case 0xDA0A50474C61746ELLU: // kqw_Latn_PG
+        case 0xDE0A434D4C61746ELLU: // kqx_Latn_CM
+        case 0xE20A455445746869LLU: // kqy_Ethi_ET
+        case 0xE60A5A414C61746ELLU: // kqz_Latn_ZA
+        case 0x6B724E474C61746ELLU: // kr_Latn_NG
+        case 0x822A4E5044657661LLU: // kra_Deva_NP
+        case 0x862A55534C61746ELLU: // krb_Latn_US
+        case 0x8A2A52554379726CLLU: // krc_Cyrl_RU
+        case 0x8E2A544C4C61746ELLU: // krd_Latn_TL
+        case 0x922A42524C61746ELLU: // kre_Latn_BR
+        case 0x962A56554C61746ELLU: // krf_Latn_VU
+        case 0x9E2A4E474C61746ELLU: // krh_Latn_NG
+        case 0xA22A534C4C61746ELLU: // kri_Latn_SL
+        case 0xA62A50484C61746ELLU: // krj_Latn_PH
+        case 0xAA2A52554379726CLLU: // krk_Cyrl_RU
+        case 0xAE2A52554C61746ELLU: // krl_Latn_RU
+        case 0xB62A4C524C61746ELLU: // krn_Latn_LR
+        case 0xBE2A4E474C61746ELLU: // krp_Latn_NG
+        case 0xC62A4B484B686D72LLU: // krr_Khmr_KH
+        case 0xCA2A53534C61746ELLU: // krs_Latn_SS
+        case 0xCE2A4E454C61746ELLU: // krt_Latn_NE
+        case 0xD22A494E44657661LLU: // kru_Deva_IN
+        case 0xD62A4B484B686D72LLU: // krv_Khmr_KH
+        case 0xDA2A4C524C61746ELLU: // krw_Latn_LR
+        case 0xDE2A534E4C61746ELLU: // krx_Latn_SN
+        case 0xE22A415A4C61746ELLU: // kry_Latn_AZ
+        case 0xE62A49444C61746ELLU: // krz_Latn_ID
+        case 0x6B73494E41726162LLU: // ks_Arab_IN
+        case 0x864A545A4C61746ELLU: // ksb_Latn_TZ
+        case 0x8A4A50484C61746ELLU: // ksc_Latn_PH
+        case 0x8E4A50474C61746ELLU: // ksd_Latn_PG
+        case 0x924A50474C61746ELLU: // kse_Latn_PG
+        case 0x964A434D4C61746ELLU: // ksf_Latn_CM
+        case 0x9A4A53424C61746ELLU: // ksg_Latn_SB
+        case 0x9E4A44454C61746ELLU: // ksh_Latn_DE
+        case 0xA24A50474C61746ELLU: // ksi_Latn_PG
+        case 0xA64A50474C61746ELLU: // ksj_Latn_PG
+        case 0xAA4A55534C61746ELLU: // ksk_Latn_US
+        case 0xAE4A50474C61746ELLU: // ksl_Latn_PG
+        case 0xB24A4E474C61746ELLU: // ksm_Latn_NG
+        case 0xB64A50484C61746ELLU: // ksn_Latn_PH
+        case 0xBA4A4E474C61746ELLU: // kso_Latn_NG
+        case 0xBE4A43464C61746ELLU: // ksp_Latn_CF
+        case 0xC24A4E474C61746ELLU: // ksq_Latn_NG
+        case 0xC64A50474C61746ELLU: // ksr_Latn_PG
+        case 0xCA4A4C524C61746ELLU: // kss_Latn_LR
+        case 0xCE4A42464C61746ELLU: // kst_Latn_BF
+        case 0xD24A494E4D796D72LLU: // ksu_Mymr_IN
+        case 0xD64A43444C61746ELLU: // ksv_Latn_CD
+        case 0xDA4A4D4D4D796D72LLU: // ksw_Mymr_MM
+        case 0xDE4A49444C61746ELLU: // ksx_Latn_ID
+        case 0xE64A494E44657661LLU: // ksz_Deva_IN
+        case 0x826A564E4C61746ELLU: // kta_Latn_VN
+        case 0x866A455445746869LLU: // ktb_Ethi_ET
+        case 0x8A6A4E474C61746ELLU: // ktc_Latn_NG
+        case 0x8E6A41554C61746ELLU: // ktd_Latn_AU
+        case 0x926A4E5044657661LLU: // kte_Deva_NP
+        case 0x966A43444C61746ELLU: // ktf_Latn_CD
+        case 0x9A6A41554C61746ELLU: // ktg_Latn_AU
+        case 0x9E6A54444C61746ELLU: // kth_Latn_TD
+        case 0xA26A49444C61746ELLU: // kti_Latn_ID
+        case 0xA66A43494C61746ELLU: // ktj_Latn_CI
+        case 0xAA6A50474C61746ELLU: // ktk_Latn_PG
+        case 0xAE6A495241726162LLU: // ktl_Arab_IR
+        case 0xB26A50474C61746ELLU: // ktm_Latn_PG
+        case 0xB66A42524C61746ELLU: // ktn_Latn_BR
+        case 0xBA6A50474C61746ELLU: // kto_Latn_PG
+        case 0xBE6A434E506C7264LLU: // ktp_Plrd_CN
+        case 0xC26A50484C61746ELLU: // ktq_Latn_PH
+        case 0xCA6A49444C61746ELLU: // kts_Latn_ID
+        case 0xCE6A49444C61746ELLU: // ktt_Latn_ID
+        case 0xD26A43444C61746ELLU: // ktu_Latn_CD
+        case 0xD66A564E4C61746ELLU: // ktv_Latn_VN
+        case 0xDA6A55534C61746ELLU: // ktw_Latn_US
+        case 0xDE6A42524C61746ELLU: // ktx_Latn_BR
+        case 0xE26A43444C61746ELLU: // kty_Latn_CD
+        case 0xE66A4E414C61746ELLU: // ktz_Latn_NA
+        case 0x6B75495141726162LLU: // ku_Arab_IQ
+        case 0x6B7554524C61746ELLU: // ku_Latn_TR
+        case 0x6B75474559657A69LLU: // ku_Yezi_GE
+        case 0x868A4E474C61746ELLU: // kub_Latn_NG
+        case 0x8A8A49444C61746ELLU: // kuc_Latn_ID
+        case 0x8E8A50474C61746ELLU: // kud_Latn_PG
+        case 0x928A50474C61746ELLU: // kue_Latn_PG
+        case 0x968A4C414C616F6FLLU: // kuf_Laoo_LA
+        case 0x9A8A4E474C61746ELLU: // kug_Latn_NG
+        case 0x9E8A4E474C61746ELLU: // kuh_Latn_NG
+        case 0xA28A42524C61746ELLU: // kui_Latn_BR
+        case 0xA68A545A4C61746ELLU: // kuj_Latn_TZ
+        case 0xAA8A49444C61746ELLU: // kuk_Latn_ID
+        case 0xAE8A4E474C61746ELLU: // kul_Latn_NG
+        case 0xB28A52554379726CLLU: // kum_Cyrl_RU
+        case 0xB68A45524C61746ELLU: // kun_Latn_ER
+        case 0xBA8A50474C61746ELLU: // kuo_Latn_PG
+        case 0xBE8A50474C61746ELLU: // kup_Latn_PG
+        case 0xC28A42524C61746ELLU: // kuq_Latn_BR
+        case 0xCA8A47484C61746ELLU: // kus_Latn_GH
+        case 0xCE8A43414C61746ELLU: // kut_Latn_CA
+        case 0xD28A55534C61746ELLU: // kuu_Latn_US
+        case 0xD68A49444C61746ELLU: // kuv_Latn_ID
+        case 0xDA8A43464C61746ELLU: // kuw_Latn_CF
+        case 0xDE8A41554C61746ELLU: // kux_Latn_AU
+        case 0xE28A41554C61746ELLU: // kuy_Latn_AU
+        case 0xE68A434C4C61746ELLU: // kuz_Latn_CL
+        case 0x6B7652554379726CLLU: // kv_Cyrl_RU
+        case 0x82AA52554379726CLLU: // kva_Cyrl_RU
+        case 0x86AA49444C61746ELLU: // kvb_Latn_ID
+        case 0x8AAA50474C61746ELLU: // kvc_Latn_PG
+        case 0x8EAA49444C61746ELLU: // kvd_Latn_ID
+        case 0x92AA4D594C61746ELLU: // kve_Latn_MY
+        case 0x96AA54444C61746ELLU: // kvf_Latn_TD
+        case 0x9AAA50474C61746ELLU: // kvg_Latn_PG
+        case 0x9EAA49444C61746ELLU: // kvh_Latn_ID
+        case 0xA2AA54444C61746ELLU: // kvi_Latn_TD
+        case 0xA6AA434D4C61746ELLU: // kvj_Latn_CM
+        case 0xAEAA4D4D4C61746ELLU: // kvl_Latn_MM
+        case 0xB2AA434D4C61746ELLU: // kvm_Latn_CM
+        case 0xB6AA434F4C61746ELLU: // kvn_Latn_CO
+        case 0xBAAA49444C61746ELLU: // kvo_Latn_ID
+        case 0xBEAA49444C61746ELLU: // kvp_Latn_ID
+        case 0xC2AA4D4D4D796D72LLU: // kvq_Mymr_MM
+        case 0xC6AA49444C61746ELLU: // kvr_Latn_ID
+        case 0xCEAA4D4D4D796D72LLU: // kvt_Mymr_MM
+        case 0xD6AA49444C61746ELLU: // kvv_Latn_ID
+        case 0xDAAA49444C61746ELLU: // kvw_Latn_ID
+        case 0xDEAA504B41726162LLU: // kvx_Arab_PK
+        case 0xE2AA4D4D4B616C69LLU: // kvy_Kali_MM
+        case 0xE6AA49444C61746ELLU: // kvz_Latn_ID
+        case 0x6B7747424C61746ELLU: // kw_Latn_GB
+        case 0x82CA42524C61746ELLU: // kwa_Latn_BR
+        case 0x86CA4E474C61746ELLU: // kwb_Latn_NG
+        case 0x8ACA43474C61746ELLU: // kwc_Latn_CG
+        case 0x8ECA53424C61746ELLU: // kwd_Latn_SB
+        case 0x92CA49444C61746ELLU: // kwe_Latn_ID
+        case 0x96CA53424C61746ELLU: // kwf_Latn_SB
+        case 0x9ACA54444C61746ELLU: // kwg_Latn_TD
+        case 0x9ECA49444C61746ELLU: // kwh_Latn_ID
+        case 0xA2CA434F4C61746ELLU: // kwi_Latn_CO
+        case 0xA6CA50474C61746ELLU: // kwj_Latn_PG
+        case 0xAACA43414C61746ELLU: // kwk_Latn_CA
+        case 0xAECA4E474C61746ELLU: // kwl_Latn_NG
+        case 0xB2CA4E414C61746ELLU: // kwm_Latn_NA
+        case 0xB6CA4E414C61746ELLU: // kwn_Latn_NA
+        case 0xBACA50474C61746ELLU: // kwo_Latn_PG
+        case 0xBECA43494C61746ELLU: // kwp_Latn_CI
+        case 0xC6CA49444C61746ELLU: // kwr_Latn_ID
+        case 0xCACA43444C61746ELLU: // kws_Latn_CD
+        case 0xCECA49444C61746ELLU: // kwt_Latn_ID
+        case 0xD2CA434D4C61746ELLU: // kwu_Latn_CM
+        case 0xD6CA54444C61746ELLU: // kwv_Latn_TD
+        case 0xDACA53524C61746ELLU: // kww_Latn_SR
+        case 0xE2CA414F4C61746ELLU: // kwy_Latn_AO
+        case 0xE6CA414F4C61746ELLU: // kwz_Latn_AO
+        case 0x82EA50474C61746ELLU: // kxa_Latn_PG
+        case 0x86EA43494C61746ELLU: // kxb_Latn_CI
+        case 0x8AEA45544C61746ELLU: // kxc_Latn_ET
+        case 0x8EEA424E4C61746ELLU: // kxd_Latn_BN
+        case 0x96EA4D4D4D796D72LLU: // kxf_Mymr_MM
+        case 0xA2EA4D594C61746ELLU: // kxi_Latn_MY
+        case 0xA6EA54444C61746ELLU: // kxj_Latn_TD
+        case 0xAAEA4D4D4D796D72LLU: // kxk_Mymr_MM
+        case 0xB2EA544854686169LLU: // kxm_Thai_TH
+        case 0xB6EA4D594C61746ELLU: // kxn_Latn_MY
+        case 0xBAEA42524C61746ELLU: // kxo_Latn_BR
+        case 0xBEEA504B41726162LLU: // kxp_Arab_PK
+        case 0xC2EA49444C61746ELLU: // kxq_Latn_ID
+        case 0xC6EA50474C61746ELLU: // kxr_Latn_PG
+        case 0xCEEA50474C61746ELLU: // kxt_Latn_PG
+        case 0xD6EA494E4C61746ELLU: // kxv_Latn_IN
+        case 0xDAEA50474C61746ELLU: // kxw_Latn_PG
+        case 0xDEEA43474C61746ELLU: // kxx_Latn_CG
+        case 0xE2EA564E4C61746ELLU: // kxy_Latn_VN
+        case 0xE6EA50474C61746ELLU: // kxz_Latn_PG
+        case 0x6B79434E41726162LLU: // ky_Arab_CN
+        case 0x6B794B474379726CLLU: // ky_Cyrl_KG
+        case 0x6B7954524C61746ELLU: // ky_Latn_TR
+        case 0x830A545A4C61746ELLU: // kya_Latn_TZ
+        case 0x870A50484C61746ELLU: // kyb_Latn_PH
+        case 0x8B0A50474C61746ELLU: // kyc_Latn_PG
+        case 0x8F0A49444C61746ELLU: // kyd_Latn_ID
+        case 0x930A47484C61746ELLU: // kye_Latn_GH
+        case 0x970A43494C61746ELLU: // kyf_Latn_CI
+        case 0x9B0A50474C61746ELLU: // kyg_Latn_PG
+        case 0x9F0A55534C61746ELLU: // kyh_Latn_US
+        case 0xA30A4D594C61746ELLU: // kyi_Latn_MY
+        case 0xA70A50484C61746ELLU: // kyj_Latn_PH
+        case 0xAB0A50484C61746ELLU: // kyk_Latn_PH
+        case 0xAF0A55534C61746ELLU: // kyl_Latn_US
+        case 0xB30A43464C61746ELLU: // kym_Latn_CF
+        case 0xB70A50484C61746ELLU: // kyn_Latn_PH
+        case 0xBB0A49444C61746ELLU: // kyo_Latn_ID
+        case 0xC30A54444C61746ELLU: // kyq_Latn_TD
+        case 0xC70A42524C61746ELLU: // kyr_Latn_BR
+        case 0xCB0A4D594C61746ELLU: // kys_Latn_MY
+        case 0xCF0A49444C61746ELLU: // kyt_Latn_ID
+        case 0xD30A4D4D4B616C69LLU: // kyu_Kali_MM
+        case 0xD70A4E5044657661LLU: // kyv_Deva_NP
+        case 0xDB0A494E44657661LLU: // kyw_Deva_IN
+        case 0xDF0A50474C61746ELLU: // kyx_Latn_PG
+        case 0xE30A50474C61746ELLU: // kyy_Latn_PG
+        case 0xE70A42524C61746ELLU: // kyz_Latn_BR
+        case 0x832A42464C61746ELLU: // kza_Latn_BF
+        case 0x872A49444C61746ELLU: // kzb_Latn_ID
+        case 0x8B2A43494C61746ELLU: // kzc_Latn_CI
+        case 0x8F2A49444C61746ELLU: // kzd_Latn_ID
+        case 0x932A50474C61746ELLU: // kze_Latn_PG
+        case 0x972A49444C61746ELLU: // kzf_Latn_ID
+        case 0xA32A4D594C61746ELLU: // kzi_Latn_MY
+        case 0xAB2A53424C61746ELLU: // kzk_Latn_SB
+        case 0xAF2A49444C61746ELLU: // kzl_Latn_ID
+        case 0xB32A49444C61746ELLU: // kzm_Latn_ID
+        case 0xB72A4D574C61746ELLU: // kzn_Latn_MW
+        case 0xBB2A47414C61746ELLU: // kzo_Latn_GA
+        case 0xBF2A49444C61746ELLU: // kzp_Latn_ID
+        case 0xC72A434D4C61746ELLU: // kzr_Latn_CM
+        case 0xCB2A4D594C61746ELLU: // kzs_Latn_MY
+        case 0xD32A49444C61746ELLU: // kzu_Latn_ID
+        case 0xD72A49444C61746ELLU: // kzv_Latn_ID
+        case 0xDB2A42524C61746ELLU: // kzw_Latn_BR
+        case 0xDF2A49444C61746ELLU: // kzx_Latn_ID
+        case 0xE32A43444C61746ELLU: // kzy_Latn_CD
+        case 0xE72A49444C61746ELLU: // kzz_Latn_ID
+        case 0x6C6156414C61746ELLU: // la_Latn_VA
+        case 0x800B50484C61746ELLU: // laa_Latn_PH
+        case 0x840B47524C696E61LLU: // lab_Lina_GR
+        case 0x880B4D584C61746ELLU: // lac_Latn_MX
+        case 0x8C0B494C48656272LLU: // lad_Hebr_IL
+        case 0x900B494E44657661LLU: // lae_Deva_IN
+        case 0x980B545A4C61746ELLU: // lag_Latn_TZ
+        case 0x9C0B504B41726162LLU: // lah_Arab_PK
+        case 0xA00B4D574C61746ELLU: // lai_Latn_MW
+        case 0xA40B55474C61746ELLU: // laj_Latn_UG
+        case 0xAC0B43444C61746ELLU: // lal_Latn_CD
+        case 0xB00B5A4D4C61746ELLU: // lam_Latn_ZM
+        case 0xB40B4E474C61746ELLU: // lan_Latn_NG
+        case 0xBC0B54444C61746ELLU: // lap_Latn_TD
+        case 0xC00B564E4C61746ELLU: // laq_Latn_VN
+        case 0xC40B47484C61746ELLU: // lar_Latn_GH
+        case 0xC80B54474C61746ELLU: // las_Latn_TG
+        case 0xD00B49444C61746ELLU: // lau_Latn_ID
+        case 0xD80B49444C61746ELLU: // law_Latn_ID
+        case 0xDC0B494E4C61746ELLU: // lax_Latn_IN
+        case 0xE40B50474C61746ELLU: // laz_Latn_PG
+        case 0x6C624C554C61746ELLU: // lb_Latn_LU
+        case 0x842B50474C61746ELLU: // lbb_Latn_PG
+        case 0x902B52554379726CLLU: // lbe_Cyrl_RU
+        case 0x942B494E44657661LLU: // lbf_Deva_IN
+        case 0xA02B434D4C61746ELLU: // lbi_Latn_CM
+        case 0xA42B494E54696274LLU: // lbj_Tibt_IN
+        case 0xAC2B50484C61746ELLU: // lbl_Latn_PH
+        case 0xB02B494E44657661LLU: // lbm_Deva_IN
+        case 0xB42B4C414C61746ELLU: // lbn_Latn_LA
+        case 0xB82B4C414C616F6FLLU: // lbo_Laoo_LA
+        case 0xC02B50474C61746ELLU: // lbq_Latn_PG
+        case 0xC42B4E5044657661LLU: // lbr_Deva_NP
+        case 0xCC2B564E4C61746ELLU: // lbt_Latn_VN
+        case 0xD02B50474C61746ELLU: // lbu_Latn_PG
+        case 0xD42B50474C61746ELLU: // lbv_Latn_PG
+        case 0xD82B49444C61746ELLU: // lbw_Latn_ID
+        case 0xDC2B49444C61746ELLU: // lbx_Latn_ID
+        case 0xE02B41554C61746ELLU: // lby_Latn_AU
+        case 0xE42B41554C61746ELLU: // lbz_Latn_AU
+        case 0x884B49444C61746ELLU: // lcc_Latn_ID
+        case 0x8C4B49444C61746ELLU: // lcd_Latn_ID
+        case 0x904B49444C61746ELLU: // lce_Latn_ID
+        case 0x944B49444C61746ELLU: // lcf_Latn_ID
+        case 0x9C4B414F4C61746ELLU: // lch_Latn_AO
+        case 0xAC4B49444C61746ELLU: // lcl_Latn_ID
+        case 0xB04B50474C61746ELLU: // lcm_Latn_PG
+        case 0xBC4B434E54686169LLU: // lcp_Thai_CN
+        case 0xC04B49444C61746ELLU: // lcq_Latn_ID
+        case 0xC84B49444C61746ELLU: // lcs_Latn_ID
+        case 0x806B43494C61746ELLU: // lda_Latn_CI
+        case 0x846B4E474C61746ELLU: // ldb_Latn_NG
+        case 0x8C6B4E474C61746ELLU: // ldd_Latn_NG
+        case 0x986B4E474C61746ELLU: // ldg_Latn_NG
+        case 0x9C6B4E474C61746ELLU: // ldh_Latn_NG
+        case 0xA06B43474C61746ELLU: // ldi_Latn_CG
+        case 0xA46B4E474C61746ELLU: // ldj_Latn_NG
+        case 0xA86B4E474C61746ELLU: // ldk_Latn_NG
+        case 0xAC6B4E474C61746ELLU: // ldl_Latn_NG
+        case 0xB06B474E4C61746ELLU: // ldm_Latn_GN
+        case 0xB86B4E474C61746ELLU: // ldo_Latn_NG
+        case 0xBC6B4E474C61746ELLU: // ldp_Latn_NG
+        case 0xC06B4E474C61746ELLU: // ldq_Latn_NG
+        case 0x808B43444C61746ELLU: // lea_Latn_CD
+        case 0x848B5A4D4C61746ELLU: // leb_Latn_ZM
+        case 0x888B424F4C61746ELLU: // lec_Latn_BO
+        case 0x8C8B43444C61746ELLU: // led_Latn_CD
+        case 0x908B42464C61746ELLU: // lee_Latn_BF
+        case 0x948B47484C61746ELLU: // lef_Latn_GH
+        case 0x9C8B5A4D4C61746ELLU: // leh_Latn_ZM
+        case 0xA08B50474C61746ELLU: // lei_Latn_PG
+        case 0xA48B43444C61746ELLU: // lej_Latn_CD
+        case 0xA88B50474C61746ELLU: // lek_Latn_PG
+        case 0xAC8B43444C61746ELLU: // lel_Latn_CD
+        case 0xB08B434D4C61746ELLU: // lem_Latn_CM
+        case 0xB48B484E4C61746ELLU: // len_Latn_HN
+        case 0xB88B434D4C61746ELLU: // leo_Latn_CM
+        case 0xBC8B494E4C657063LLU: // lep_Lepc_IN
+        case 0xC08B50474C61746ELLU: // leq_Latn_PG
+        case 0xC48B50474C61746ELLU: // ler_Latn_PG
+        case 0xC88B43444C61746ELLU: // les_Latn_CD
+        case 0xCC8B50474C61746ELLU: // let_Latn_PG
+        case 0xD08B50474C61746ELLU: // leu_Latn_PG
+        case 0xD48B49444C61746ELLU: // lev_Latn_ID
+        case 0xD88B49444C61746ELLU: // lew_Latn_ID
+        case 0xDC8B49444C61746ELLU: // lex_Latn_ID
+        case 0xE08B49444C61746ELLU: // ley_Latn_ID
+        case 0xE48B52554379726CLLU: // lez_Cyrl_RU
+        case 0x80AB434D4C61746ELLU: // lfa_Latn_CM
+        case 0x6C6755474C61746ELLU: // lg_Latn_UG
+        case 0x80CB53424C61746ELLU: // lga_Latn_SB
+        case 0x84CB53424C61746ELLU: // lgb_Latn_SB
+        case 0x98CB55474C61746ELLU: // lgg_Latn_UG
+        case 0x9CCB564E4C61746ELLU: // lgh_Latn_VN
+        case 0xA0CB49444C61746ELLU: // lgi_Latn_ID
+        case 0xA8CB56554C61746ELLU: // lgk_Latn_VU
+        case 0xACCB53424C61746ELLU: // lgl_Latn_SB
+        case 0xB0CB43444C61746ELLU: // lgm_Latn_CD
+        case 0xB4CB45544C61746ELLU: // lgn_Latn_ET
+        case 0xB8CB53534C61746ELLU: // lgo_Latn_SS
+        case 0xC0CB47484C61746ELLU: // lgq_Latn_GH
+        case 0xC4CB53424C61746ELLU: // lgr_Latn_SB
+        case 0xCCCB50474C61746ELLU: // lgt_Latn_PG
+        case 0xD0CB53424C61746ELLU: // lgu_Latn_SB
+        case 0xE4CB43444C61746ELLU: // lgz_Latn_CD
+        case 0x80EB564E4C61746ELLU: // lha_Latn_VN
+        case 0x9CEB49444C61746ELLU: // lhh_Latn_ID
+        case 0xA0EB434E4C61746ELLU: // lhi_Latn_CN
+        case 0xB0EB4E5044657661LLU: // lhm_Deva_NP
+        case 0xB4EB4D594C61746ELLU: // lhn_Latn_MY
+        case 0xC8EB535953797263LLU: // lhs_Syrc_SY
+        case 0xCCEB56554C61746ELLU: // lht_Latn_VU
+        case 0xD0EB434E4C61746ELLU: // lhu_Latn_CN
+        case 0x6C694E4C4C61746ELLU: // li_Latn_NL
+        case 0x810B534C4C61746ELLU: // lia_Latn_SL
+        case 0x850B50474C61746ELLU: // lib_Latn_PG
+        case 0x890B434E4C61746ELLU: // lic_Latn_CN
+        case 0x8D0B50474C61746ELLU: // lid_Latn_PG
+        case 0x910B43444C61746ELLU: // lie_Latn_CD
+        case 0x950B4E5044657661LLU: // lif_Deva_NP
+        case 0x950B494E4C696D62LLU: // lif_Limb_IN
+        case 0x990B47484C61746ELLU: // lig_Latn_GH
+        case 0x9D0B50474C61746ELLU: // lih_Latn_PG
+        case 0xA50B49544C61746ELLU: // lij_Latn_IT
+        case 0xA90B43444C61746ELLU: // lik_Latn_CD
+        case 0xAD0B43414C61746ELLU: // lil_Latn_CA
+        case 0xB90B49444C61746ELLU: // lio_Latn_ID
+        case 0xBD0B47484C61746ELLU: // lip_Latn_GH
+        case 0xC10B45544C61746ELLU: // liq_Latn_ET
+        case 0xC50B4C524C61746ELLU: // lir_Latn_LR
+        case 0xC90B434E4C697375LLU: // lis_Lisu_CN
+        case 0xD10B53444C61746ELLU: // liu_Latn_SD
+        case 0xD50B4C564C61746ELLU: // liv_Latn_LV
+        case 0xD90B49444C61746ELLU: // liw_Latn_ID
+        case 0xDD0B49444C61746ELLU: // lix_Latn_ID
+        case 0xE10B43464C61746ELLU: // liy_Latn_CF
+        case 0xE50B43444C61746ELLU: // liz_Latn_CD
+        case 0x812B41554C61746ELLU: // lja_Latn_AU
+        case 0x912B49444C61746ELLU: // lje_Latn_ID
+        case 0xA12B49444C61746ELLU: // lji_Latn_ID
+        case 0xAD2B49444C61746ELLU: // ljl_Latn_ID
+        case 0xBD2B49444C61746ELLU: // ljp_Latn_ID
+        case 0xD92B41554C61746ELLU: // ljw_Latn_AU
+        case 0xDD2B41554C61746ELLU: // ljx_Latn_AU
+        case 0x814B544C4C61746ELLU: // lka_Latn_TL
+        case 0x854B4B454C61746ELLU: // lkb_Latn_KE
+        case 0x894B564E4C61746ELLU: // lkc_Latn_VN
+        case 0x8D4B42524C61746ELLU: // lkd_Latn_BR
+        case 0x914B55474C61746ELLU: // lke_Latn_UG
+        case 0x9D4B425454696274LLU: // lkh_Tibt_BT
+        case 0xA14B495241726162LLU: // lki_Arab_IR
+        case 0xA54B4D594C61746ELLU: // lkj_Latn_MY
+        case 0xAD4B50474C61746ELLU: // lkl_Latn_PG
+        case 0xB14B41554C61746ELLU: // lkm_Latn_AU
+        case 0xB54B56554C61746ELLU: // lkn_Latn_VU
+        case 0xB94B4B454C61746ELLU: // lko_Latn_KE
+        case 0xC54B53534C61746ELLU: // lkr_Latn_SS
+        case 0xC94B4B454C61746ELLU: // lks_Latn_KE
+        case 0xCD4B55534C61746ELLU: // lkt_Latn_US
+        case 0xD14B41554C61746ELLU: // lku_Latn_AU
+        case 0xE14B53534C61746ELLU: // lky_Latn_SS
+        case 0x816B4E474C61746ELLU: // lla_Latn_NG
+        case 0x856B4D5A4C61746ELLU: // llb_Latn_MZ
+        case 0x896B474E4C61746ELLU: // llc_Latn_GN
+        case 0x8D6B49544C61746ELLU: // lld_Latn_IT
+        case 0x916B50474C61746ELLU: // lle_Latn_PG
+        case 0x956B50474C61746ELLU: // llf_Latn_PG
+        case 0x996B49444C61746ELLU: // llg_Latn_ID
+        case 0xA16B43474C61746ELLU: // lli_Latn_CG
+        case 0xA56B41554C61746ELLU: // llj_Latn_AU
+        case 0xA96B4D594C61746ELLU: // llk_Latn_MY
+        case 0xAD6B50474C61746ELLU: // lll_Latn_PG
+        case 0xB16B49444C61746ELLU: // llm_Latn_ID
+        case 0xB56B54444C61746ELLU: // lln_Latn_TD
+        case 0xBD6B56554C61746ELLU: // llp_Latn_VU
+        case 0xC16B49444C61746ELLU: // llq_Latn_ID
+        case 0xD16B53424C61746ELLU: // llu_Latn_SB
+        case 0xDD6B464A4C61746ELLU: // llx_Latn_FJ
+        case 0x818B474E4C61746ELLU: // lma_Latn_GN
+        case 0x858B56554C61746ELLU: // lmb_Latn_VU
+        case 0x898B41554C61746ELLU: // lmc_Latn_AU
+        case 0x8D8B53444C61746ELLU: // lmd_Latn_SD
+        case 0x918B54444C61746ELLU: // lme_Latn_TD
+        case 0x958B49444C61746ELLU: // lmf_Latn_ID
+        case 0x998B50474C61746ELLU: // lmg_Latn_PG
+        case 0x9D8B4E5044657661LLU: // lmh_Deva_NP
+        case 0xA18B43444C61746ELLU: // lmi_Latn_CD
+        case 0xA58B49444C61746ELLU: // lmj_Latn_ID
+        case 0xA98B494E4C61746ELLU: // lmk_Latn_IN
+        case 0xAD8B56554C61746ELLU: // lml_Latn_VU
+        case 0xB58B494E54656C75LLU: // lmn_Telu_IN
+        case 0xB98B49544C61746ELLU: // lmo_Latn_IT
+        case 0xBD8B434D4C61746ELLU: // lmp_Latn_CM
+        case 0xC18B49444C61746ELLU: // lmq_Latn_ID
+        case 0xC58B49444C61746ELLU: // lmr_Latn_ID
+        case 0xD18B56554C61746ELLU: // lmu_Latn_VU
+        case 0xD58B464A4C61746ELLU: // lmv_Latn_FJ
+        case 0xD98B55534C61746ELLU: // lmw_Latn_US
+        case 0xDD8B434D4C61746ELLU: // lmx_Latn_CM
+        case 0xE18B49444C61746ELLU: // lmy_Latn_ID
+        case 0x6C6E43444C61746ELLU: // ln_Latn_CD
+        case 0x81AB43464C61746ELLU: // lna_Latn_CF
+        case 0x85AB4E414C61746ELLU: // lnb_Latn_NA
+        case 0x8DAB49444C61746ELLU: // lnd_Latn_ID
+        case 0x99AB48554C61746ELLU: // lng_Latn_HU
+        case 0x9DAB4D594C61746ELLU: // lnh_Latn_MY
+        case 0xA1AB50474C61746ELLU: // lni_Latn_PG
+        case 0xA5AB41554C61746ELLU: // lnj_Latn_AU
+        case 0xADAB43464C61746ELLU: // lnl_Latn_CF
+        case 0xB1AB50474C61746ELLU: // lnm_Latn_PG
+        case 0xB5AB56554C61746ELLU: // lnn_Latn_VU
+        case 0xC9AB434D4C61746ELLU: // lns_Latn_CM
+        case 0xD1AB4E474C61746ELLU: // lnu_Latn_NG
+        case 0xD9AB41554C61746ELLU: // lnw_Latn_AU
+        case 0xE5AB43444C61746ELLU: // lnz_Latn_CD
+        case 0x6C6F4C414C616F6FLLU: // lo_Laoo_LA
+        case 0x81CB49444C61746ELLU: // loa_Latn_ID
+        case 0x85CB42464C61746ELLU: // lob_Latn_BF
+        case 0x89CB50484C61746ELLU: // loc_Latn_PH
+        case 0x91CB49444C61746ELLU: // loe_Latn_ID
+        case 0x99CB43444C61746ELLU: // log_Latn_CD
+        case 0x9DCB53534C61746ELLU: // loh_Latn_SS
+        case 0xA1CB43494C61746ELLU: // loi_Latn_CI
+        case 0xA5CB50474C61746ELLU: // loj_Latn_PG
+        case 0xA9CB534C4C61746ELLU: // lok_Latn_SL
+        case 0xADCB43444C61746ELLU: // lol_Latn_CD
+        case 0xB1CB4C524C61746ELLU: // lom_Latn_LR
+        case 0xB5CB4D574C61746ELLU: // lon_Latn_MW
+        case 0xB9CB43444C61746ELLU: // loo_Latn_CD
+        case 0xBDCB4E474C61746ELLU: // lop_Latn_NG
+        case 0xC1CB43444C61746ELLU: // loq_Latn_CD
+        case 0xC5CB43494C61746ELLU: // lor_Latn_CI
+        case 0xC9CB50474C61746ELLU: // los_Latn_PG
+        case 0xCDCB53534C61746ELLU: // lot_Latn_SS
+        case 0xD1CB55534C61746ELLU: // lou_Latn_US
+        case 0xD9CB4D594C61746ELLU: // low_Latn_MY
+        case 0xDDCB49444C61746ELLU: // lox_Latn_ID
+        case 0xE1CB4E5044657661LLU: // loy_Deva_NP
+        case 0xE5CB5A4D4C61746ELLU: // loz_Latn_ZM
+        case 0x81EB56554C61746ELLU: // lpa_Latn_VU
+        case 0x91EB49444C61746ELLU: // lpe_Latn_ID
+        case 0xB5EB4D4D4C61746ELLU: // lpn_Latn_MM
+        case 0xB9EB434E506C7264LLU: // lpo_Plrd_CN
+        case 0xDDEB53534C61746ELLU: // lpx_Latn_SS
+        case 0xC60B53534C61746ELLU: // lqr_Latn_SS
+        case 0x822B4D594C61746ELLU: // lra_Latn_MY
+        case 0x8A2B495241726162LLU: // lrc_Arab_IR
+        case 0x9A2B41554C61746ELLU: // lrg_Latn_AU
+        case 0xA22B4B454C61746ELLU: // lri_Latn_KE
+        case 0xAA2B504B41726162LLU: // lrk_Arab_PK
+        case 0xAE2B495241726162LLU: // lrl_Arab_IR
+        case 0xB22B4B454C61746ELLU: // lrm_Latn_KE
+        case 0xB62B49444C61746ELLU: // lrn_Latn_ID
+        case 0xBA2B53444C61746ELLU: // lro_Latn_SD
+        case 0xCE2B49444C61746ELLU: // lrt_Latn_ID
+        case 0xD62B56554C61746ELLU: // lrv_Latn_VU
+        case 0xE62B56554C61746ELLU: // lrz_Latn_VU
+        case 0x824B495241726162LLU: // lsa_Arab_IR
+        case 0x8E4B494C48656272LLU: // lsd_Hebr_IL
+        case 0x924B43444C61746ELLU: // lse_Latn_CD
+        case 0xA24B4D4D4C61746ELLU: // lsi_Latn_MM
+        case 0xB24B55474C61746ELLU: // lsm_Latn_UG
+        case 0xC64B50474C61746ELLU: // lsr_Latn_PG
+        case 0xCA4B504B41726162LLU: // lss_Arab_PK
+        case 0x6C744C544C61746ELLU: // lt_Latn_LT
+        case 0x8A6B434E48616E74LLU: // ltc_Hant_CN
+        case 0x9A6B4C564C61746ELLU: // ltg_Latn_LV
+        case 0x9E6B55474C61746ELLU: // lth_Latn_UG
+        case 0xA26B49444C61746ELLU: // lti_Latn_ID
+        case 0xB66B42524C61746ELLU: // ltn_Latn_BR
+        case 0xBA6B4B454C61746ELLU: // lto_Latn_KE
+        case 0xCA6B4B454C61746ELLU: // lts_Latn_KE
+        case 0xD26B49444C61746ELLU: // ltu_Latn_ID
+        case 0x6C7543444C61746ELLU: // lu_Latn_CD
+        case 0x828B43444C61746ELLU: // lua_Latn_CD
+        case 0x8A8B55474C61746ELLU: // luc_Latn_UG
+        case 0x8E8B52554C61746ELLU: // lud_Latn_RU
+        case 0x928B5A4D4C61746ELLU: // lue_Latn_ZM
+        case 0x968B50474C61746ELLU: // luf_Latn_PG
+        case 0xA28B55534C61746ELLU: // lui_Latn_US
+        case 0xA68B43444C61746ELLU: // luj_Latn_CD
+        case 0xAA8B425454696274LLU: // luk_Tibt_BT
+        case 0xAE8B53534C61746ELLU: // lul_Latn_SS
+        case 0xB28B414F4C61746ELLU: // lum_Latn_AO
+        case 0xB68B5A4D4C61746ELLU: // lun_Latn_ZM
+        case 0xBA8B4B454C61746ELLU: // luo_Latn_KE
+        case 0xBE8B47414C61746ELLU: // lup_Latn_GA
+        case 0xC28B43554C61746ELLU: // luq_Latn_CU
+        case 0xC68B49444C61746ELLU: // lur_Latn_ID
+        case 0xCA8B494E4C61746ELLU: // lus_Latn_IN
+        case 0xCE8B55534C61746ELLU: // lut_Latn_US
+        case 0xD28B4E5044657661LLU: // luu_Deva_NP
+        case 0xD68B4F4D41726162LLU: // luv_Arab_OM
+        case 0xDA8B434D4C61746ELLU: // luw_Latn_CM
+        case 0xE28B4B454C61746ELLU: // luy_Latn_KE
+        case 0xE68B495241726162LLU: // luz_Arab_IR
+        case 0x6C764C564C61746ELLU: // lv_Latn_LV
+        case 0x82AB544C4C61746ELLU: // lva_Latn_TL
+        case 0xA2AB4C414C61746ELLU: // lvi_Latn_LA
+        case 0xAAAB53424C61746ELLU: // lvk_Latn_SB
+        case 0xAEAB43444C61746ELLU: // lvl_Latn_CD
+        case 0xD2AB49444C61746ELLU: // lvu_Latn_ID
+        case 0x82CB43444C61746ELLU: // lwa_Latn_CD
+        case 0x92CB49444C61746ELLU: // lwe_Latn_ID
+        case 0x9ACB4B454C61746ELLU: // lwg_Latn_KE
+        case 0x9ECB564E4C61746ELLU: // lwh_Latn_VN
+        case 0xAECB544854686169LLU: // lwl_Thai_TH
+        case 0xB2CB434E54686169LLU: // lwm_Thai_CN
+        case 0xBACB53534C61746ELLU: // lwo_Latn_SS
+        case 0xCECB49444C61746ELLU: // lwt_Latn_ID
+        case 0xDACB56554C61746ELLU: // lww_Latn_VU
+        case 0xB2EB50474C61746ELLU: // lxm_Latn_PG
+        case 0x830B425454696274LLU: // lya_Tibt_BT
+        case 0xB70B5A4D4C61746ELLU: // lyn_Latn_ZM
+        case 0x9F2B434E48616E73LLU: // lzh_Hans_CN
+        case 0xAF2B56554C61746ELLU: // lzl_Latn_VU
+        case 0xB72B4D4D4C61746ELLU: // lzn_Latn_MM
+        case 0xE72B54524C61746ELLU: // lzz_Latn_TR
+        case 0x800C4D584C61746ELLU: // maa_Latn_MX
+        case 0x840C4D584C61746ELLU: // mab_Latn_MX
+        case 0x8C0C49444C61746ELLU: // mad_Latn_ID
+        case 0x900C4E474C61746ELLU: // mae_Latn_NG
+        case 0x940C434D4C61746ELLU: // maf_Latn_CM
+        case 0x980C494E44657661LLU: // mag_Deva_IN
+        case 0xA00C494E44657661LLU: // mai_Deva_IN
+        case 0xA40C4D584C61746ELLU: // maj_Latn_MX
+        case 0xA80C49444C61746ELLU: // mak_Latn_ID
+        case 0xB00C47544C61746ELLU: // mam_Latn_GT
+        case 0xB40C474D4C61746ELLU: // man_Latn_GM
+        case 0xB40C474E4E6B6F6FLLU: // man_Nkoo_GN
+        case 0xC00C4D584C61746ELLU: // maq_Latn_MX
+        case 0xC80C4B454C61746ELLU: // mas_Latn_KE
+        case 0xCC0C4D584C61746ELLU: // mat_Latn_MX
+        case 0xD00C4D584C61746ELLU: // mau_Latn_MX
+        case 0xD40C42524C61746ELLU: // mav_Latn_BR
+        case 0xD80C47484C61746ELLU: // maw_Latn_GH
+        case 0xDC0C49444C61746ELLU: // max_Latn_ID
+        case 0xE40C4D584C61746ELLU: // maz_Latn_MX
+        case 0x802C50484C61746ELLU: // mba_Latn_PH
+        case 0x842C50484C61746ELLU: // mbb_Latn_PH
+        case 0x882C42524C61746ELLU: // mbc_Latn_BR
+        case 0x8C2C50484C61746ELLU: // mbd_Latn_PH
+        case 0x942C53474C61746ELLU: // mbf_Latn_SG
+        case 0x9C2C50474C61746ELLU: // mbh_Latn_PG
+        case 0xA02C50484C61746ELLU: // mbi_Latn_PH
+        case 0xA42C42524C61746ELLU: // mbj_Latn_BR
+        case 0xA82C50474C61746ELLU: // mbk_Latn_PG
+        case 0xAC2C42524C61746ELLU: // mbl_Latn_BR
+        case 0xB02C43474C61746ELLU: // mbm_Latn_CG
+        case 0xB42C434F4C61746ELLU: // mbn_Latn_CO
+        case 0xB82C434D4C61746ELLU: // mbo_Latn_CM
+        case 0xBC2C434F4C61746ELLU: // mbp_Latn_CO
+        case 0xC02C50474C61746ELLU: // mbq_Latn_PG
+        case 0xC42C434F4C61746ELLU: // mbr_Latn_CO
+        case 0xC82C50484C61746ELLU: // mbs_Latn_PH
+        case 0xCC2C50484C61746ELLU: // mbt_Latn_PH
+        case 0xD02C4E474C61746ELLU: // mbu_Latn_NG
+        case 0xD42C474E4C61746ELLU: // mbv_Latn_GN
+        case 0xD82C50474C61746ELLU: // mbw_Latn_PG
+        case 0xDC2C50474C61746ELLU: // mbx_Latn_PG
+        case 0xE02C504B41726162LLU: // mby_Arab_PK
+        case 0xE42C4D584C61746ELLU: // mbz_Latn_MX
+        case 0x804C50594C61746ELLU: // mca_Latn_PY
+        case 0x844C50454C61746ELLU: // mcb_Latn_PE
+        case 0x884C50474C61746ELLU: // mcc_Latn_PG
+        case 0x8C4C50454C61746ELLU: // mcd_Latn_PE
+        case 0x904C4D584C61746ELLU: // mce_Latn_MX
+        case 0x944C50454C61746ELLU: // mcf_Latn_PE
+        case 0x984C56454C61746ELLU: // mcg_Latn_VE
+        case 0x9C4C56454C61746ELLU: // mch_Latn_VE
+        case 0xA04C50474C61746ELLU: // mci_Latn_PG
+        case 0xA44C4E474C61746ELLU: // mcj_Latn_NG
+        case 0xA84C414F4C61746ELLU: // mck_Latn_AO
+        case 0xAC4C434F4C61746ELLU: // mcl_Latn_CO
+        case 0xB04C4D594C61746ELLU: // mcm_Latn_MY
+        case 0xB44C54444C61746ELLU: // mcn_Latn_TD
+        case 0xB84C4D584C61746ELLU: // mco_Latn_MX
+        case 0xBC4C434D4C61746ELLU: // mcp_Latn_CM
+        case 0xC04C50474C61746ELLU: // mcq_Latn_PG
+        case 0xC44C50474C61746ELLU: // mcr_Latn_PG
+        case 0xC84C434D4C61746ELLU: // mcs_Latn_CM
+        case 0xCC4C434D4C61746ELLU: // mct_Latn_CM
+        case 0xD04C434D4C61746ELLU: // mcu_Latn_CM
+        case 0xD44C50474C61746ELLU: // mcv_Latn_PG
+        case 0xD84C54444C61746ELLU: // mcw_Latn_TD
+        case 0xDC4C43464C61746ELLU: // mcx_Latn_CF
+        case 0xE04C50474C61746ELLU: // mcy_Latn_PG
+        case 0xE44C50474C61746ELLU: // mcz_Latn_PG
+        case 0x806C4E474C61746ELLU: // mda_Latn_NG
+        case 0x846C50474C61746ELLU: // mdb_Latn_PG
+        case 0x886C50474C61746ELLU: // mdc_Latn_PG
+        case 0x8C6C434D4C61746ELLU: // mdd_Latn_CM
+        case 0x906C544441726162LLU: // mde_Arab_TD
+        case 0x946C52554379726CLLU: // mdf_Cyrl_RU
+        case 0x986C54444C61746ELLU: // mdg_Latn_TD
+        case 0x9C6C50484C61746ELLU: // mdh_Latn_PH
+        case 0xA06C43444C61746ELLU: // mdi_Latn_CD
+        case 0xA46C43444C61746ELLU: // mdj_Latn_CD
+        case 0xA86C43444C61746ELLU: // mdk_Latn_CD
+        case 0xB06C43444C61746ELLU: // mdm_Latn_CD
+        case 0xB46C43464C61746ELLU: // mdn_Latn_CF
+        case 0xBC6C43444C61746ELLU: // mdp_Latn_CD
+        case 0xC06C43444C61746ELLU: // mdq_Latn_CD
+        case 0xC46C49444C61746ELLU: // mdr_Latn_ID
+        case 0xC86C50474C61746ELLU: // mds_Latn_PG
+        case 0xCC6C43474C61746ELLU: // mdt_Latn_CG
+        case 0xD06C43474C61746ELLU: // mdu_Latn_CG
+        case 0xD46C4D584C61746ELLU: // mdv_Latn_MX
+        case 0xD86C43474C61746ELLU: // mdw_Latn_CG
+        case 0xDC6C455445746869LLU: // mdx_Ethi_ET
+        case 0xE06C455445746869LLU: // mdy_Ethi_ET
+        case 0xE46C42524C61746ELLU: // mdz_Latn_BR
+        case 0x808C434D4C61746ELLU: // mea_Latn_CM
+        case 0x848C50474C61746ELLU: // meb_Latn_PG
+        case 0x888C41554C61746ELLU: // mec_Latn_AU
+        case 0x8C8C50474C61746ELLU: // med_Latn_PG
+        case 0x908C50474C61746ELLU: // mee_Latn_PG
+        case 0x9C8C4D584C61746ELLU: // meh_Latn_MX
+        case 0xA48C49444C61746ELLU: // mej_Latn_ID
+        case 0xA88C50474C61746ELLU: // mek_Latn_PG
+        case 0xAC8C4D594C61746ELLU: // mel_Latn_MY
+        case 0xB08C41554C61746ELLU: // mem_Latn_AU
+        case 0xB48C534C4C61746ELLU: // men_Latn_SL
+        case 0xB88C4D594C61746ELLU: // meo_Latn_MY
+        case 0xBC8C41554C61746ELLU: // mep_Latn_AU
+        case 0xC08C434D4C61746ELLU: // meq_Latn_CM
+        case 0xC48C4B454C61746ELLU: // mer_Latn_KE
+        case 0xC88C54444C61746ELLU: // mes_Latn_TD
+        case 0xCC8C50474C61746ELLU: // met_Latn_PG
+        case 0xD08C50474C61746ELLU: // meu_Latn_PG
+        case 0xD48C4C524C61746ELLU: // mev_Latn_LR
+        case 0xD88C4E474C61746ELLU: // mew_Latn_NG
+        case 0xE08C534E4C61746ELLU: // mey_Latn_SN
+        case 0xE48C55534C61746ELLU: // mez_Latn_US
+        case 0x80AC544841726162LLU: // mfa_Arab_TH
+        case 0x84AC49444C61746ELLU: // mfb_Latn_ID
+        case 0x88AC43444C61746ELLU: // mfc_Latn_CD
+        case 0x8CAC434D4C61746ELLU: // mfd_Latn_CM
+        case 0x90AC4D554C61746ELLU: // mfe_Latn_MU
+        case 0x94AC434D4C61746ELLU: // mff_Latn_CM
+        case 0x98AC474E4C61746ELLU: // mfg_Latn_GN
+        case 0x9CAC434D4C61746ELLU: // mfh_Latn_CM
+        case 0xA0AC434D41726162LLU: // mfi_Arab_CM
+        case 0xA4AC434D4C61746ELLU: // mfj_Latn_CM
+        case 0xA8AC434D4C61746ELLU: // mfk_Latn_CM
+        case 0xACAC4E474C61746ELLU: // mfl_Latn_NG
+        case 0xB0AC4E474C61746ELLU: // mfm_Latn_NG
+        case 0xB4AC4E474C61746ELLU: // mfn_Latn_NG
+        case 0xB8AC4E474C61746ELLU: // mfo_Latn_NG
+        case 0xBCAC49444C61746ELLU: // mfp_Latn_ID
+        case 0xC0AC54474C61746ELLU: // mfq_Latn_TG
+        case 0xC4AC41554C61746ELLU: // mfr_Latn_AU
+        case 0xCCAC50474C61746ELLU: // mft_Latn_PG
+        case 0xD0AC414F4C61746ELLU: // mfu_Latn_AO
+        case 0xD4AC534E4C61746ELLU: // mfv_Latn_SN
+        case 0xD8AC50474C61746ELLU: // mfw_Latn_PG
+        case 0xDCAC45544C61746ELLU: // mfx_Latn_ET
+        case 0xE0AC4D584C61746ELLU: // mfy_Latn_MX
+        case 0xE4AC53534C61746ELLU: // mfz_Latn_SS
+        case 0x6D674D474C61746ELLU: // mg_Latn_MG
+        case 0x80CC49454C617467LLU: // mga_Latg_IE
+        case 0x84CC54444C61746ELLU: // mgb_Latn_TD
+        case 0x88CC53534C61746ELLU: // mgc_Latn_SS
+        case 0x8CCC53534C61746ELLU: // mgd_Latn_SS
+        case 0x90CC54444C61746ELLU: // mge_Latn_TD
+        case 0x94CC49444C61746ELLU: // mgf_Latn_ID
+        case 0x98CC434D4C61746ELLU: // mgg_Latn_CM
+        case 0x9CCC4D5A4C61746ELLU: // mgh_Latn_MZ
+        case 0xA0CC4E474C61746ELLU: // mgi_Latn_NG
+        case 0xA4CC4E474C61746ELLU: // mgj_Latn_NG
+        case 0xA8CC49444C61746ELLU: // mgk_Latn_ID
+        case 0xACCC50474C61746ELLU: // mgl_Latn_PG
+        case 0xB0CC544C4C61746ELLU: // mgm_Latn_TL
+        case 0xB4CC43464C61746ELLU: // mgn_Latn_CF
+        case 0xB8CC434D4C61746ELLU: // mgo_Latn_CM
+        case 0xBCCC4E5044657661LLU: // mgp_Deva_NP
+        case 0xC0CC545A4C61746ELLU: // mgq_Latn_TZ
+        case 0xC4CC5A4D4C61746ELLU: // mgr_Latn_ZM
+        case 0xC8CC545A4C61746ELLU: // mgs_Latn_TZ
+        case 0xCCCC50474C61746ELLU: // mgt_Latn_PG
+        case 0xD0CC50474C61746ELLU: // mgu_Latn_PG
+        case 0xD4CC545A4C61746ELLU: // mgv_Latn_TZ
+        case 0xD8CC545A4C61746ELLU: // mgw_Latn_TZ
+        case 0xE0CC545A4C61746ELLU: // mgy_Latn_TZ
+        case 0xE4CC545A4C61746ELLU: // mgz_Latn_TZ
+        case 0x6D684D484C61746ELLU: // mh_Latn_MH
+        case 0x84EC47414C61746ELLU: // mhb_Latn_GA
+        case 0x88EC4D584C61746ELLU: // mhc_Latn_MX
+        case 0x8CEC545A4C61746ELLU: // mhd_Latn_TZ
+        case 0x90EC4D594C61746ELLU: // mhe_Latn_MY
+        case 0x94EC50474C61746ELLU: // mhf_Latn_PG
+        case 0x98EC41554C61746ELLU: // mhg_Latn_AU
+        case 0xA0EC55474C61746ELLU: // mhi_Latn_UG
+        case 0xA4EC414641726162LLU: // mhj_Arab_AF
+        case 0xA8EC434D4C61746ELLU: // mhk_Latn_CM
+        case 0xACEC50474C61746ELLU: // mhl_Latn_PG
+        case 0xB0EC4D5A4C61746ELLU: // mhm_Latn_MZ
+        case 0xB4EC49544C61746ELLU: // mhn_Latn_IT
+        case 0xB8EC5A4D4C61746ELLU: // mho_Latn_ZM
+        case 0xBCEC49444C61746ELLU: // mhp_Latn_ID
+        case 0xC0EC55534C61746ELLU: // mhq_Latn_US
+        case 0xC8EC49444C61746ELLU: // mhs_Latn_ID
+        case 0xCCEC56454C61746ELLU: // mht_Latn_VE
+        case 0xD0EC494E4C61746ELLU: // mhu_Latn_IN
+        case 0xD8EC42574C61746ELLU: // mhw_Latn_BW
+        case 0xDCEC4D4D4C61746ELLU: // mhx_Latn_MM
+        case 0xE0EC49444C61746ELLU: // mhy_Latn_ID
+        case 0xE4EC49444C61746ELLU: // mhz_Latn_ID
+        case 0x6D694E5A4C61746ELLU: // mi_Latn_NZ
+        case 0x810C55534C61746ELLU: // mia_Latn_US
+        case 0x850C4D584C61746ELLU: // mib_Latn_MX
+        case 0x890C43414C61746ELLU: // mic_Latn_CA
+        case 0x8D0C49514D616E64LLU: // mid_Mand_IQ
+        case 0x910C4D584C61746ELLU: // mie_Latn_MX
+        case 0x950C434D4C61746ELLU: // mif_Latn_CM
+        case 0x990C4D584C61746ELLU: // mig_Latn_MX
+        case 0x9D0C4D584C61746ELLU: // mih_Latn_MX
+        case 0xA10C4D584C61746ELLU: // mii_Latn_MX
+        case 0xA50C434D4C61746ELLU: // mij_Latn_CM
+        case 0xA90C55534C61746ELLU: // mik_Latn_US
+        case 0xAD0C4D584C61746ELLU: // mil_Latn_MX
+        case 0xB10C4D584C61746ELLU: // mim_Latn_MX
+        case 0xB50C49444C61746ELLU: // min_Latn_ID
+        case 0xB90C4D584C61746ELLU: // mio_Latn_MX
+        case 0xBD0C4D584C61746ELLU: // mip_Latn_MX
+        case 0xC10C4E494C61746ELLU: // miq_Latn_NI
+        case 0xC50C4D584C61746ELLU: // mir_Latn_MX
+        case 0xCD0C4D584C61746ELLU: // mit_Latn_MX
+        case 0xD10C4D584C61746ELLU: // miu_Latn_MX
+        case 0xD90C50474C61746ELLU: // miw_Latn_PG
+        case 0xDD0C4D584C61746ELLU: // mix_Latn_MX
+        case 0xE10C4D584C61746ELLU: // miy_Latn_MX
+        case 0xE50C4D584C61746ELLU: // miz_Latn_MX
+        case 0x852C544C4C61746ELLU: // mjb_Latn_TL
+        case 0x892C4D584C61746ELLU: // mjc_Latn_MX
+        case 0x8D2C55534C61746ELLU: // mjd_Latn_US
+        case 0x912C54444C61746ELLU: // mje_Latn_TD
+        case 0x992C434E4C61746ELLU: // mjg_Latn_CN
+        case 0x9D2C545A4C61746ELLU: // mjh_Latn_TZ
+        case 0xA12C434E4C61746ELLU: // mji_Latn_CN
+        case 0xA52C50474C61746ELLU: // mjj_Latn_PG
+        case 0xA92C50474C61746ELLU: // mjk_Latn_PG
+        case 0xAD2C494E44657661LLU: // mjl_Deva_IN
+        case 0xB12C50474C61746ELLU: // mjm_Latn_PG
+        case 0xB52C50474C61746ELLU: // mjn_Latn_PG
+        case 0xC12C494E4D6C796DLLU: // mjq_Mlym_IN
+        case 0xC52C494E4D6C796DLLU: // mjr_Mlym_IN
+        case 0xC92C4E474C61746ELLU: // mjs_Latn_NG
+        case 0xCD2C494E44657661LLU: // mjt_Deva_IN
+        case 0xD12C494E54656C75LLU: // mju_Telu_IN
+        case 0xD52C494E4D6C796DLLU: // mjv_Mlym_IN
+        case 0xD92C494E4C61746ELLU: // mjw_Latn_IN
+        case 0xDD2C42444C61746ELLU: // mjx_Latn_BD
+        case 0xE12C55534C61746ELLU: // mjy_Latn_US
+        case 0xE52C4E5044657661LLU: // mjz_Deva_NP
+        case 0x6D6B4D4B4379726CLLU: // mk_Cyrl_MK
+        case 0x814C43494C61746ELLU: // mka_Latn_CI
+        case 0x854C494E44657661LLU: // mkb_Deva_IN
+        case 0x894C50474C61746ELLU: // mkc_Latn_PG
+        case 0x914C494E44657661LLU: // mke_Deva_IN
+        case 0x954C4E474C61746ELLU: // mkf_Latn_NG
+        case 0xA14C504B41726162LLU: // mki_Arab_PK
+        case 0xA54C464D4C61746ELLU: // mkj_Latn_FM
+        case 0xA94C434D4C61746ELLU: // mkk_Latn_CM
+        case 0xAD4C424A4C61746ELLU: // mkl_Latn_BJ
+        case 0xB14C544854686169LLU: // mkm_Thai_TH
+        case 0xB54C49444C61746ELLU: // mkn_Latn_ID
+        case 0xB94C4E474C61746ELLU: // mko_Latn_NG
+        case 0xBD4C50474C61746ELLU: // mkp_Latn_PG
+        case 0xC54C50474C61746ELLU: // mkr_Latn_PG
+        case 0xC94C4D584C61746ELLU: // mks_Latn_MX
+        case 0xCD4C4E434C61746ELLU: // mkt_Latn_NC
+        case 0xD14C474E4C61746ELLU: // mku_Latn_GN
+        case 0xD54C56554C61746ELLU: // mkv_Latn_VU
+        case 0xD94C43474C61746ELLU: // mkw_Latn_CG
+        case 0xDD4C50484C61746ELLU: // mkx_Latn_PH
+        case 0xE14C49444C61746ELLU: // mky_Latn_ID
+        case 0xE54C544C4C61746ELLU: // mkz_Latn_TL
+        case 0x6D6C494E4D6C796DLLU: // ml_Mlym_IN
+        case 0x816C56554C61746ELLU: // mla_Latn_VU
+        case 0x856C434D4C61746ELLU: // mlb_Latn_CM
+        case 0x896C564E4C61746ELLU: // mlc_Latn_VN
+        case 0x916C50474C61746ELLU: // mle_Latn_PG
+        case 0x956C4C4154686169LLU: // mlf_Thai_LA
+        case 0x9D6C50474C61746ELLU: // mlh_Latn_PG
+        case 0xA16C49444C61746ELLU: // mli_Latn_ID
+        case 0xA56C54444C61746ELLU: // mlj_Latn_TD
+        case 0xA96C4B454C61746ELLU: // mlk_Latn_KE
+        case 0xAD6C56554C61746ELLU: // mll_Latn_VU
+        case 0xB56C53424C61746ELLU: // mln_Latn_SB
+        case 0xB96C534E4C61746ELLU: // mlo_Latn_SN
+        case 0xBD6C50474C61746ELLU: // mlp_Latn_PG
+        case 0xC16C534E4C61746ELLU: // mlq_Latn_SN
+        case 0xC56C434D4C61746ELLU: // mlr_Latn_CM
+        case 0xC96C53444C61746ELLU: // mls_Latn_SD
+        case 0xD16C53424C61746ELLU: // mlu_Latn_SB
+        case 0xD56C56554C61746ELLU: // mlv_Latn_VU
+        case 0xD96C434D4C61746ELLU: // mlw_Latn_CM
+        case 0xDD6C56554C61746ELLU: // mlx_Latn_VU
+        case 0xE56C50484C61746ELLU: // mlz_Latn_PH
+        case 0x818C4E474C61746ELLU: // mma_Latn_NG
+        case 0x858C49444C61746ELLU: // mmb_Latn_ID
+        case 0x898C4D584C61746ELLU: // mmc_Latn_MX
+        case 0x8D8C434E4C61746ELLU: // mmd_Latn_CN
+        case 0x918C56554C61746ELLU: // mme_Latn_VU
+        case 0x958C4E474C61746ELLU: // mmf_Latn_NG
+        case 0x998C56554C61746ELLU: // mmg_Latn_VU
+        case 0x9D8C42524C61746ELLU: // mmh_Latn_BR
+        case 0xA18C50474C61746ELLU: // mmi_Latn_PG
+        case 0xB18C56554C61746ELLU: // mmm_Latn_VU
+        case 0xB58C50484C61746ELLU: // mmn_Latn_PH
+        case 0xB98C50474C61746ELLU: // mmo_Latn_PG
+        case 0xBD8C50474C61746ELLU: // mmp_Latn_PG
+        case 0xC18C50474C61746ELLU: // mmq_Latn_PG
+        case 0xC58C434E4C61746ELLU: // mmr_Latn_CN
+        case 0xCD8C50474C61746ELLU: // mmt_Latn_PG
+        case 0xD18C434D4C61746ELLU: // mmu_Latn_CM
+        case 0xD58C42524C61746ELLU: // mmv_Latn_BR
+        case 0xD98C56554C61746ELLU: // mmw_Latn_VU
+        case 0xDD8C50474C61746ELLU: // mmx_Latn_PG
+        case 0xE18C54444C61746ELLU: // mmy_Latn_TD
+        case 0xE58C43444C61746ELLU: // mmz_Latn_CD
+        case 0x6D6E4D4E4379726CLLU: // mn_Cyrl_MN
+        case 0x6D6E434E4D6F6E67LLU: // mn_Mong_CN
+        case 0x81AC50474C61746ELLU: // mna_Latn_PG
+        case 0x85AC49444C61746ELLU: // mnb_Latn_ID
+        case 0x89AC434E4D6F6E67LLU: // mnc_Mong_CN
+        case 0x8DAC42524C61746ELLU: // mnd_Latn_BR
+        case 0x91AC54444C61746ELLU: // mne_Latn_TD
+        case 0x95AC434D4C61746ELLU: // mnf_Latn_CM
+        case 0x99AC564E4C61746ELLU: // mng_Latn_VN
+        case 0x9DAC43444C61746ELLU: // mnh_Latn_CD
+        case 0xA1AC494E42656E67LLU: // mni_Beng_IN
+        case 0xA5AC414641726162LLU: // mnj_Arab_AF
+        case 0xADAC56554C61746ELLU: // mnl_Latn_VU
+        case 0xB1AC50474C61746ELLU: // mnm_Latn_PG
+        case 0xB5AC564E4C61746ELLU: // mnn_Latn_VN
+        case 0xBDAC434E4C61746ELLU: // mnp_Latn_CN
+        case 0xC1AC4D594C61746ELLU: // mnq_Latn_MY
+        case 0xC5AC55534C61746ELLU: // mnr_Latn_US
+        case 0xC9AC52554379726CLLU: // mns_Cyrl_RU
+        case 0xD1AC49444C61746ELLU: // mnu_Latn_ID
+        case 0xD5AC53424C61746ELLU: // mnv_Latn_SB
+        case 0xD9AC4D4D4D796D72LLU: // mnw_Mymr_MM
+        case 0xDDAC49444C61746ELLU: // mnx_Latn_ID
+        case 0xE1AC4D5A4C61746ELLU: // mny_Latn_MZ
+        case 0xE5AC49444C61746ELLU: // mnz_Latn_ID
+        case 0x6D6F524F4C61746ELLU: // mo_Latn_RO
+        case 0x81CC43494C61746ELLU: // moa_Latn_CI
+        case 0x89CC41524C61746ELLU: // moc_Latn_AR
+        case 0x8DCC55534C61746ELLU: // mod_Latn_US
+        case 0x91CC43414C61746ELLU: // moe_Latn_CA
+        case 0x99CC49444C61746ELLU: // mog_Latn_ID
+        case 0x9DCC43414C61746ELLU: // moh_Latn_CA
+        case 0xA1CC4E474C61746ELLU: // moi_Latn_NG
+        case 0xA5CC43474C61746ELLU: // moj_Latn_CG
+        case 0xA9CC49444C61746ELLU: // mok_Latn_ID
+        case 0xB1CC4E494C61746ELLU: // mom_Latn_NI
+        case 0xB9CC564E4C61746ELLU: // moo_Latn_VN
+        case 0xBDCC425A4C61746ELLU: // mop_Latn_BZ
+        case 0xC1CC49444C61746ELLU: // moq_Latn_ID
+        case 0xC5CC53444C61746ELLU: // mor_Latn_SD
+        case 0xC9CC42464C61746ELLU: // mos_Latn_BF
+        case 0xCDCC434F4C61746ELLU: // mot_Latn_CO
+        case 0xD1CC54444C61746ELLU: // mou_Latn_TD
+        case 0xD5CC55534C61746ELLU: // mov_Latn_US
+        case 0xD9CC43474C61746ELLU: // mow_Latn_CG
+        case 0xDDCC50474C61746ELLU: // mox_Latn_PG
+        case 0xE1CC45544C61746ELLU: // moy_Latn_ET
+        case 0xE5CC54444C61746ELLU: // moz_Latn_TD
+        case 0x81EC545A4C61746ELLU: // mpa_Latn_TZ
+        case 0x85EC41554C61746ELLU: // mpb_Latn_AU
+        case 0x89EC41554C61746ELLU: // mpc_Latn_AU
+        case 0x8DEC42524C61746ELLU: // mpd_Latn_BR
+        case 0x91EC45544C61746ELLU: // mpe_Latn_ET
+        case 0x99EC54444C61746ELLU: // mpg_Latn_TD
+        case 0x9DEC41554C61746ELLU: // mph_Latn_AU
+        case 0xA1EC434D4C61746ELLU: // mpi_Latn_CM
+        case 0xA5EC41554C61746ELLU: // mpj_Latn_AU
+        case 0xA9EC54444C61746ELLU: // mpk_Latn_TD
+        case 0xADEC50474C61746ELLU: // mpl_Latn_PG
+        case 0xB1EC4D584C61746ELLU: // mpm_Latn_MX
+        case 0xB5EC50474C61746ELLU: // mpn_Latn_PG
+        case 0xB9EC50474C61746ELLU: // mpo_Latn_PG
+        case 0xBDEC50474C61746ELLU: // mpp_Latn_PG
+        case 0xC1EC42524C61746ELLU: // mpq_Latn_BR
+        case 0xC5EC53424C61746ELLU: // mpr_Latn_SB
+        case 0xC9EC50474C61746ELLU: // mps_Latn_PG
+        case 0xCDEC50474C61746ELLU: // mpt_Latn_PG
+        case 0xD1EC42524C61746ELLU: // mpu_Latn_BR
+        case 0xD5EC50474C61746ELLU: // mpv_Latn_PG
+        case 0xD9EC42524C61746ELLU: // mpw_Latn_BR
+        case 0xDDEC50474C61746ELLU: // mpx_Latn_PG
+        case 0xE1EC49444C61746ELLU: // mpy_Latn_ID
+        case 0xE5EC544854686169LLU: // mpz_Thai_TH
+        case 0x820C49444C61746ELLU: // mqa_Latn_ID
+        case 0x860C434D4C61746ELLU: // mqb_Latn_CM
+        case 0x8A0C49444C61746ELLU: // mqc_Latn_ID
+        case 0x920C50474C61746ELLU: // mqe_Latn_PG
+        case 0x960C49444C61746ELLU: // mqf_Latn_ID
+        case 0x9A0C49444C61746ELLU: // mqg_Latn_ID
+        case 0x9E0C4D584C61746ELLU: // mqh_Latn_MX
+        case 0xA20C49444C61746ELLU: // mqi_Latn_ID
+        case 0xA60C49444C61746ELLU: // mqj_Latn_ID
+        case 0xAA0C50484C61746ELLU: // mqk_Latn_PH
+        case 0xAE0C424A4C61746ELLU: // mql_Latn_BJ
+        case 0xB20C50464C61746ELLU: // mqm_Latn_PF
+        case 0xB60C49444C61746ELLU: // mqn_Latn_ID
+        case 0xBA0C49444C61746ELLU: // mqo_Latn_ID
+        case 0xBE0C49444C61746ELLU: // mqp_Latn_ID
+        case 0xC20C4D594C61746ELLU: // mqq_Latn_MY
+        case 0xC60C49444C61746ELLU: // mqr_Latn_ID
+        case 0xCA0C49444C61746ELLU: // mqs_Latn_ID
+        case 0xD20C53534C61746ELLU: // mqu_Latn_SS
+        case 0xD60C50474C61746ELLU: // mqv_Latn_PG
+        case 0xDA0C50474C61746ELLU: // mqw_Latn_PG
+        case 0xDE0C49444C61746ELLU: // mqx_Latn_ID
+        case 0xE20C49444C61746ELLU: // mqy_Latn_ID
+        case 0xE60C50474C61746ELLU: // mqz_Latn_PG
+        case 0x6D72494E44657661LLU: // mr_Deva_IN
+        case 0x822C544854686169LLU: // mra_Thai_TH
+        case 0x862C56554C61746ELLU: // mrb_Latn_VU
+        case 0x8A2C55534C61746ELLU: // mrc_Latn_US
+        case 0x8E2C4E5044657661LLU: // mrd_Deva_NP
+        case 0x962C49444C61746ELLU: // mrf_Latn_ID
+        case 0x9A2C494E4C61746ELLU: // mrg_Latn_IN
+        case 0x9E2C494E4C61746ELLU: // mrh_Latn_IN
+        case 0xA62C52554379726CLLU: // mrj_Cyrl_RU
+        case 0xAA2C4E434C61746ELLU: // mrk_Latn_NC
+        case 0xAE2C464D4C61746ELLU: // mrl_Latn_FM
+        case 0xB22C56554C61746ELLU: // mrm_Latn_VU
+        case 0xB62C53424C61746ELLU: // mrn_Latn_SB
+        case 0xBA2C42444D726F6FLLU: // mro_Mroo_BD
+        case 0xBE2C56554C61746ELLU: // mrp_Latn_VU
+        case 0xC22C50464C61746ELLU: // mrq_Latn_PF
+        case 0xC62C494E44657661LLU: // mrr_Deva_IN
+        case 0xCA2C56554C61746ELLU: // mrs_Latn_VU
+        case 0xCE2C4E474C61746ELLU: // mrt_Latn_NG
+        case 0xD22C434D4C61746ELLU: // mru_Latn_CM
+        case 0xD62C50464C61746ELLU: // mrv_Latn_PF
+        case 0xDA2C50484C61746ELLU: // mrw_Latn_PH
+        case 0xDE2C49444C61746ELLU: // mrx_Latn_ID
+        case 0xE22C50484C61746ELLU: // mry_Latn_PH
+        case 0xE62C49444C61746ELLU: // mrz_Latn_ID
+        case 0x6D734D594C61746ELLU: // ms_Latn_MY
+        case 0x864C50484C61746ELLU: // msb_Latn_PH
+        case 0x8A4C474E4C61746ELLU: // msc_Latn_GN
+        case 0x924C54444C61746ELLU: // mse_Latn_TD
+        case 0x964C49444C61746ELLU: // msf_Latn_ID
+        case 0x9A4C49444C61746ELLU: // msg_Latn_ID
+        case 0x9E4C4D474C61746ELLU: // msh_Latn_MG
+        case 0xA24C4D594C61746ELLU: // msi_Latn_MY
+        case 0xA64C43444C61746ELLU: // msj_Latn_CD
+        case 0xAA4C50484C61746ELLU: // msk_Latn_PH
+        case 0xAE4C49444C61746ELLU: // msl_Latn_ID
+        case 0xB24C50484C61746ELLU: // msm_Latn_PH
+        case 0xB64C56554C61746ELLU: // msn_Latn_VU
+        case 0xBA4C49444C61746ELLU: // mso_Latn_ID
+        case 0xBE4C42524C61746ELLU: // msp_Latn_BR
+        case 0xC24C4E434C61746ELLU: // msq_Latn_NC
+        case 0xCA4C49444C61746ELLU: // mss_Latn_ID
+        case 0xD24C50474C61746ELLU: // msu_Latn_PG
+        case 0xD64C434D4C61746ELLU: // msv_Latn_CM
+        case 0xDA4C47574C61746ELLU: // msw_Latn_GW
+        case 0xDE4C50474C61746ELLU: // msx_Latn_PG
+        case 0xE24C50474C61746ELLU: // msy_Latn_PG
+        case 0xE64C50474C61746ELLU: // msz_Latn_PG
+        case 0x6D744D544C61746ELLU: // mt_Latn_MT
+        case 0x826C50484C61746ELLU: // mta_Latn_PH
+        case 0x866C43494C61746ELLU: // mtb_Latn_CI
+        case 0x8A6C50474C61746ELLU: // mtc_Latn_PG
+        case 0x8E6C49444C61746ELLU: // mtd_Latn_ID
+        case 0x926C53424C61746ELLU: // mte_Latn_SB
+        case 0x966C50474C61746ELLU: // mtf_Latn_PG
+        case 0x9A6C49444C61746ELLU: // mtg_Latn_ID
+        case 0x9E6C49444C61746ELLU: // mth_Latn_ID
+        case 0xA26C50474C61746ELLU: // mti_Latn_PG
+        case 0xA66C49444C61746ELLU: // mtj_Latn_ID
+        case 0xAA6C434D4C61746ELLU: // mtk_Latn_CM
+        case 0xAE6C4E474C61746ELLU: // mtl_Latn_NG
+        case 0xB26C52554379726CLLU: // mtm_Cyrl_RU
+        case 0xB66C4E494C61746ELLU: // mtn_Latn_NI
+        case 0xBA6C4D584C61746ELLU: // mto_Latn_MX
+        case 0xBE6C424F4C61746ELLU: // mtp_Latn_BO
+        case 0xC26C564E4C61746ELLU: // mtq_Latn_VN
+        case 0xC66C494E44657661LLU: // mtr_Deva_IN
+        case 0xCA6C50454C61746ELLU: // mts_Latn_PE
+        case 0xCE6C56554C61746ELLU: // mtt_Latn_VU
+        case 0xD26C4D584C61746ELLU: // mtu_Latn_MX
+        case 0xD66C50474C61746ELLU: // mtv_Latn_PG
+        case 0xDA6C50484C61746ELLU: // mtw_Latn_PH
+        case 0xDE6C4D584C61746ELLU: // mtx_Latn_MX
+        case 0xE26C50474C61746ELLU: // mty_Latn_PG
+        case 0x828C434D4C61746ELLU: // mua_Latn_CM
+        case 0x868C54444C61746ELLU: // mub_Latn_TD
+        case 0x8A8C434D4C61746ELLU: // muc_Latn_CM
+        case 0x8E8C52554379726CLLU: // mud_Cyrl_RU
+        case 0x928C45434C61746ELLU: // mue_Latn_EC
+        case 0x9A8C434D4C61746ELLU: // mug_Latn_CM
+        case 0x9E8C53534C61746ELLU: // muh_Latn_SS
+        case 0xA28C49444C61746ELLU: // mui_Latn_ID
+        case 0xA68C54444C61746ELLU: // muj_Latn_TD
+        case 0xAA8C4E5054696274LLU: // muk_Tibt_NP
+        case 0xB28C50474C61746ELLU: // mum_Latn_PG
+        case 0xBA8C434D4C61746ELLU: // muo_Latn_CM
+        case 0xC28C434E4C61746ELLU: // muq_Latn_CN
+        case 0xC68C53534C61746ELLU: // mur_Latn_SS
+        case 0xCA8C55534C61746ELLU: // mus_Latn_US
+        case 0xCE8C494E44657661LLU: // mut_Deva_IN
+        case 0xD28C4B454C61746ELLU: // muu_Latn_KE
+        case 0xD68C494E54616D6CLLU: // muv_Taml_IN
+        case 0xDE8C50474C61746ELLU: // mux_Latn_PG
+        case 0xE28C434D4C61746ELLU: // muy_Latn_CM
+        case 0xE68C455445746869LLU: // muz_Ethi_ET
+        case 0x82AC50474C61746ELLU: // mva_Latn_PG
+        case 0x8EAC49444C61746ELLU: // mvd_Latn_ID
+        case 0x92AC504B41726162LLU: // mve_Arab_PK
+        case 0x96AC434E4D6F6E67LLU: // mvf_Mong_CN
+        case 0x9AAC4D584C61746ELLU: // mvg_Latn_MX
+        case 0x9EAC54444C61746ELLU: // mvh_Latn_TD
+        case 0xAAAC50474C61746ELLU: // mvk_Latn_PG
+        case 0xAEAC41554C61746ELLU: // mvl_Latn_AU
+        case 0xB6AC50474C61746ELLU: // mvn_Latn_PG
+        case 0xBAAC53424C61746ELLU: // mvo_Latn_SB
+        case 0xBEAC49444C61746ELLU: // mvp_Latn_ID
+        case 0xC2AC50474C61746ELLU: // mvq_Latn_PG
+        case 0xC6AC49444C61746ELLU: // mvr_Latn_ID
+        case 0xCAAC49444C61746ELLU: // mvs_Latn_ID
+        case 0xCEAC56554C61746ELLU: // mvt_Latn_VU
+        case 0xD2AC54444C61746ELLU: // mvu_Latn_TD
+        case 0xD6AC4D594C61746ELLU: // mvv_Latn_MY
+        case 0xDAAC545A4C61746ELLU: // mvw_Latn_TZ
+        case 0xDEAC49444C61746ELLU: // mvx_Latn_ID
+        case 0xE2AC504B41726162LLU: // mvy_Arab_PK
+        case 0xE6AC455445746869LLU: // mvz_Ethi_ET
+        case 0x82CC50474C61746ELLU: // mwa_Latn_PG
+        case 0x86CC50474C61746ELLU: // mwb_Latn_PG
+        case 0x8ACC50474C61746ELLU: // mwc_Latn_PG
+        case 0x92CC545A4C61746ELLU: // mwe_Latn_TZ
+        case 0x96CC41554C61746ELLU: // mwf_Latn_AU
+        case 0x9ACC50474C61746ELLU: // mwg_Latn_PG
+        case 0x9ECC50474C61746ELLU: // mwh_Latn_PG
+        case 0xA2CC56554C61746ELLU: // mwi_Latn_VU
+        case 0xAACC4D4C4C61746ELLU: // mwk_Latn_ML
+        case 0xAECC50544C61746ELLU: // mwl_Latn_PT
+        case 0xB2CC54444C61746ELLU: // mwm_Latn_TD
+        case 0xB6CC5A4D4C61746ELLU: // mwn_Latn_ZM
+        case 0xBACC56554C61746ELLU: // mwo_Latn_VU
+        case 0xBECC41554C61746ELLU: // mwp_Latn_AU
+        case 0xC2CC4D4D4C61746ELLU: // mwq_Latn_MM
+        case 0xC6CC494E44657661LLU: // mwr_Deva_IN
+        case 0xCACC4B454C61746ELLU: // mws_Latn_KE
+        case 0xCECC4D4D4D796D72LLU: // mwt_Mymr_MM
+        case 0xD2CC53534C61746ELLU: // mwu_Latn_SS
+        case 0xD6CC49444C61746ELLU: // mwv_Latn_ID
+        case 0xDACC5553486D6E70LLU: // mww_Hmnp_US
+        case 0xE6CC43444C61746ELLU: // mwz_Latn_CD
+        case 0x82EC4D584C61746ELLU: // mxa_Latn_MX
+        case 0x86EC4D584C61746ELLU: // mxb_Latn_MX
+        case 0x8AEC5A574C61746ELLU: // mxc_Latn_ZW
+        case 0x8EEC49444C61746ELLU: // mxd_Latn_ID
+        case 0x92EC56554C61746ELLU: // mxe_Latn_VU
+        case 0x96EC434D4C61746ELLU: // mxf_Latn_CM
+        case 0x9AEC414F4C61746ELLU: // mxg_Latn_AO
+        case 0x9EEC43444C61746ELLU: // mxh_Latn_CD
+        case 0xA2EC45534C61746ELLU: // mxi_Latn_ES
+        case 0xA6EC494E4C61746ELLU: // mxj_Latn_IN
+        case 0xAAEC50474C61746ELLU: // mxk_Latn_PG
+        case 0xAEEC424A4C61746ELLU: // mxl_Latn_BJ
+        case 0xB2EC50474C61746ELLU: // mxm_Latn_PG
+        case 0xB6EC49444C61746ELLU: // mxn_Latn_ID
+        case 0xBAEC5A4D4C61746ELLU: // mxo_Latn_ZM
+        case 0xBEEC4D584C61746ELLU: // mxp_Latn_MX
+        case 0xC2EC4D584C61746ELLU: // mxq_Latn_MX
+        case 0xC6EC4D594C61746ELLU: // mxr_Latn_MY
+        case 0xCAEC4D584C61746ELLU: // mxs_Latn_MX
+        case 0xCEEC4D584C61746ELLU: // mxt_Latn_MX
+        case 0xD2EC434D4C61746ELLU: // mxu_Latn_CM
+        case 0xD6EC4D584C61746ELLU: // mxv_Latn_MX
+        case 0xDAEC50474C61746ELLU: // mxw_Latn_PG
+        case 0xDEEC43494C61746ELLU: // mxx_Latn_CI
+        case 0xE2EC4D584C61746ELLU: // mxy_Latn_MX
+        case 0xE6EC49444C61746ELLU: // mxz_Latn_ID
+        case 0x6D794D4D4D796D72LLU: // my_Mymr_MM
+        case 0x870C54444C61746ELLU: // myb_Latn_TD
+        case 0x8B0C43444C61746ELLU: // myc_Latn_CD
+        case 0x930C47414C61746ELLU: // mye_Latn_GA
+        case 0x970C45544C61746ELLU: // myf_Latn_ET
+        case 0x9B0C434D4C61746ELLU: // myg_Latn_CM
+        case 0x9F0C55534C61746ELLU: // myh_Latn_US
+        case 0xA70C53534C61746ELLU: // myj_Latn_SS
+        case 0xAB0C4D4C4C61746ELLU: // myk_Latn_ML
+        case 0xAF0C49444C61746ELLU: // myl_Latn_ID
+        case 0xB30C455445746869LLU: // mym_Ethi_ET
+        case 0xBF0C42524C61746ELLU: // myp_Latn_BR
+        case 0xC70C50454C61746ELLU: // myr_Latn_PE
+        case 0xD30C42524C61746ELLU: // myu_Latn_BR
+        case 0xD70C52554379726CLLU: // myv_Cyrl_RU
+        case 0xDB0C50474C61746ELLU: // myw_Latn_PG
+        case 0xDF0C55474C61746ELLU: // myx_Latn_UG
+        case 0xE30C434F4C61746ELLU: // myy_Latn_CO
+        case 0xE70C49524D616E64LLU: // myz_Mand_IR
+        case 0x832C4D584C61746ELLU: // mza_Latn_MX
+        case 0x8F2C434D4C61746ELLU: // mzd_Latn_CM
+        case 0x932C50474C61746ELLU: // mze_Latn_PG
+        case 0x9F2C41524C61746ELLU: // mzh_Latn_AR
+        case 0xA32C4D584C61746ELLU: // mzi_Latn_MX
+        case 0xA72C4C524C61746ELLU: // mzj_Latn_LR
+        case 0xAB2C4E474C61746ELLU: // mzk_Latn_NG
+        case 0xAF2C4D584C61746ELLU: // mzl_Latn_MX
+        case 0xB32C4E474C61746ELLU: // mzm_Latn_NG
+        case 0xB72C495241726162LLU: // mzn_Arab_IR
+        case 0xBB2C42524C61746ELLU: // mzo_Latn_BR
+        case 0xBF2C424F4C61746ELLU: // mzp_Latn_BO
+        case 0xC32C49444C61746ELLU: // mzq_Latn_ID
+        case 0xC72C42524C61746ELLU: // mzr_Latn_BR
+        case 0xCF2C4D594C61746ELLU: // mzt_Latn_MY
+        case 0xD32C50474C61746ELLU: // mzu_Latn_PG
+        case 0xD72C43464C61746ELLU: // mzv_Latn_CF
+        case 0xDB2C47484C61746ELLU: // mzw_Latn_GH
+        case 0xDF2C47594C61746ELLU: // mzx_Latn_GY
+        case 0xE72C50474C61746ELLU: // mzz_Latn_PG
+        case 0x6E614E524C61746ELLU: // na_Latn_NR
+        case 0x800D49444C61746ELLU: // naa_Latn_ID
+        case 0x840D42524C61746ELLU: // nab_Latn_BR
+        case 0x880D50474C61746ELLU: // nac_Latn_PG
+        case 0x900D49444C61746ELLU: // nae_Latn_ID
+        case 0x940D50474C61746ELLU: // naf_Latn_PG
+        case 0x980D494E4C61746ELLU: // nag_Latn_IN
+        case 0xA40D474E4C61746ELLU: // naj_Latn_GN
+        case 0xA80D50474C61746ELLU: // nak_Latn_PG
+        case 0xAC0D50474C61746ELLU: // nal_Latn_PG
+        case 0xB00D41554C61746ELLU: // nam_Latn_AU
+        case 0xB40D434E48616E73LLU: // nan_Hans_CN
+        case 0xB80D4E5044657661LLU: // nao_Deva_NP
+        case 0xBC0D49544C61746ELLU: // nap_Latn_IT
+        case 0xC00D4E414C61746ELLU: // naq_Latn_NA
+        case 0xC40D4E474C61746ELLU: // nar_Latn_NG
+        case 0xC80D50474C61746ELLU: // nas_Latn_PG
+        case 0xCC0D4E474C61746ELLU: // nat_Latn_NG
+        case 0xD80D47484C61746ELLU: // naw_Latn_GH
+        case 0xDC0D50474C61746ELLU: // nax_Latn_PG
+        case 0xE00D41554C61746ELLU: // nay_Latn_AU
+        case 0xE40D4D584C61746ELLU: // naz_Latn_MX
+        case 0x6E624E4F4C61746ELLU: // nb_Latn_NO
+        case 0x802D414F4C61746ELLU: // nba_Latn_AO
+        case 0x842D4E474C61746ELLU: // nbb_Latn_NG
+        case 0x882D494E4C61746ELLU: // nbc_Latn_IN
+        case 0x8C2D43444C61746ELLU: // nbd_Latn_CD
+        case 0x902D494E4C61746ELLU: // nbe_Latn_IN
+        case 0x9C2D4E474C61746ELLU: // nbh_Latn_NG
+        case 0xA02D494E4C61746ELLU: // nbi_Latn_IN
+        case 0xA42D41554C61746ELLU: // nbj_Latn_AU
+        case 0xA82D50474C61746ELLU: // nbk_Latn_PG
+        case 0xB02D43464C61746ELLU: // nbm_Latn_CF
+        case 0xB42D49444C61746ELLU: // nbn_Latn_ID
+        case 0xB82D4E474C61746ELLU: // nbo_Latn_NG
+        case 0xBC2D4E474C61746ELLU: // nbp_Latn_NG
+        case 0xC02D49444C61746ELLU: // nbq_Latn_ID
+        case 0xC42D4E474C61746ELLU: // nbr_Latn_NG
+        case 0xCC2D494E4C61746ELLU: // nbt_Latn_IN
+        case 0xD02D494E4C61746ELLU: // nbu_Latn_IN
+        case 0xD42D434D4C61746ELLU: // nbv_Latn_CM
+        case 0xD82D43444C61746ELLU: // nbw_Latn_CD
+        case 0xE02D50474C61746ELLU: // nby_Latn_PG
+        case 0x804D50474C61746ELLU: // nca_Latn_PG
+        case 0x844D494E4C61746ELLU: // ncb_Latn_IN
+        case 0x884D50474C61746ELLU: // ncc_Latn_PG
+        case 0x8C4D4E5044657661LLU: // ncd_Deva_NP
+        case 0x904D50474C61746ELLU: // nce_Latn_PG
+        case 0x944D50474C61746ELLU: // ncf_Latn_PG
+        case 0x984D43414C61746ELLU: // ncg_Latn_CA
+        case 0x9C4D4D584C61746ELLU: // nch_Latn_MX
+        case 0xA04D4D584C61746ELLU: // nci_Latn_MX
+        case 0xA44D4D584C61746ELLU: // ncj_Latn_MX
+        case 0xA84D41554C61746ELLU: // nck_Latn_AU
+        case 0xAC4D4D584C61746ELLU: // ncl_Latn_MX
+        case 0xB04D50474C61746ELLU: // ncm_Latn_PG
+        case 0xB44D50474C61746ELLU: // ncn_Latn_PG
+        case 0xB84D50474C61746ELLU: // nco_Latn_PG
+        case 0xC04D4C414C616F6FLLU: // ncq_Laoo_LA
+        case 0xC44D434D4C61746ELLU: // ncr_Latn_CM
+        case 0xCC4D494E4C61746ELLU: // nct_Latn_IN
+        case 0xD04D47484C61746ELLU: // ncu_Latn_GH
+        case 0xDC4D4D584C61746ELLU: // ncx_Latn_MX
+        case 0xE44D55534C61746ELLU: // ncz_Latn_US
+        case 0x6E645A574C61746ELLU: // nd_Latn_ZW
+        case 0x806D43474C61746ELLU: // nda_Latn_CG
+        case 0x846D434D4C61746ELLU: // ndb_Latn_CM
+        case 0x886D4D5A4C61746ELLU: // ndc_Latn_MZ
+        case 0x8C6D4E474C61746ELLU: // ndd_Latn_NG
+        case 0x946D52554379726CLLU: // ndf_Cyrl_RU
+        case 0x986D545A4C61746ELLU: // ndg_Latn_TZ
+        case 0x9C6D545A4C61746ELLU: // ndh_Latn_TZ
+        case 0xA06D4E474C61746ELLU: // ndi_Latn_NG
+        case 0xA46D545A4C61746ELLU: // ndj_Latn_TZ
+        case 0xA86D43444C61746ELLU: // ndk_Latn_CD
+        case 0xAC6D43444C61746ELLU: // ndl_Latn_CD
+        case 0xB06D54444C61746ELLU: // ndm_Latn_TD
+        case 0xB46D43474C61746ELLU: // ndn_Latn_CG
+        case 0xBC6D55474C61746ELLU: // ndp_Latn_UG
+        case 0xC06D414F4C61746ELLU: // ndq_Latn_AO
+        case 0xC46D4E474C61746ELLU: // ndr_Latn_NG
+        case 0xC86D44454C61746ELLU: // nds_Latn_DE
+        case 0xCC6D43444C61746ELLU: // ndt_Latn_CD
+        case 0xD06D434D4C61746ELLU: // ndu_Latn_CM
+        case 0xD46D534E4C61746ELLU: // ndv_Latn_SN
+        case 0xD86D43444C61746ELLU: // ndw_Latn_CD
+        case 0xDC6D49444C61746ELLU: // ndx_Latn_ID
+        case 0xE06D43464C61746ELLU: // ndy_Latn_CF
+        case 0xE46D53534C61746ELLU: // ndz_Latn_SS
+        case 0x6E654E5044657661LLU: // ne_Deva_NP
+        case 0x808D49444C61746ELLU: // nea_Latn_ID
+        case 0x848D43494C61746ELLU: // neb_Latn_CI
+        case 0x888D49444C61746ELLU: // nec_Latn_ID
+        case 0x8C8D4E474C61746ELLU: // ned_Latn_NG
+        case 0x908D4E434C61746ELLU: // nee_Latn_NC
+        case 0x988D52554379726CLLU: // neg_Cyrl_RU
+        case 0x9C8D425454696274LLU: // neh_Tibt_BT
+        case 0xA08D545258737578LLU: // nei_Xsux_TR
+        case 0xA48D50474C61746ELLU: // nej_Latn_PG
+        case 0xA88D4E434C61746ELLU: // nek_Latn_NC
+        case 0xB08D4E434C61746ELLU: // nem_Latn_NC
+        case 0xB48D4E434C61746ELLU: // nen_Latn_NC
+        case 0xB88D564E4C61746ELLU: // neo_Latn_VN
+        case 0xC08D4D584C61746ELLU: // neq_Latn_MX
+        case 0xC48D49444C61746ELLU: // ner_Latn_ID
+        case 0xCC8D50474C61746ELLU: // net_Latn_PG
+        case 0xD88D4E5044657661LLU: // new_Deva_NP
+        case 0xDC8D50474C61746ELLU: // nex_Latn_PG
+        case 0xE08D43494C61746ELLU: // ney_Latn_CI
+        case 0xE48D55534C61746ELLU: // nez_Latn_US
+        case 0x80AD49444C61746ELLU: // nfa_Latn_ID
+        case 0x8CAD4E474C61746ELLU: // nfd_Latn_NG
+        case 0xACAD53424C61746ELLU: // nfl_Latn_SB
+        case 0xC4AD47484C61746ELLU: // nfr_Latn_GH
+        case 0xD0AD434D4C61746ELLU: // nfu_Latn_CM
+        case 0x6E674E414C61746ELLU: // ng_Latn_NA
+        case 0x80CD43444C61746ELLU: // nga_Latn_CD
+        case 0x84CD43444C61746ELLU: // ngb_Latn_CD
+        case 0x88CD43444C61746ELLU: // ngc_Latn_CD
+        case 0x8CCD43464C61746ELLU: // ngd_Latn_CF
+        case 0x90CD434D4C61746ELLU: // nge_Latn_CM
+        case 0x98CD43464C61746ELLU: // ngg_Latn_CF
+        case 0x9CCD5A414C61746ELLU: // ngh_Latn_ZA
+        case 0xA0CD4E474C61746ELLU: // ngi_Latn_NG
+        case 0xA4CD434D4C61746ELLU: // ngj_Latn_CM
+        case 0xA8CD41554C61746ELLU: // ngk_Latn_AU
+        case 0xACCD4D5A4C61746ELLU: // ngl_Latn_MZ
+        case 0xB0CD464D4C61746ELLU: // ngm_Latn_FM
+        case 0xB4CD434D4C61746ELLU: // ngn_Latn_CM
+        case 0xBCCD545A4C61746ELLU: // ngp_Latn_TZ
+        case 0xC0CD545A4C61746ELLU: // ngq_Latn_TZ
+        case 0xC4CD53424C61746ELLU: // ngr_Latn_SB
+        case 0xC8CD4E474C61746ELLU: // ngs_Latn_NG
+        case 0xCCCD4C414C616F6FLLU: // ngt_Laoo_LA
+        case 0xD0CD4D584C61746ELLU: // ngu_Latn_MX
+        case 0xD4CD434D4C61746ELLU: // ngv_Latn_CM
+        case 0xD8CD4E474C61746ELLU: // ngw_Latn_NG
+        case 0xDCCD4E474C61746ELLU: // ngx_Latn_NG
+        case 0xE0CD434D4C61746ELLU: // ngy_Latn_CM
+        case 0xE4CD43474C61746ELLU: // ngz_Latn_CG
+        case 0x80ED41554C61746ELLU: // nha_Latn_AU
+        case 0x84ED43494C61746ELLU: // nhb_Latn_CI
+        case 0x88ED4D584C61746ELLU: // nhc_Latn_MX
+        case 0x8CED50594C61746ELLU: // nhd_Latn_PY
+        case 0x90ED4D584C61746ELLU: // nhe_Latn_MX
+        case 0x94ED41554C61746ELLU: // nhf_Latn_AU
+        case 0x98ED4D584C61746ELLU: // nhg_Latn_MX
+        case 0xA0ED4D584C61746ELLU: // nhi_Latn_MX
+        case 0xA8ED4D584C61746ELLU: // nhk_Latn_MX
+        case 0xB0ED4D584C61746ELLU: // nhm_Latn_MX
+        case 0xB4ED4D584C61746ELLU: // nhn_Latn_MX
+        case 0xB8ED50474C61746ELLU: // nho_Latn_PG
+        case 0xBCED4D584C61746ELLU: // nhp_Latn_MX
+        case 0xC0ED4D584C61746ELLU: // nhq_Latn_MX
+        case 0xC4ED42574C61746ELLU: // nhr_Latn_BW
+        case 0xCCED4D584C61746ELLU: // nht_Latn_MX
+        case 0xD0ED434D4C61746ELLU: // nhu_Latn_CM
+        case 0xD4ED4D584C61746ELLU: // nhv_Latn_MX
+        case 0xD8ED4D584C61746ELLU: // nhw_Latn_MX
+        case 0xDCED4D584C61746ELLU: // nhx_Latn_MX
+        case 0xE0ED4D584C61746ELLU: // nhy_Latn_MX
+        case 0xE4ED4D584C61746ELLU: // nhz_Latn_MX
+        case 0x810D49444C61746ELLU: // nia_Latn_ID
+        case 0x850D50474C61746ELLU: // nib_Latn_PG
+        case 0x8D0D41554C61746ELLU: // nid_Latn_AU
+        case 0x910D54444C61746ELLU: // nie_Latn_TD
+        case 0x950D50474C61746ELLU: // nif_Latn_PG
+        case 0x990D41554C61746ELLU: // nig_Latn_AU
+        case 0x9D0D545A4C61746ELLU: // nih_Latn_TZ
+        case 0xA10D50474C61746ELLU: // nii_Latn_PG
+        case 0xA50D49444C61746ELLU: // nij_Latn_ID
+        case 0xAD0D49444C61746ELLU: // nil_Latn_ID
+        case 0xB10D545A4C61746ELLU: // nim_Latn_TZ
+        case 0xB50D4E474C61746ELLU: // nin_Latn_NG
+        case 0xB90D52554379726CLLU: // nio_Cyrl_RU
+        case 0xC10D4B454C61746ELLU: // niq_Latn_KE
+        case 0xC50D49444C61746ELLU: // nir_Latn_ID
+        case 0xC90D50474C61746ELLU: // nis_Latn_PG
+        case 0xCD0D494E54656C75LLU: // nit_Telu_IN
+        case 0xD10D4E554C61746ELLU: // niu_Latn_NU
+        case 0xD50D52554379726CLLU: // niv_Cyrl_RU
+        case 0xD90D50474C61746ELLU: // niw_Latn_PG
+        case 0xDD0D43444C61746ELLU: // nix_Latn_CD
+        case 0xE10D43444C61746ELLU: // niy_Latn_CD
+        case 0xE50D50474C61746ELLU: // niz_Latn_PG
+        case 0x812D4E474C61746ELLU: // nja_Latn_NG
+        case 0x852D494E4C61746ELLU: // njb_Latn_IN
+        case 0x8D2D545A4C61746ELLU: // njd_Latn_TZ
+        case 0x9D2D494E4C61746ELLU: // njh_Latn_IN
+        case 0xA12D41554C61746ELLU: // nji_Latn_AU
+        case 0xA52D434D4C61746ELLU: // njj_Latn_CM
+        case 0xAD2D53534C61746ELLU: // njl_Latn_SS
+        case 0xB12D494E4C61746ELLU: // njm_Latn_IN
+        case 0xB52D494E4C61746ELLU: // njn_Latn_IN
+        case 0xB92D494E4C61746ELLU: // njo_Latn_IN
+        case 0xC52D4E474C61746ELLU: // njr_Latn_NG
+        case 0xC92D49444C61746ELLU: // njs_Latn_ID
+        case 0xCD2D53524C61746ELLU: // njt_Latn_SR
+        case 0xD12D41554C61746ELLU: // nju_Latn_AU
+        case 0xDD2D43474C61746ELLU: // njx_Latn_CG
+        case 0xE12D434D4C61746ELLU: // njy_Latn_CM
+        case 0xE52D494E4C61746ELLU: // njz_Latn_IN
+        case 0x814D5A4D4C61746ELLU: // nka_Latn_ZM
+        case 0x854D494E4C61746ELLU: // nkb_Latn_IN
+        case 0x894D434D4C61746ELLU: // nkc_Latn_CM
+        case 0x8D4D494E4C61746ELLU: // nkd_Latn_IN
+        case 0x914D53424C61746ELLU: // nke_Latn_SB
+        case 0x954D494E4C61746ELLU: // nkf_Latn_IN
+        case 0x994D50474C61746ELLU: // nkg_Latn_PG
+        case 0x9D4D494E4C61746ELLU: // nkh_Latn_IN
+        case 0xA14D494E4C61746ELLU: // nki_Latn_IN
+        case 0xA54D49444C61746ELLU: // nkj_Latn_ID
+        case 0xA94D56554C61746ELLU: // nkk_Latn_VU
+        case 0xB14D50474C61746ELLU: // nkm_Latn_PG
+        case 0xB54D414F4C61746ELLU: // nkn_Latn_AO
+        case 0xB94D47484C61746ELLU: // nko_Latn_GH
+        case 0xC14D47484C61746ELLU: // nkq_Latn_GH
+        case 0xC54D464D4C61746ELLU: // nkr_Latn_FM
+        case 0xC94D49444C61746ELLU: // nks_Latn_ID
+        case 0xCD4D545A4C61746ELLU: // nkt_Latn_TZ
+        case 0xD14D43494C61746ELLU: // nku_Latn_CI
+        case 0xD54D4D574C61746ELLU: // nkv_Latn_MW
+        case 0xD94D43444C61746ELLU: // nkw_Latn_CD
+        case 0xDD4D4E474C61746ELLU: // nkx_Latn_NG
+        case 0xE54D4E474C61746ELLU: // nkz_Latn_NG
+        case 0x6E6C4E4C4C61746ELLU: // nl_Latn_NL
+        case 0x816D434D4C61746ELLU: // nla_Latn_CM
+        case 0x896D49444C61746ELLU: // nlc_Latn_ID
+        case 0x916D4B454C61746ELLU: // nle_Latn_KE
+        case 0x996D53424C61746ELLU: // nlg_Latn_SB
+        case 0xA16D414641726162LLU: // nli_Arab_AF
+        case 0xA56D43444C61746ELLU: // nlj_Latn_CD
+        case 0xA96D49444C61746ELLU: // nlk_Latn_ID
+        case 0xB16D504B41726162LLU: // nlm_Arab_PK
+        case 0xB96D43444C61746ELLU: // nlo_Latn_CD
+        case 0xC16D4D4D4C61746ELLU: // nlq_Latn_MM
+        case 0xD16D47484C61746ELLU: // nlu_Latn_GH
+        case 0xD56D4D584C61746ELLU: // nlv_Latn_MX
+        case 0xD96D41554C61746ELLU: // nlw_Latn_AU
+        case 0xDD6D494E44657661LLU: // nlx_Deva_IN
+        case 0xE16D41554C61746ELLU: // nly_Latn_AU
+        case 0xE56D53424C61746ELLU: // nlz_Latn_SB
+        case 0x818D494E4C61746ELLU: // nma_Latn_IN
+        case 0x858D56554C61746ELLU: // nmb_Latn_VU
+        case 0x898D54444C61746ELLU: // nmc_Latn_TD
+        case 0x8D8D47414C61746ELLU: // nmd_Latn_GA
+        case 0x918D494E4C61746ELLU: // nme_Latn_IN
+        case 0x958D494E4C61746ELLU: // nmf_Latn_IN
+        case 0x998D434D4C61746ELLU: // nmg_Latn_CM
+        case 0x9D8D494E4C61746ELLU: // nmh_Latn_IN
+        case 0xA18D4E474C61746ELLU: // nmi_Latn_NG
+        case 0xA58D43464C61746ELLU: // nmj_Latn_CF
+        case 0xA98D56554C61746ELLU: // nmk_Latn_VU
+        case 0xAD8D434D4C61746ELLU: // nml_Latn_CM
+        case 0xB18D4E5044657661LLU: // nmm_Deva_NP
+        case 0xB58D42574C61746ELLU: // nmn_Latn_BW
+        case 0xB98D494E4C61746ELLU: // nmo_Latn_IN
+        case 0xBD8D41554C61746ELLU: // nmp_Latn_AU
+        case 0xC18D5A574C61746ELLU: // nmq_Latn_ZW
+        case 0xC58D434D4C61746ELLU: // nmr_Latn_CM
+        case 0xC98D56554C61746ELLU: // nms_Latn_VU
+        case 0xCD8D464D4C61746ELLU: // nmt_Latn_FM
+        case 0xD18D55534C61746ELLU: // nmu_Latn_US
+        case 0xD58D41554C61746ELLU: // nmv_Latn_AU
+        case 0xD98D50474C61746ELLU: // nmw_Latn_PG
+        case 0xDD8D50474C61746ELLU: // nmx_Latn_PG
+        case 0xE58D54474C61746ELLU: // nmz_Latn_TG
+        case 0x6E6E4E4F4C61746ELLU: // nn_Latn_NO
+        case 0x81AD41554C61746ELLU: // nna_Latn_AU
+        case 0x85AD43444C61746ELLU: // nnb_Latn_CD
+        case 0x89AD54444C61746ELLU: // nnc_Latn_TD
+        case 0x8DAD56554C61746ELLU: // nnd_Latn_VU
+        case 0x91AD414F4C61746ELLU: // nne_Latn_AO
+        case 0x95AD50474C61746ELLU: // nnf_Latn_PG
+        case 0x99AD494E4C61746ELLU: // nng_Latn_IN
+        case 0x9DAD434D4C61746ELLU: // nnh_Latn_CM
+        case 0xA1AD49444C61746ELLU: // nni_Latn_ID
+        case 0xA5AD45544C61746ELLU: // nnj_Latn_ET
+        case 0xA9AD50474C61746ELLU: // nnk_Latn_PG
+        case 0xADAD494E4C61746ELLU: // nnl_Latn_IN
+        case 0xB1AD50474C61746ELLU: // nnm_Latn_PG
+        case 0xB5AD54444C61746ELLU: // nnn_Latn_TD
+        case 0xBDAD494E5763686FLLU: // nnp_Wcho_IN
+        case 0xC1AD545A4C61746ELLU: // nnq_Latn_TZ
+        case 0xC5AD41554C61746ELLU: // nnr_Latn_AU
+        case 0xCDAD55534C61746ELLU: // nnt_Latn_US
+        case 0xD1AD47484C61746ELLU: // nnu_Latn_GH
+        case 0xD5AD41554C61746ELLU: // nnv_Latn_AU
+        case 0xD9AD42464C61746ELLU: // nnw_Latn_BF
+        case 0xE1AD41554C61746ELLU: // nny_Latn_AU
+        case 0xE5AD434D4C61746ELLU: // nnz_Latn_CM
+        case 0x6E6F4E4F4C61746ELLU: // no_Latn_NO
+        case 0x81CD434F4C61746ELLU: // noa_Latn_CO
+        case 0x89CD50474C61746ELLU: // noc_Latn_PG
+        case 0x8DCD54484C616E61LLU: // nod_Lana_TH
+        case 0x91CD494E44657661LLU: // noe_Deva_IN
+        case 0x95CD50474C61746ELLU: // nof_Latn_PG
+        case 0x99CD52554379726CLLU: // nog_Cyrl_RU
+        case 0x9DCD50474C61746ELLU: // noh_Latn_PG
+        case 0xA1CD494E44657661LLU: // noi_Deva_IN
+        case 0xA5CD434F4C61746ELLU: // noj_Latn_CO
+        case 0xA9CD55534C61746ELLU: // nok_Latn_US
+        case 0xB5CD534552756E72LLU: // non_Runr_SE
+        case 0xBDCD50474C61746ELLU: // nop_Latn_PG
+        case 0xC1CD43444C61746ELLU: // noq_Latn_CD
+        case 0xC9CD434E59696969LLU: // nos_Yiii_CN
+        case 0xCDCD50454C61746ELLU: // not_Latn_PE
+        case 0xD1CD50474C61746ELLU: // nou_Latn_PG
+        case 0xD9CD545A4C61746ELLU: // now_Latn_TZ
+        case 0xE1CD54444C61746ELLU: // noy_Latn_TD
+        case 0x85ED425454696274LLU: // npb_Tibt_BT
+        case 0x99ED4D4D4C61746ELLU: // npg_Latn_MM
+        case 0x9DED494E4C61746ELLU: // nph_Latn_IN
+        case 0xADED4D584C61746ELLU: // npl_Latn_MX
+        case 0xB5ED50474C61746ELLU: // npn_Latn_PG
+        case 0xB9ED494E4C61746ELLU: // npo_Latn_IN
+        case 0xC9ED49444C61746ELLU: // nps_Latn_ID
+        case 0xD1ED494E4C61746ELLU: // npu_Latn_IN
+        case 0xDDED53424C61746ELLU: // npx_Latn_SB
+        case 0xE1ED49444C61746ELLU: // npy_Latn_ID
+        case 0x9A0D424A4C61746ELLU: // nqg_Latn_BJ
+        case 0xAA0D424A4C61746ELLU: // nqk_Latn_BJ
+        case 0xAE0D414F4C61746ELLU: // nql_Latn_AO
+        case 0xB20D49444C61746ELLU: // nqm_Latn_ID
+        case 0xB60D50474C61746ELLU: // nqn_Latn_PG
+        case 0xBA0D474E4E6B6F6FLLU: // nqo_Nkoo_GN
+        case 0xC20D4D4D4C61746ELLU: // nqq_Latn_MM
+        case 0xCE0D4E474C61746ELLU: // nqt_Latn_NG
+        case 0xE20D4D4D4C61746ELLU: // nqy_Latn_MM
+        case 0x6E725A414C61746ELLU: // nr_Latn_ZA
+        case 0x822D47414C61746ELLU: // nra_Latn_GA
+        case 0x862D45524C61746ELLU: // nrb_Latn_ER
+        case 0x922D494E4C61746ELLU: // nre_Latn_IN
+        case 0x962D4A454C61746ELLU: // nrf_Latn_JE
+        case 0x9A2D56554C61746ELLU: // nrg_Latn_VU
+        case 0xA22D494E4C61746ELLU: // nri_Latn_IN
+        case 0xAA2D41554C61746ELLU: // nrk_Latn_AU
+        case 0xAE2D41554C61746ELLU: // nrl_Latn_AU
+        case 0xB22D4D594C61746ELLU: // nrm_Latn_MY
+        case 0xB62D474252756E72LLU: // nrn_Runr_GB
+        case 0xBE2D49544C61746ELLU: // nrp_Latn_IT
+        case 0xD22D434E4C61746ELLU: // nru_Latn_CN
+        case 0xDE2D41554C61746ELLU: // nrx_Latn_AU
+        case 0xE62D50474C61746ELLU: // nrz_Latn_PG
+        case 0x824D494E4C61746ELLU: // nsa_Latn_IN
+        case 0x864D5A414C61746ELLU: // nsb_Latn_ZA
+        case 0x8A4D4E474C61746ELLU: // nsc_Latn_NG
+        case 0x8E4D434E59696969LLU: // nsd_Yiii_CN
+        case 0x924D5A4D4C61746ELLU: // nse_Latn_ZM
+        case 0x964D434E59696969LLU: // nsf_Yiii_CN
+        case 0x9A4D545A4C61746ELLU: // nsg_Latn_TZ
+        case 0x9E4D434D4C61746ELLU: // nsh_Latn_CM
+        case 0xAA4D434143616E73LLU: // nsk_Cans_CA
+        case 0xB24D494E4C61746ELLU: // nsm_Latn_IN
+        case 0xB64D50474C61746ELLU: // nsn_Latn_PG
+        case 0xBA4D5A414C61746ELLU: // nso_Latn_ZA
+        case 0xC24D55534C61746ELLU: // nsq_Latn_US
+        case 0xCA4D50474C61746ELLU: // nss_Latn_PG
+        case 0xCE4D494E546E7361LLU: // nst_Tnsa_IN
+        case 0xD24D4D584C61746ELLU: // nsu_Latn_MX
+        case 0xD64D434E59696969LLU: // nsv_Yiii_CN
+        case 0xDA4D56554C61746ELLU: // nsw_Latn_VU
+        case 0xDE4D414F4C61746ELLU: // nsx_Latn_AO
+        case 0xE24D49444C61746ELLU: // nsy_Latn_ID
+        case 0xE64D55534C61746ELLU: // nsz_Latn_US
+        case 0x8E6D4D594C61746ELLU: // ntd_Latn_MY
+        case 0x926D4D5A4C61746ELLU: // nte_Latn_MZ
+        case 0x9A6D41554C61746ELLU: // ntg_Latn_AU
+        case 0xA26D42464C61746ELLU: // nti_Latn_BF
+        case 0xA66D41554C61746ELLU: // ntj_Latn_AU
+        case 0xAA6D545A4C61746ELLU: // ntk_Latn_TZ
+        case 0xB26D424A4C61746ELLU: // ntm_Latn_BJ
+        case 0xBA6D43444C61746ELLU: // nto_Latn_CD
+        case 0xBE6D4D584C61746ELLU: // ntp_Latn_MX
+        case 0xC66D47484C61746ELLU: // ntr_Latn_GH
+        case 0xD26D53424C61746ELLU: // ntu_Latn_SB
+        case 0xDE6D4D4D4C61746ELLU: // ntx_Latn_MM
+        case 0xE26D564E59696969LLU: // nty_Yiii_VN
+        case 0xE66D495241726162LLU: // ntz_Arab_IR
+        case 0x828D4E434C61746ELLU: // nua_Latn_NC
+        case 0x8A8D42524C61746ELLU: // nuc_Latn_BR
+        case 0x8E8D50474C61746ELLU: // nud_Latn_PG
+        case 0x928D43444C61746ELLU: // nue_Latn_CD
+        case 0x968D434E4C61746ELLU: // nuf_Latn_CN
+        case 0x9A8D41554C61746ELLU: // nug_Latn_AU
+        case 0x9E8D4E474C61746ELLU: // nuh_Latn_NG
+        case 0xA28D47514C61746ELLU: // nui_Latn_GQ
+        case 0xA68D55474C61746ELLU: // nuj_Latn_UG
+        case 0xAA8D43414C61746ELLU: // nuk_Latn_CA
+        case 0xB28D544F4C61746ELLU: // num_Latn_TO
+        case 0xB68D4D4D4C61746ELLU: // nun_Latn_MM
+        case 0xBA8D564E4C61746ELLU: // nuo_Latn_VN
+        case 0xBE8D4E474C61746ELLU: // nup_Latn_NG
+        case 0xC28D50474C61746ELLU: // nuq_Latn_PG
+        case 0xC68D50474C61746ELLU: // nur_Latn_PG
+        case 0xCA8D53534C61746ELLU: // nus_Latn_SS
+        case 0xCE8D564E4C61746ELLU: // nut_Latn_VN
+        case 0xD28D43444C61746ELLU: // nuu_Latn_CD
+        case 0xD68D42464C61746ELLU: // nuv_Latn_BF
+        case 0xDA8D464D4C61746ELLU: // nuw_Latn_FM
+        case 0xDE8D50474C61746ELLU: // nux_Latn_PG
+        case 0xE28D41554C61746ELLU: // nuy_Latn_AU
+        case 0xE68D4D584C61746ELLU: // nuz_Latn_MX
+        case 0x6E7655534C61746ELLU: // nv_Latn_US
+        case 0x9EAD56554C61746ELLU: // nvh_Latn_VU
+        case 0xB2AD50474C61746ELLU: // nvm_Latn_PG
+        case 0xBAAD434D4C61746ELLU: // nvo_Latn_CM
+        case 0x86CD43494C61746ELLU: // nwb_Latn_CI
+        case 0x8ACD4E504E657761LLU: // nwc_Newa_NP
+        case 0x92CD434D4C61746ELLU: // nwe_Latn_CM
+        case 0x9ACD41554C61746ELLU: // nwg_Latn_AU
+        case 0xA2CD56554C61746ELLU: // nwi_Latn_VU
+        case 0xB2CD53534C61746ELLU: // nwm_Latn_SS
+        case 0xBACD41554C61746ELLU: // nwo_Latn_AU
+        case 0xC6CD50474C61746ELLU: // nwr_Latn_PG
+        case 0xDACD545A4C61746ELLU: // nww_Latn_TZ
+        case 0xDECD4E5044657661LLU: // nwx_Deva_NP
+        case 0x82ED544C4C61746ELLU: // nxa_Latn_TL
+        case 0x8EED43444C61746ELLU: // nxd_Latn_CD
+        case 0x92ED49444C61746ELLU: // nxe_Latn_ID
+        case 0x9AED49444C61746ELLU: // nxg_Latn_ID
+        case 0xA2ED545A4C61746ELLU: // nxi_Latn_TZ
+        case 0xAEED49444C61746ELLU: // nxl_Latn_ID
+        case 0xB6ED41554C61746ELLU: // nxn_Latn_AU
+        case 0xBAED47414C61746ELLU: // nxo_Latn_GA
+        case 0xC2ED434E4C61746ELLU: // nxq_Latn_CN
+        case 0xC6ED50474C61746ELLU: // nxr_Latn_PG
+        case 0xDEED49444C61746ELLU: // nxx_Latn_ID
+        case 0x6E794D574C61746ELLU: // ny_Latn_MW
+        case 0x870D47484C61746ELLU: // nyb_Latn_GH
+        case 0x8B0D43444C61746ELLU: // nyc_Latn_CD
+        case 0x8F0D4B454C61746ELLU: // nyd_Latn_KE
+        case 0x930D414F4C61746ELLU: // nye_Latn_AO
+        case 0x970D4B454C61746ELLU: // nyf_Latn_KE
+        case 0x9B0D43444C61746ELLU: // nyg_Latn_CD
+        case 0x9F0D41554C61746ELLU: // nyh_Latn_AU
+        case 0xA30D53444C61746ELLU: // nyi_Latn_SD
+        case 0xA70D43444C61746ELLU: // nyj_Latn_CD
+        case 0xAB0D414F4C61746ELLU: // nyk_Latn_AO
+        case 0xAF0D544854686169LLU: // nyl_Thai_TH
+        case 0xB30D545A4C61746ELLU: // nym_Latn_TZ
+        case 0xB70D55474C61746ELLU: // nyn_Latn_UG
+        case 0xBB0D55474C61746ELLU: // nyo_Latn_UG
+        case 0xBF0D55474C61746ELLU: // nyp_Latn_UG
+        case 0xC30D495241726162LLU: // nyq_Arab_IR
+        case 0xC70D4D574C61746ELLU: // nyr_Latn_MW
+        case 0xCB0D41554C61746ELLU: // nys_Latn_AU
+        case 0xCF0D41554C61746ELLU: // nyt_Latn_AU
+        case 0xD30D4D5A4C61746ELLU: // nyu_Latn_MZ
+        case 0xD70D41554C61746ELLU: // nyv_Latn_AU
+        case 0xDB0D544854686169LLU: // nyw_Thai_TH
+        case 0xDF0D41554C61746ELLU: // nyx_Latn_AU
+        case 0xE30D545A4C61746ELLU: // nyy_Latn_TZ
+        case 0x832D434D4C61746ELLU: // nza_Latn_CM
+        case 0x872D47414C61746ELLU: // nzb_Latn_GA
+        case 0x8F2D43444C61746ELLU: // nzd_Latn_CD
+        case 0xA32D47484C61746ELLU: // nzi_Latn_GH
+        case 0xAB2D43464C61746ELLU: // nzk_Latn_CF
+        case 0xB32D494E4C61746ELLU: // nzm_Latn_IN
+        case 0xC72D4E474C61746ELLU: // nzr_Latn_NG
+        case 0xD32D43474C61746ELLU: // nzu_Latn_CG
+        case 0xE32D54444C61746ELLU: // nzy_Latn_TD
+        case 0xE72D4D4C4C61746ELLU: // nzz_Latn_ML
+        case 0x800E52554379726CLLU: // oaa_Cyrl_RU
+        case 0x880E52554379726CLLU: // oac_Cyrl_RU
+        case 0xC40E535953797263LLU: // oar_Syrc_SY
+        case 0xD40E474547656F72LLU: // oav_Geor_GE
+        case 0xA02E55534C61746ELLU: // obi_Latn_US
+        case 0xA82E50484C61746ELLU: // obk_Latn_PH
+        case 0xAC2E434D4C61746ELLU: // obl_Latn_CM
+        case 0xB02E4A4F50686E78LLU: // obm_Phnx_JO
+        case 0xB82E50484C61746ELLU: // obo_Latn_PH
+        case 0xC42E4D4D4D796D72LLU: // obr_Mymr_MM
+        case 0xCC2E46524C61746ELLU: // obt_Latn_FR
+        case 0xD02E4E474C61746ELLU: // obu_Latn_NG
+        case 0x6F6346524C61746ELLU: // oc_Latn_FR
+        case 0x804E50454C61746ELLU: // oca_Latn_PE
+        case 0xB84E47424C61746ELLU: // oco_Latn_GB
+        case 0xD04E4D584C61746ELLU: // ocu_Latn_MX
+        case 0x806E4E474C61746ELLU: // oda_Latn_NG
+        case 0xA86E504B41726162LLU: // odk_Arab_PK
+        case 0xCC6E4E4C4C61746ELLU: // odt_Latn_NL
+        case 0xD06E4E474C61746ELLU: // odu_Latn_NG
+        case 0xC8AE4E4C4C61746ELLU: // ofs_Latn_NL
+        case 0xD0AE4E474C61746ELLU: // ofu_Latn_NG
+        case 0x84CE4E474C61746ELLU: // ogb_Latn_NG
+        case 0x88CE4E474C61746ELLU: // ogc_Latn_NG
+        case 0x98CE4E474C61746ELLU: // ogg_Latn_NG
+        case 0xB8CE4E474C61746ELLU: // ogo_Latn_NG
+        case 0xD0CE4E474C61746ELLU: // ogu_Latn_NG
+        case 0xCCEE545258737578LLU: // oht_Xsux_TR
+        case 0xD0EE48554C61746ELLU: // ohu_Latn_HU
+        case 0x810E49444C61746ELLU: // oia_Latn_ID
+        case 0x910E53534C61746ELLU: // oie_Latn_SS
+        case 0xB50E50474C61746ELLU: // oin_Latn_PG
+        case 0x6F6A434143616E73LLU: // oj_Cans_CA
+        case 0x852E43414C61746ELLU: // ojb_Latn_CA
+        case 0x892E43414C61746ELLU: // ojc_Latn_CA
+        case 0xC92E434143616E73LLU: // ojs_Cans_CA
+        case 0xD52E53424C61746ELLU: // ojv_Latn_SB
+        case 0xD92E43414C61746ELLU: // ojw_Latn_CA
+        case 0x814E43414C61746ELLU: // oka_Latn_CA
+        case 0x854E4E474C61746ELLU: // okb_Latn_NG
+        case 0x894E43444C61746ELLU: // okc_Latn_CD
+        case 0x8D4E4E474C61746ELLU: // okd_Latn_NG
+        case 0x914E4E474C61746ELLU: // oke_Latn_NG
+        case 0x994E41554C61746ELLU: // okg_Latn_AU
+        case 0xA14E4B454C61746ELLU: // oki_Latn_KE
+        case 0xA94E50474C61746ELLU: // okk_Latn_PG
+        case 0xB14E4B5248616E67LLU: // okm_Hang_KR
+        case 0xB94E4B5248616E69LLU: // oko_Hani_KR
+        case 0xC54E4E474C61746ELLU: // okr_Latn_NG
+        case 0xC94E4E474C61746ELLU: // oks_Latn_NG
+        case 0xD14E434D4C61746ELLU: // oku_Latn_CM
+        case 0xD54E50474C61746ELLU: // okv_Latn_PG
+        case 0xDD4E4E474C61746ELLU: // okx_Latn_NG
+        case 0xE54E4B484B686D72LLU: // okz_Khmr_KH
+        case 0x816E4E5044657661LLU: // ola_Deva_NP
+        case 0x8D6E545A4C61746ELLU: // old_Latn_TZ
+        case 0x916E425454696274LLU: // ole_Tibt_BT
+        case 0xA96E41554C61746ELLU: // olk_Latn_AU
+        case 0xB16E4E474C61746ELLU: // olm_Latn_NG
+        case 0xB96E52554C61746ELLU: // olo_Latn_RU
+        case 0xC56E56554C61746ELLU: // olr_Latn_VU
+        case 0xCD6E4C544C61746ELLU: // olt_Latn_LT
+        case 0xD16E414F4C61746ELLU: // olu_Latn_AO
+        case 0x6F6D45544C61746ELLU: // om_Latn_ET
+        case 0x818E55534C61746ELLU: // oma_Latn_US
+        case 0x858E56554C61746ELLU: // omb_Latn_VU
+        case 0x898E50454C61746ELLU: // omc_Latn_PE
+        case 0x998E50454C61746ELLU: // omg_Latn_PE
+        case 0xA18E43444C61746ELLU: // omi_Latn_CD
+        case 0xA98E52554379726CLLU: // omk_Cyrl_RU
+        case 0xAD8E43444C61746ELLU: // oml_Latn_CD
+        case 0xB98E50474C61746ELLU: // omo_Latn_PG
+        case 0xBD8E494E4D746569LLU: // omp_Mtei_IN
+        case 0xC58E494E4D6F6469LLU: // omr_Modi_IN
+        case 0xCD8E4B454C61746ELLU: // omt_Latn_KE
+        case 0xD18E50454C61746ELLU: // omu_Latn_PE
+        case 0xD98E50474C61746ELLU: // omw_Latn_PG
+        case 0xDD8E4D4D4D796D72LLU: // omx_Mymr_MM
+        case 0x81AE41524C61746ELLU: // ona_Latn_AR
+        case 0x91AE43414C61746ELLU: // one_Latn_CA
+        case 0x99AE50474C61746ELLU: // ong_Latn_PG
+        case 0xA1AE49444C61746ELLU: // oni_Latn_ID
+        case 0xA5AE50474C61746ELLU: // onj_Latn_PG
+        case 0xA9AE50474C61746ELLU: // onk_Latn_PG
+        case 0xB5AE50474C61746ELLU: // onn_Latn_PG
+        case 0xB9AE43414C61746ELLU: // ono_Latn_CA
+        case 0xBDAE494E4C61746ELLU: // onp_Latn_IN
+        case 0xC5AE50474C61746ELLU: // onr_Latn_PG
+        case 0xC9AE50474C61746ELLU: // ons_Latn_PG
+        case 0xCDAE50474C61746ELLU: // ont_Latn_PG
+        case 0xD1AE56554C61746ELLU: // onu_Latn_VU
+        case 0xDDAE49444C61746ELLU: // onx_Latn_ID
+        case 0x8DCE55534C61746ELLU: // ood_Latn_US
+        case 0xB5CE494E44657661LLU: // oon_Deva_IN
+        case 0xC5CE5A414C61746ELLU: // oor_Latn_ZA
+        case 0x81EE4E474C61746ELLU: // opa_Latn_NG
+        case 0xA9EE49444C61746ELLU: // opk_Latn_ID
+        case 0xB1EE50474C61746ELLU: // opm_Latn_PG
+        case 0xB9EE50474C61746ELLU: // opo_Latn_PG
+        case 0xCDEE4D584C61746ELLU: // opt_Latn_MX
+        case 0xE1EE42524C61746ELLU: // opy_Latn_BR
+        case 0x6F72494E4F727961LLU: // or_Orya_IN
+        case 0x822E53424C61746ELLU: // ora_Latn_SB
+        case 0x8A2E4B454C61746ELLU: // orc_Latn_KE
+        case 0x922E50454C61746ELLU: // ore_Latn_PE
+        case 0x9A2E4E474C61746ELLU: // org_Latn_NG
+        case 0xB62E4D594C61746ELLU: // orn_Latn_MY
+        case 0xBA2E50474C61746ELLU: // oro_Latn_PG
+        case 0xC62E4E474C61746ELLU: // orr_Latn_NG
+        case 0xCA2E4D594C61746ELLU: // ors_Latn_MY
+        case 0xCE2E494E54656C75LLU: // ort_Telu_IN
+        case 0xD22E504B41726162LLU: // oru_Arab_PK
+        case 0xD62E52554379726CLLU: // orv_Cyrl_RU
+        case 0xDA2E42524C61746ELLU: // orw_Latn_BR
+        case 0xDE2E4E474C61746ELLU: // orx_Latn_NG
+        case 0xE62E49444C61746ELLU: // orz_Latn_ID
+        case 0x6F7347454379726CLLU: // os_Cyrl_GE
+        case 0x824E55534F736765LLU: // osa_Osge_US
+        case 0x8A4E49544974616CLLU: // osc_Ital_IT
+        case 0xA24E49444A617661LLU: // osi_Java_ID
+        case 0xBA4E4E474C61746ELLU: // oso_Latn_NG
+        case 0xBE4E45534C61746ELLU: // osp_Latn_ES
+        case 0xCE4E434D4C61746ELLU: // ost_Latn_CM
+        case 0xD24E50474C61746ELLU: // osu_Latn_PG
+        case 0xDE4E44454C61746ELLU: // osx_Latn_DE
+        case 0x826E545241726162LLU: // ota_Arab_TR
+        case 0x866E434E54696274LLU: // otb_Tibt_CN
+        case 0x8E6E49444C61746ELLU: // otd_Latn_ID
+        case 0x926E4D584C61746ELLU: // ote_Latn_MX
+        case 0xA26E42524C61746ELLU: // oti_Latn_BR
+        case 0xAA6E4D4E4F726B68LLU: // otk_Orkh_MN
+        case 0xAE6E4D584C61746ELLU: // otl_Latn_MX
+        case 0xB26E4D584C61746ELLU: // otm_Latn_MX
+        case 0xB66E4D584C61746ELLU: // otn_Latn_MX
+        case 0xC26E4D584C61746ELLU: // otq_Latn_MX
+        case 0xC66E53444C61746ELLU: // otr_Latn_SD
+        case 0xCA6E4D584C61746ELLU: // ots_Latn_MX
+        case 0xCE6E4D584C61746ELLU: // ott_Latn_MX
+        case 0xD26E42524C61746ELLU: // otu_Latn_BR
+        case 0xDA6E43414C61746ELLU: // otw_Latn_CA
+        case 0xDE6E4D584C61746ELLU: // otx_Latn_MX
+        case 0xE26E494E4772616ELLU: // oty_Gran_IN
+        case 0xE66E4D584C61746ELLU: // otz_Latn_MX
+        case 0x868E4C524C61746ELLU: // oub_Latn_LR
+        case 0x928E50474C61746ELLU: // oue_Latn_PG
+        case 0xA28E434E4F756772LLU: // oui_Ougr_CN
+        case 0xB28E50474C61746ELLU: // oum_Latn_PG
+        case 0x8EAE53454C61746ELLU: // ovd_Latn_SE
+        case 0xA2CE50474C61746ELLU: // owi_Latn_PG
+        case 0xAECE47424C61746ELLU: // owl_Latn_GB
+        case 0x8F0E45544C61746ELLU: // oyd_Latn_ET
+        case 0xB30E42524C61746ELLU: // oym_Latn_BR
+        case 0xE30E50474C61746ELLU: // oyy_Latn_PG
+        case 0xB32E434D4C61746ELLU: // ozm_Latn_CM
+        case 0x7061504B41726162LLU: // pa_Arab_PK
+        case 0x7061494E47757275LLU: // pa_Guru_IN
+        case 0x840F42524C61746ELLU: // pab_Latn_BR
+        case 0x880F564E4C61746ELLU: // pac_Latn_VN
+        case 0x8C0F42524C61746ELLU: // pad_Latn_BR
+        case 0x900F43444C61746ELLU: // pae_Latn_CD
+        case 0x940F42524C61746ELLU: // paf_Latn_BR
+        case 0x980F50484C61746ELLU: // pag_Latn_PH
+        case 0x9C0F42524C61746ELLU: // pah_Latn_BR
+        case 0xA00F4E474C61746ELLU: // pai_Latn_NG
+        case 0xA80F42524C61746ELLU: // pak_Latn_BR
+        case 0xAC0F495250686C69LLU: // pal_Phli_IR
+        case 0xAC0F434E50686C70LLU: // pal_Phlp_CN
+        case 0xB00F50484C61746ELLU: // pam_Latn_PH
+        case 0xB80F55534C61746ELLU: // pao_Latn_US
+        case 0xBC0F43574C61746ELLU: // pap_Latn_CW
+        case 0xC00F544A4379726CLLU: // paq_Cyrl_TJ
+        case 0xC40F55534C61746ELLU: // par_Latn_US
+        case 0xC80F49444C61746ELLU: // pas_Latn_ID
+        case 0xD00F50574C61746ELLU: // pau_Latn_PW
+        case 0xD40F42524C61746ELLU: // pav_Latn_BR
+        case 0xD80F55534C61746ELLU: // paw_Latn_US
+        case 0xDC0F42524C61746ELLU: // pax_Latn_BR
+        case 0xE00F484E4C61746ELLU: // pay_Latn_HN
+        case 0xE40F42524C61746ELLU: // paz_Latn_BR
+        case 0x842F434F4C61746ELLU: // pbb_Latn_CO
+        case 0x882F47594C61746ELLU: // pbc_Latn_GY
+        case 0x902F4D584C61746ELLU: // pbe_Latn_MX
+        case 0x942F4D584C61746ELLU: // pbf_Latn_MX
+        case 0x982F56454C61746ELLU: // pbg_Latn_VE
+        case 0x9C2F56454C61746ELLU: // pbh_Latn_VE
+        case 0xA02F434D4C61746ELLU: // pbi_Latn_CM
+        case 0xAC2F4E474C61746ELLU: // pbl_Latn_NG
+        case 0xB02F4D584C61746ELLU: // pbm_Latn_MX
+        case 0xB42F4E474C61746ELLU: // pbn_Latn_NG
+        case 0xB82F47574C61746ELLU: // pbo_Latn_GW
+        case 0xBC2F474E4C61746ELLU: // pbp_Latn_GN
+        case 0xC42F545A4C61746ELLU: // pbr_Latn_TZ
+        case 0xC82F4D584C61746ELLU: // pbs_Latn_MX
+        case 0xCC2F414641726162LLU: // pbt_Arab_AF
+        case 0xD42F494E4C61746ELLU: // pbv_Latn_IN
+        case 0xE02F50474C61746ELLU: // pby_Latn_PG
+        case 0x804F4D584C61746ELLU: // pca_Latn_MX
+        case 0x844F4B484B686D72LLU: // pcb_Khmr_KH
+        case 0x884F434E4C61746ELLU: // pcc_Latn_CN
+        case 0x8C4F46524C61746ELLU: // pcd_Latn_FR
+        case 0x904F4D4D4D796D72LLU: // pce_Mymr_MM
+        case 0x944F494E4D6C796DLLU: // pcf_Mlym_IN
+        case 0x984F494E4D6C796DLLU: // pcg_Mlym_IN
+        case 0x9C4F494E44657661LLU: // pch_Deva_IN
+        case 0xA04F494E44657661LLU: // pci_Deva_IN
+        case 0xA44F494E54656C75LLU: // pcj_Telu_IN
+        case 0xA84F494E4C61746ELLU: // pck_Latn_IN
+        case 0xB04F4E474C61746ELLU: // pcm_Latn_NG
+        case 0xB44F4E474C61746ELLU: // pcn_Latn_NG
+        case 0xBC4F424F4C61746ELLU: // pcp_Latn_BO
+        case 0xD84F4E474C61746ELLU: // pcw_Latn_NG
+        case 0x806F50474C61746ELLU: // pda_Latn_PG
+        case 0x886F55534C61746ELLU: // pdc_Latn_US
+        case 0xB46F49444C61746ELLU: // pdn_Latn_ID
+        case 0xB86F49444C61746ELLU: // pdo_Latn_ID
+        case 0xCC6F43414C61746ELLU: // pdt_Latn_CA
+        case 0xD06F4D4D4C61746ELLU: // pdu_Latn_MM
+        case 0x808F49444C61746ELLU: // pea_Latn_ID
+        case 0x848F55534C61746ELLU: // peb_Latn_US
+        case 0x8C8F50474C61746ELLU: // ped_Latn_PG
+        case 0x908F49444C61746ELLU: // pee_Latn_ID
+        case 0x988F494E4F727961LLU: // peg_Orya_IN
+        case 0xA08F4D584C61746ELLU: // pei_Latn_MX
+        case 0xA88F50474C61746ELLU: // pek_Latn_PG
+        case 0xAC8F49444C61746ELLU: // pel_Latn_ID
+        case 0xB08F43444C61746ELLU: // pem_Latn_CD
+        case 0xB88F49525870656FLLU: // peo_Xpeo_IR
+        case 0xBC8F50474C61746ELLU: // pep_Latn_PG
+        case 0xC08F55534C61746ELLU: // peq_Latn_US
+        case 0xD48F56454C61746ELLU: // pev_Latn_VE
+        case 0xDC8F50474C61746ELLU: // pex_Latn_PG
+        case 0xE08F49444C61746ELLU: // pey_Latn_ID
+        case 0xE48F4D594C61746ELLU: // pez_Latn_MY
+        case 0x80AF464D4C61746ELLU: // pfa_Latn_FM
+        case 0x90AF434D4C61746ELLU: // pfe_Latn_CM
+        case 0xACAF44454C61746ELLU: // pfl_Latn_DE
+        case 0x80CF53534C61746ELLU: // pga_Latn_SS
+        case 0x8CCF504B4B686172LLU: // pgd_Khar_PK
+        case 0x98CF494E44657661LLU: // pgg_Deva_IN
+        case 0xA0CF50474C61746ELLU: // pgi_Latn_PG
+        case 0xA8CF56554C61746ELLU: // pgk_Latn_VU
+        case 0xACCF49454F67616DLLU: // pgl_Ogam_IE
+        case 0xB4CF49544974616CLLU: // pgn_Ital_IT
+        case 0xC8CF4E474C61746ELLU: // pgs_Latn_NG
+        case 0xD0CF49444C61746ELLU: // pgu_Latn_ID
+        case 0x8CEF494E44657661LLU: // phd_Deva_IN
+        case 0x98EF564E4C61746ELLU: // phg_Latn_VN
+        case 0x9CEF564E4C61746ELLU: // phh_Latn_VN
+        case 0xA8EF494E4D796D72LLU: // phk_Mymr_IN
+        case 0xACEF504B41726162LLU: // phl_Arab_PK
+        case 0xB0EF4D5A4C61746ELLU: // phm_Latn_MZ
+        case 0xB4EF4C4250686E78LLU: // phn_Phnx_LB
+        case 0xB8EF4C414C616F6FLLU: // pho_Laoo_LA
+        case 0xC4EF504B41726162LLU: // phr_Arab_PK
+        case 0xCCEF544854686169LLU: // pht_Thai_TH
+        case 0xD0EF544854686169LLU: // phu_Thai_TH
+        case 0xD4EF414641726162LLU: // phv_Arab_AF
+        case 0xD8EF4E5044657661LLU: // phw_Deva_NP
+        case 0x7069494E53696E68LLU: // pi_Sinh_IN
+        case 0x810F4D584C61746ELLU: // pia_Latn_MX
+        case 0x850F50454C61746ELLU: // pib_Latn_PE
+        case 0x890F47414C61746ELLU: // pic_Latn_GA
+        case 0x8D0F56454C61746ELLU: // pid_Latn_VE
+        case 0x950F464D4C61746ELLU: // pif_Latn_FM
+        case 0x990F50454C61746ELLU: // pig_Latn_PE
+        case 0x9D0F4E464C61746ELLU: // pih_Latn_NF
+        case 0xA50F434F4C61746ELLU: // pij_Latn_CO
+        case 0xAD0F424A4C61746ELLU: // pil_Latn_BJ
+        case 0xB10F55534C61746ELLU: // pim_Latn_US
+        case 0xB50F50474C61746ELLU: // pin_Latn_PG
+        case 0xB90F434F4C61746ELLU: // pio_Latn_CO
+        case 0xBD0F4E474C61746ELLU: // pip_Latn_NG
+        case 0xC50F42524C61746ELLU: // pir_Latn_BR
+        case 0xC90F53424C61746ELLU: // pis_Latn_SB
+        case 0xCD0F41554C61746ELLU: // pit_Latn_AU
+        case 0xD10F41554C61746ELLU: // piu_Latn_AU
+        case 0xD50F53424C61746ELLU: // piv_Latn_SB
+        case 0xD90F545A4C61746ELLU: // piw_Latn_TZ
+        case 0xDD0F50474C61746ELLU: // pix_Latn_PG
+        case 0xE10F4E474C61746ELLU: // piy_Latn_NG
+        case 0xE50F4E434C61746ELLU: // piz_Latn_NC
+        case 0xCD2F41554C61746ELLU: // pjt_Latn_AU
+        case 0x814F494E42726168LLU: // pka_Brah_IN
+        case 0x854F4B454C61746ELLU: // pkb_Latn_KE
+        case 0x994F50474C61746ELLU: // pkg_Latn_PG
+        case 0x9D4F42444C61746ELLU: // pkh_Latn_BD
+        case 0xB54F41554C61746ELLU: // pkn_Latn_AU
+        case 0xB94F4B454C61746ELLU: // pko_Latn_KE
+        case 0xBD4F434B4C61746ELLU: // pkp_Latn_CK
+        case 0xC54F494E4D6C796DLLU: // pkr_Mlym_IN
+        case 0xD14F49444C61746ELLU: // pku_Latn_ID
+        case 0x706C504C4C61746ELLU: // pl_Latn_PL
+        case 0x816F50474C61746ELLU: // pla_Latn_PG
+        case 0x856F56554C61746ELLU: // plb_Latn_VU
+        case 0x896F50484C61746ELLU: // plc_Latn_PH
+        case 0x8D6F47424C61746ELLU: // pld_Latn_GB
+        case 0x916F49444C61746ELLU: // ple_Latn_ID
+        case 0x996F41524C61746ELLU: // plg_Latn_AR
+        case 0x9D6F49444C61746ELLU: // plh_Latn_ID
+        case 0xA96F504B41726162LLU: // plk_Arab_PK
+        case 0xAD6F4D4D4D796D72LLU: // pll_Mymr_MM
+        case 0xB56F434F4C61746ELLU: // pln_Latn_CO
+        case 0xB96F4D584C61746ELLU: // plo_Latn_MX
+        case 0xC56F43494C61746ELLU: // plr_Latn_CI
+        case 0xC96F4D584C61746ELLU: // pls_Latn_MX
+        case 0xD16F42524C61746ELLU: // plu_Latn_BR
+        case 0xD56F50484C61746ELLU: // plv_Latn_PH
+        case 0xD96F50484C61746ELLU: // plw_Latn_PH
+        case 0xE56F4D594C61746ELLU: // plz_Latn_MY
+        case 0x818F56554C61746ELLU: // pma_Latn_VU
+        case 0x858F43444C61746ELLU: // pmb_Latn_CD
+        case 0x8D8F41554C61746ELLU: // pmd_Latn_AU
+        case 0x918F4E434C61746ELLU: // pme_Latn_NC
+        case 0x958F49444C61746ELLU: // pmf_Latn_ID
+        case 0x9D8F494E42726168LLU: // pmh_Brah_IN
+        case 0xA18F434E4C61746ELLU: // pmi_Latn_CN
+        case 0xA58F434E4C61746ELLU: // pmj_Latn_CN
+        case 0xAD8F544E4C61746ELLU: // pml_Latn_TN
+        case 0xB18F434D4C61746ELLU: // pmm_Latn_CM
+        case 0xB58F434D4C61746ELLU: // pmn_Latn_CM
+        case 0xB98F49444C61746ELLU: // pmo_Latn_ID
+        case 0xC18F4D584C61746ELLU: // pmq_Latn_MX
+        case 0xC58F50474C61746ELLU: // pmr_Latn_PG
+        case 0xC98F49544C61746ELLU: // pms_Latn_IT
+        case 0xCD8F50464C61746ELLU: // pmt_Latn_PF
+        case 0xD98F55534C61746ELLU: // pmw_Latn_US
+        case 0xDD8F494E4C61746ELLU: // pmx_Latn_IN
+        case 0xE18F49444C61746ELLU: // pmy_Latn_ID
+        case 0xE58F4D584C61746ELLU: // pmz_Latn_MX
+        case 0x81AF4D594C61746ELLU: // pna_Latn_MY
+        case 0x89AF49444C61746ELLU: // pnc_Latn_ID
+        case 0x8DAF414F4C61746ELLU: // pnd_Latn_AO
+        case 0x91AF4D594C61746ELLU: // pne_Latn_MY
+        case 0x99AF4E474C61746ELLU: // png_Latn_NG
+        case 0x9DAF434B4C61746ELLU: // pnh_Latn_CK
+        case 0xA1AF49444C61746ELLU: // pni_Latn_ID
+        case 0xA5AF41554C61746ELLU: // pnj_Latn_AU
+        case 0xA9AF424F4C61746ELLU: // pnk_Latn_BO
+        case 0xADAF42464C61746ELLU: // pnl_Latn_BF
+        case 0xB1AF4D594C61746ELLU: // pnm_Latn_MY
+        case 0xB5AF50474C61746ELLU: // pnn_Latn_PG
+        case 0xB9AF50454C61746ELLU: // pno_Latn_PE
+        case 0xBDAF49444C61746ELLU: // pnp_Latn_ID
+        case 0xC1AF42464C61746ELLU: // pnq_Latn_BF
+        case 0xC5AF50474C61746ELLU: // pnr_Latn_PG
+        case 0xC9AF49444C61746ELLU: // pns_Latn_ID
+        case 0xCDAF47524772656BLLU: // pnt_Grek_GR
+        case 0xD5AF41554C61746ELLU: // pnv_Latn_AU
+        case 0xD9AF41554C61746ELLU: // pnw_Latn_AU
+        case 0xE1AF434D4C61746ELLU: // pny_Latn_CM
+        case 0xE5AF43464C61746ELLU: // pnz_Latn_CF
+        case 0x89CF47544C61746ELLU: // poc_Latn_GT
+        case 0x91CF4D584C61746ELLU: // poe_Latn_MX
+        case 0x95CF43444C61746ELLU: // pof_Latn_CD
+        case 0x99CF42524C61746ELLU: // pog_Latn_BR
+        case 0x9DCF47544C61746ELLU: // poh_Latn_GT
+        case 0xA1CF4D584C61746ELLU: // poi_Latn_MX
+        case 0xA9CF42524C61746ELLU: // pok_Latn_BR
+        case 0xB1CF55534C61746ELLU: // pom_Latn_US
+        case 0xB5CF464D4C61746ELLU: // pon_Latn_FM
+        case 0xB9CF55534C61746ELLU: // poo_Latn_US
+        case 0xBDCF4E434C61746ELLU: // pop_Latn_NC
+        case 0xC1CF4D584C61746ELLU: // poq_Latn_MX
+        case 0xC9CF4D584C61746ELLU: // pos_Latn_MX
+        case 0xCDCF55534C61746ELLU: // pot_Latn_US
+        case 0xD5CF47574C61746ELLU: // pov_Latn_GW
+        case 0xD9CF4D584C61746ELLU: // pow_Latn_MX
+        case 0xE1CF545A4C61746ELLU: // poy_Latn_TZ
+        case 0x91EF50474C61746ELLU: // ppe_Latn_PG
+        case 0xA1EF4D584C61746ELLU: // ppi_Latn_MX
+        case 0xA9EF49444C61746ELLU: // ppk_Latn_ID
+        case 0xADEF53564C61746ELLU: // ppl_Latn_SV
+        case 0xB1EF49444C61746ELLU: // ppm_Latn_ID
+        case 0xB5EF50474C61746ELLU: // ppn_Latn_PG
+        case 0xB9EF50474C61746ELLU: // ppo_Latn_PG
+        case 0xBDEF43444C61746ELLU: // ppp_Latn_CD
+        case 0xC1EF50474C61746ELLU: // ppq_Latn_PG
+        case 0xC9EF4D584C61746ELLU: // pps_Latn_MX
+        case 0xCDEF50474C61746ELLU: // ppt_Latn_PG
+        case 0x820F4E474C61746ELLU: // pqa_Latn_NG
+        case 0xB20F43414C61746ELLU: // pqm_Latn_CA
+        case 0x822F504B4B686172LLU: // pra_Khar_PK
+        case 0x8A2F414641726162LLU: // prc_Arab_AF
+        case 0x8E2F495241726162LLU: // prd_Arab_IR
+        case 0x922F53544C61746ELLU: // pre_Latn_ST
+        case 0x962F50484C61746ELLU: // prf_Latn_PH
+        case 0x9A2F504C4C61746ELLU: // prg_Latn_PL
+        case 0x9E2F50484C61746ELLU: // prh_Latn_PH
+        case 0xA22F4E434C61746ELLU: // pri_Latn_NC
+        case 0xAA2F4D4D4C61746ELLU: // prk_Latn_MM
+        case 0xB22F50474C61746ELLU: // prm_Latn_PG
+        case 0xBA2F46524C61746ELLU: // pro_Latn_FR
+        case 0xC22F50454C61746ELLU: // prq_Latn_PE
+        case 0xC62F42524C61746ELLU: // prr_Latn_BR
+        case 0xCE2F544854686169LLU: // prt_Thai_TH
+        case 0xD22F49444C61746ELLU: // pru_Latn_ID
+        case 0xDA2F50474C61746ELLU: // prw_Latn_PG
+        case 0xDE2F494E41726162LLU: // prx_Arab_IN
+        case 0x7073414641726162LLU: // ps_Arab_AF
+        case 0x824F49444C61746ELLU: // psa_Latn_ID
+        case 0x924F49444C61746ELLU: // pse_Latn_ID
+        case 0x9E4F414641726162LLU: // psh_Arab_AF
+        case 0xA24F414641726162LLU: // psi_Arab_AF
+        case 0xB24F424F4C61746ELLU: // psm_Latn_BO
+        case 0xB64F49444C61746ELLU: // psn_Latn_ID
+        case 0xC24F50474C61746ELLU: // psq_Latn_PG
+        case 0xCA4F50474C61746ELLU: // pss_Latn_PG
+        case 0xCE4F504B41726162LLU: // pst_Arab_PK
+        case 0xD24F494E42726168LLU: // psu_Brah_IN
+        case 0xDA4F56554C61746ELLU: // psw_Latn_VU
+        case 0x707442524C61746ELLU: // pt_Latn_BR
+        case 0x826F50594C61746ELLU: // pta_Latn_PY
+        case 0x9E6F42524C61746ELLU: // pth_Latn_BR
+        case 0xA26F41554C61746ELLU: // pti_Latn_AU
+        case 0xB66F49444C61746ELLU: // ptn_Latn_ID
+        case 0xBA6F42524C61746ELLU: // pto_Latn_BR
+        case 0xBE6F50474C61746ELLU: // ptp_Latn_PG
+        case 0xC66F56554C61746ELLU: // ptr_Latn_VU
+        case 0xCE6F49444C61746ELLU: // ptt_Latn_ID
+        case 0xD26F49444C61746ELLU: // ptu_Latn_ID
+        case 0xD66F56554C61746ELLU: // ptv_Latn_VU
+        case 0x828F4D584C61746ELLU: // pua_Latn_MX
+        case 0x868F494E4C61746ELLU: // pub_Latn_IN
+        case 0x8A8F49444C61746ELLU: // puc_Latn_ID
+        case 0x8E8F49444C61746ELLU: // pud_Latn_ID
+        case 0x928F41524C61746ELLU: // pue_Latn_AR
+        case 0x968F49444C61746ELLU: // puf_Latn_ID
+        case 0x9A8F42464C61746ELLU: // pug_Latn_BF
+        case 0xA28F434F4C61746ELLU: // pui_Latn_CO
+        case 0xA68F49444C61746ELLU: // puj_Latn_ID
+        case 0xB28F4E5044657661LLU: // pum_Deva_NP
+        case 0xBA8F564E4C61746ELLU: // puo_Latn_VN
+        case 0xBE8F50474C61746ELLU: // pup_Latn_PG
+        case 0xC28F424F4C61746ELLU: // puq_Latn_BO
+        case 0xC68F42524C61746ELLU: // pur_Latn_BR
+        case 0xCE8F49444C61746ELLU: // put_Latn_ID
+        case 0xD28F47414C61746ELLU: // puu_Latn_GA
+        case 0xDA8F464D4C61746ELLU: // puw_Latn_FM
+        case 0xDE8F50474C61746ELLU: // pux_Latn_PG
+        case 0xE28F55534C61746ELLU: // puy_Latn_US
+        case 0x82CF50474C61746ELLU: // pwa_Latn_PG
+        case 0x86CF4E474C61746ELLU: // pwb_Latn_NG
+        case 0x9ACF50474C61746ELLU: // pwg_Latn_PG
+        case 0xB2CF50484C61746ELLU: // pwm_Latn_PH
+        case 0xB6CF54574C61746ELLU: // pwn_Latn_TW
+        case 0xBACF4D4D4D796D72LLU: // pwo_Mymr_MM
+        case 0xC6CF494E44657661LLU: // pwr_Deva_IN
+        case 0xDACF544854686169LLU: // pww_Thai_TH
+        case 0xB2EF4D584C61746ELLU: // pxm_Latn_MX
+        case 0x930F43494C61746ELLU: // pye_Latn_CI
+        case 0xB30F4E474C61746ELLU: // pym_Latn_NG
+        case 0xB70F42524C61746ELLU: // pyn_Latn_BR
+        case 0xD30F54574C61746ELLU: // pyu_Latn_TW
+        case 0xDF0F4D4D4D796D72LLU: // pyx_Mymr_MM
+        case 0xE30F4D4D4C61746ELLU: // pyy_Latn_MM
+        case 0x932F4E474C61746ELLU: // pze_Latn_NG
+        case 0x9F2F54574C61746ELLU: // pzh_Latn_TW
+        case 0xB72F4D4D4C61746ELLU: // pzn_Latn_MM
+        case 0x717550454C61746ELLU: // qu_Latn_PE
+        case 0x829055534C61746ELLU: // qua_Latn_US
+        case 0x869050454C61746ELLU: // qub_Latn_PE
+        case 0x8A9047544C61746ELLU: // quc_Latn_GT
+        case 0x8E9045434C61746ELLU: // qud_Latn_EC
+        case 0x969050454C61746ELLU: // quf_Latn_PE
+        case 0x9A9045434C61746ELLU: // qug_Latn_EC
+        case 0xA29055534C61746ELLU: // qui_Latn_US
+        case 0xAA9050454C61746ELLU: // quk_Latn_PE
+        case 0xAE90424F4C61746ELLU: // qul_Latn_BO
+        case 0xB29047544C61746ELLU: // qum_Latn_GT
+        case 0xB69055534C61746ELLU: // qun_Latn_US
+        case 0xBE9050454C61746ELLU: // qup_Latn_PE
+        case 0xC29045534C61746ELLU: // quq_Latn_ES
+        case 0xC69050454C61746ELLU: // qur_Latn_PE
+        case 0xCA9041524C61746ELLU: // qus_Latn_AR
+        case 0xD69047544C61746ELLU: // quv_Latn_GT
+        case 0xDA9045434C61746ELLU: // quw_Latn_EC
+        case 0xDE9050454C61746ELLU: // qux_Latn_PE
+        case 0xE29050454C61746ELLU: // quy_Latn_PE
+        case 0x82B050454C61746ELLU: // qva_Latn_PE
+        case 0x8AB050454C61746ELLU: // qvc_Latn_PE
+        case 0x92B050454C61746ELLU: // qve_Latn_PE
+        case 0x9EB050454C61746ELLU: // qvh_Latn_PE
+        case 0xA2B045434C61746ELLU: // qvi_Latn_EC
+        case 0xA6B045434C61746ELLU: // qvj_Latn_EC
+        case 0xAEB050454C61746ELLU: // qvl_Latn_PE
+        case 0xB2B050454C61746ELLU: // qvm_Latn_PE
+        case 0xB6B050454C61746ELLU: // qvn_Latn_PE
+        case 0xBAB050454C61746ELLU: // qvo_Latn_PE
+        case 0xBEB050454C61746ELLU: // qvp_Latn_PE
+        case 0xCAB050454C61746ELLU: // qvs_Latn_PE
+        case 0xDAB050454C61746ELLU: // qvw_Latn_PE
+        case 0xE6B045434C61746ELLU: // qvz_Latn_EC
+        case 0x82D050454C61746ELLU: // qwa_Latn_PE
+        case 0x8AD050454C61746ELLU: // qwc_Latn_PE
+        case 0x9ED050454C61746ELLU: // qwh_Latn_PE
+        case 0xB2D048554C61746ELLU: // qwm_Latn_HU
+        case 0xCAD050454C61746ELLU: // qws_Latn_PE
+        case 0xCED055534C61746ELLU: // qwt_Latn_US
+        case 0x82F050454C61746ELLU: // qxa_Latn_PE
+        case 0x8AF050454C61746ELLU: // qxc_Latn_PE
+        case 0x9EF050454C61746ELLU: // qxh_Latn_PE
+        case 0xAEF045434C61746ELLU: // qxl_Latn_EC
+        case 0xB6F050454C61746ELLU: // qxn_Latn_PE
+        case 0xBAF050454C61746ELLU: // qxo_Latn_PE
+        case 0xBEF050454C61746ELLU: // qxp_Latn_PE
+        case 0xC2F0495241726162LLU: // qxq_Arab_IR
+        case 0xC6F045434C61746ELLU: // qxr_Latn_EC
+        case 0xCEF050454C61746ELLU: // qxt_Latn_PE
+        case 0xD2F050454C61746ELLU: // qxu_Latn_PE
+        case 0xDAF050454C61746ELLU: // qxw_Latn_PE
+        case 0xBF1055534C61746ELLU: // qyp_Latn_US
+        case 0x80114E5044657661LLU: // raa_Deva_NP
+        case 0x84114E5044657661LLU: // rab_Deva_NP
+        case 0x881149444C61746ELLU: // rac_Latn_ID
+        case 0x8C11564E4C61746ELLU: // rad_Latn_VN
+        case 0x94114E5044657661LLU: // raf_Deva_NP
+        case 0x98114B454C61746ELLU: // rag_Latn_KE
+        case 0x9C11494E42656E67LLU: // rah_Beng_IN
+        case 0xA01150474C61746ELLU: // rai_Latn_PG
+        case 0xA411494E44657661LLU: // raj_Deva_IN
+        case 0xA81150474C61746ELLU: // rak_Latn_PG
+        case 0xB01142524C61746ELLU: // ram_Latn_BR
+        case 0xB41149444C61746ELLU: // ran_Latn_ID
+        case 0xB81150474C61746ELLU: // rao_Latn_PG
+        case 0xBC11434C4C61746ELLU: // rap_Latn_CL
+        case 0xC411434B4C61746ELLU: // rar_Latn_CK
+        case 0xD4114E5044657661LLU: // rav_Deva_NP
+        case 0xD8114D4D4C61746ELLU: // raw_Latn_MM
+        case 0xDC114E474C61746ELLU: // rax_Latn_NG
+        case 0xE01150464C61746ELLU: // ray_Latn_PF
+        case 0xE41149444C61746ELLU: // raz_Latn_ID
+        case 0x84314D4D4D796D72LLU: // rbb_Mymr_MM
+        case 0xA83150484C61746ELLU: // rbk_Latn_PH
+        case 0xAC3150484C61746ELLU: // rbl_Latn_PH
+        case 0xBC3141554C61746ELLU: // rbp_Latn_AU
+        case 0x945152454C61746ELLU: // rcf_Latn_RE
+        case 0x8471495241726162LLU: // rdb_Arab_IR
+        case 0x809150474C61746ELLU: // rea_Latn_PG
+        case 0x849149444C61746ELLU: // reb_Latn_ID
+        case 0x90914D594C61746ELLU: // ree_Latn_MY
+        case 0x9891545A4C61746ELLU: // reg_Latn_TZ
+        case 0xA091494E4F727961LLU: // rei_Orya_IN
+        case 0xA49149444C61746ELLU: // rej_Latn_ID
+        case 0xAC914B454C61746ELLU: // rel_Latn_KE
+        case 0xB09150454C61746ELLU: // rem_Latn_PE
+        case 0xB491564E4C61746ELLU: // ren_Latn_VN
+        case 0xC8914E474C61746ELLU: // res_Latn_NG
+        case 0xCC9149444C61746ELLU: // ret_Latn_ID
+        case 0xE091424F4C61746ELLU: // rey_Latn_BO
+        case 0x80D156554C61746ELLU: // rga_Latn_VU
+        case 0xB4D149544C61746ELLU: // rgn_Latn_IT
+        case 0xC4D150454C61746ELLU: // rgr_Latn_PE
+        case 0xC8D1564E4C61746ELLU: // rgs_Latn_VN
+        case 0xD0D149444C61746ELLU: // rgu_Latn_ID
+        case 0x98F14D4D526F6867LLU: // rhg_Rohg_MM
+        case 0xBCF150474C61746ELLU: // rhp_Latn_PG
+        case 0x8111494E4C61746ELLU: // ria_Latn_IN
+        case 0x95114D414C61746ELLU: // rif_Latn_MA
+        case 0xAD114D4D4C61746ELLU: // ril_Latn_MM
+        case 0xB111545A4C61746ELLU: // rim_Latn_TZ
+        case 0xB5114E474C61746ELLU: // rin_Latn_NG
+        case 0xC51149444C61746ELLU: // rir_Latn_ID
+        case 0xCD1141554C61746ELLU: // rit_Latn_AU
+        case 0xD11149444C61746ELLU: // riu_Latn_ID
+        case 0x993149444C61746ELLU: // rjg_Latn_ID
+        case 0xA1314E5044657661LLU: // rji_Deva_NP
+        case 0xC9314E5044657661LLU: // rjs_Deva_NP
+        case 0x81514B484B686D72LLU: // rka_Khmr_KH
+        case 0x855142524C61746ELLU: // rkb_Latn_BR
+        case 0x9D51434B4C61746ELLU: // rkh_Latn_CK
+        case 0xA1514D4D4D796D72LLU: // rki_Mymr_MM
+        case 0xB15142464C61746ELLU: // rkm_Latn_BF
+        case 0xCD51424442656E67LLU: // rkt_Beng_BD
+        case 0xD95141554C61746ELLU: // rkw_Latn_AU
+        case 0x726D43484C61746ELLU: // rm_Latn_CH
+        case 0x81914E494C61746ELLU: // rma_Latn_NI
+        case 0x859141554C61746ELLU: // rmb_Latn_AU
+        case 0x8991534B4C61746ELLU: // rmc_Latn_SK
+        case 0x8D91444B4C61746ELLU: // rmd_Latn_DK
+        case 0x919147424C61746ELLU: // rme_Latn_GB
+        case 0x959146494C61746ELLU: // rmf_Latn_FI
+        case 0x99914E4F4C61746ELLU: // rmg_Latn_NO
+        case 0x9D9149444C61746ELLU: // rmh_Latn_ID
+        case 0xA191414D41726D6ELLU: // rmi_Armn_AM
+        case 0xA99150474C61746ELLU: // rmk_Latn_PG
+        case 0xAD91504C4C61746ELLU: // rml_Latn_PL
+        case 0xB19149444C61746ELLU: // rmm_Latn_ID
+        case 0xB59152534C61746ELLU: // rmn_Latn_RS
+        case 0xB99143484C61746ELLU: // rmo_Latn_CH
+        case 0xBD9150474C61746ELLU: // rmp_Latn_PG
+        case 0xC19145534C61746ELLU: // rmq_Latn_ES
+        case 0xCD91495241726162LLU: // rmt_Arab_IR
+        case 0xD19153454C61746ELLU: // rmu_Latn_SE
+        case 0xD99147424C61746ELLU: // rmw_Latn_GB
+        case 0xDD91564E4C61746ELLU: // rmx_Latn_VN
+        case 0xE591494E4D796D72LLU: // rmz_Mymr_IN
+        case 0x726E42494C61746ELLU: // rn_Latn_BI
+        case 0x8DB143444C61746ELLU: // rnd_Latn_CD
+        case 0x99B14D5A4C61746ELLU: // rng_Latn_MZ
+        case 0xADB1494E4C61746ELLU: // rnl_Latn_IN
+        case 0xB5B149444C61746ELLU: // rnn_Latn_ID
+        case 0xC5B141554C61746ELLU: // rnr_Latn_AU
+        case 0xD9B1545A4C61746ELLU: // rnw_Latn_TZ
+        case 0x726F524F4C61746ELLU: // ro_Latn_RO
+        case 0x85D149444C61746ELLU: // rob_Latn_ID
+        case 0x89D1564E4C61746ELLU: // roc_Latn_VN
+        case 0x8DD14E474C61746ELLU: // rod_Latn_NG
+        case 0x91D150474C61746ELLU: // roe_Latn_PG
+        case 0x95D1545A4C61746ELLU: // rof_Latn_TZ
+        case 0x99D1564E4C61746ELLU: // rog_Latn_VN
+        case 0xADD150484C61746ELLU: // rol_Latn_PH
+        case 0xB1D1524F4C61746ELLU: // rom_Latn_RO
+        case 0xB9D150474C61746ELLU: // roo_Latn_PG
+        case 0xBDD141554C61746ELLU: // rop_Latn_AU
+        case 0xC5D149444C61746ELLU: // ror_Latn_ID
+        case 0xD1D154444C61746ELLU: // rou_Latn_TD
+        case 0xD9D149444C61746ELLU: // row_Latn_ID
+        case 0xB5F156554C61746ELLU: // rpn_Latn_VU
+        case 0xCDF150474C61746ELLU: // rpt_Latn_PG
+        case 0xA23153424C61746ELLU: // rri_Latn_SB
+        case 0xB2314E5A4C61746ELLU: // rrm_Latn_NZ
+        case 0xBA3150474C61746ELLU: // rro_Latn_PG
+        case 0xCE3141554C61746ELLU: // rrt_Latn_AU
+        case 0xAA5152534379726CLLU: // rsk_Cyrl_RS
+        case 0xDA514E474C61746ELLU: // rsw_Latn_NG
+        case 0x8A714D4D4C61746ELLU: // rtc_Latn_MM
+        case 0x9E7149444C61746ELLU: // rth_Latn_ID
+        case 0xB271464A4C61746ELLU: // rtm_Latn_FJ
+        case 0xDA71494E44657661LLU: // rtw_Deva_IN
+        case 0x727552554379726CLLU: // ru_Cyrl_RU
+        case 0x869155474C61746ELLU: // rub_Latn_UG
+        case 0x8A9155474C61746ELLU: // ruc_Latn_UG
+        case 0x929155414379726CLLU: // rue_Cyrl_UA
+        case 0x9691545A4C61746ELLU: // ruf_Latn_TZ
+        case 0x9A9153424C61746ELLU: // rug_Latn_SB
+        case 0xA291545A4C61746ELLU: // rui_Latn_TZ
+        case 0xAA914E474C61746ELLU: // ruk_Latn_NG
+        case 0xBA9148524C61746ELLU: // ruo_Latn_HR
+        case 0xBE91524F4C61746ELLU: // rup_Latn_RO
+        case 0xC29147524C61746ELLU: // ruq_Latn_GR
+        case 0xCE9152554379726CLLU: // rut_Cyrl_RU
+        case 0xD2914D594C61746ELLU: // ruu_Latn_MY
+        case 0xE2914E474C61746ELLU: // ruy_Latn_NG
+        case 0xE6914E474C61746ELLU: // ruz_Latn_NG
+        case 0x727752574C61746ELLU: // rw_Latn_RW
+        case 0x82D150474C61746ELLU: // rwa_Latn_PG
+        case 0xAAD1545A4C61746ELLU: // rwk_Latn_TZ
+        case 0xAED1545A4C61746ELLU: // rwl_Latn_TZ
+        case 0xB2D155474C61746ELLU: // rwm_Latn_UG
+        case 0xBAD150474C61746ELLU: // rwo_Latn_PG
+        case 0xC6D1494E44657661LLU: // rwr_Deva_IN
+        case 0x8EF141554C61746ELLU: // rxd_Latn_AU
+        case 0xDAF141554C61746ELLU: // rxw_Latn_AU
+        case 0xD3114A504B616E61LLU: // ryu_Kana_JP
+        case 0x7361494E44657661LLU: // sa_Deva_IN
+        case 0x801254444C61746ELLU: // saa_Latn_TD
+        case 0x841250414C61746ELLU: // sab_Latn_PA
+        case 0x881255534C61746ELLU: // sac_Latn_US
+        case 0x8C12545A4C61746ELLU: // sad_Latn_TZ
+        case 0x901242524C61746ELLU: // sae_Latn_BR
+        case 0x941247484C61746ELLU: // saf_Latn_GH
+        case 0x9C1252554379726CLLU: // sah_Cyrl_RU
+        case 0xA41249444C61746ELLU: // saj_Latn_ID
+        case 0xA81247414C61746ELLU: // sak_Latn_GA
+        case 0xB012505353616D72LLU: // sam_Samr_PS
+        case 0xB81249444C61746ELLU: // sao_Latn_ID
+        case 0xC0124B454C61746ELLU: // saq_Latn_KE
+        case 0xC412424F4C61746ELLU: // sar_Latn_BO
+        case 0xC81249444C61746ELLU: // sas_Latn_ID
+        case 0xCC12494E4F6C636BLLU: // sat_Olck_IN
+        case 0xD01249444C61746ELLU: // sau_Latn_ID
+        case 0xD412534E4C61746ELLU: // sav_Latn_SN
+        case 0xD81249444C61746ELLU: // saw_Latn_ID
+        case 0xDC1256554C61746ELLU: // sax_Latn_VU
+        case 0xE0124E474C61746ELLU: // say_Latn_NG
+        case 0xE412494E53617572LLU: // saz_Saur_IN
+        case 0x803254444C61746ELLU: // sba_Latn_TD
+        case 0x843253424C61746ELLU: // sbb_Latn_SB
+        case 0x883250474C61746ELLU: // sbc_Latn_PG
+        case 0x8C3242464C61746ELLU: // sbd_Latn_BF
+        case 0x903250474C61746ELLU: // sbe_Latn_PG
+        case 0x983249444C61746ELLU: // sbg_Latn_ID
+        case 0x9C3250474C61746ELLU: // sbh_Latn_PG
+        case 0xA03250474C61746ELLU: // sbi_Latn_PG
+        case 0xA43254444C61746ELLU: // sbj_Latn_TD
+        case 0xA832545A4C61746ELLU: // sbk_Latn_TZ
+        case 0xAC3250484C61746ELLU: // sbl_Latn_PH
+        case 0xB032545A4C61746ELLU: // sbm_Latn_TZ
+        case 0xB432504B41726162LLU: // sbn_Arab_PK
+        case 0xB8324D594C61746ELLU: // sbo_Latn_MY
+        case 0xBC32545A4C61746ELLU: // sbp_Latn_TZ
+        case 0xC03250474C61746ELLU: // sbq_Latn_PG
+        case 0xC43249444C61746ELLU: // sbr_Latn_ID
+        case 0xC8324E414C61746ELLU: // sbs_Latn_NA
+        case 0xCC3249444C61746ELLU: // sbt_Latn_ID
+        case 0xD032494E54696274LLU: // sbu_Tibt_IN
+        case 0xD43249544C61746ELLU: // sbv_Latn_IT
+        case 0xD83247414C61746ELLU: // sbw_Latn_GA
+        case 0xDC3249444C61746ELLU: // sbx_Latn_ID
+        case 0xE0325A4D4C61746ELLU: // sby_Latn_ZM
+        case 0xE43243464C61746ELLU: // sbz_Latn_CF
+        case 0x736349544C61746ELLU: // sc_Latn_IT
+        case 0x8452564E4C61746ELLU: // scb_Latn_VN
+        case 0x9052434E4C61746ELLU: // sce_Latn_CN
+        case 0x945250414C61746ELLU: // scf_Latn_PA
+        case 0x985249444C61746ELLU: // scg_Latn_ID
+        case 0x9C52494E4C61746ELLU: // sch_Latn_IN
+        case 0xA0524C4B4C61746ELLU: // sci_Latn_LK
+        case 0xA852494E44657661LLU: // sck_Deva_IN
+        case 0xAC52504B41726162LLU: // scl_Arab_PK
+        case 0xB45249544C61746ELLU: // scn_Latn_IT
+        case 0xB85247424C61746ELLU: // sco_Latn_GB
+        case 0xBC524E5044657661LLU: // scp_Deva_NP
+        case 0xC85243414C61746ELLU: // scs_Latn_CA
+        case 0xCC524C414C616F6FLLU: // sct_Laoo_LA
+        case 0xD052494E54616B72LLU: // scu_Takr_IN
+        case 0xD4524E474C61746ELLU: // scv_Latn_NG
+        case 0xD8524E474C61746ELLU: // scw_Latn_NG
+        case 0xDC5249544772656BLLU: // scx_Grek_IT
+        case 0x7364504B41726162LLU: // sd_Arab_PK
+        case 0x7364494E44657661LLU: // sd_Deva_IN
+        case 0x7364494E4B686F6ALLU: // sd_Khoj_IN
+        case 0x7364494E53696E64LLU: // sd_Sind_IN
+        case 0x807249444C61746ELLU: // sda_Latn_ID
+        case 0x8472495141726162LLU: // sdb_Arab_IQ
+        case 0x887249544C61746ELLU: // sdc_Latn_IT
+        case 0x90724E474C61746ELLU: // sde_Latn_NG
+        case 0x9472495141726162LLU: // sdf_Arab_IQ
+        case 0x9872414641726162LLU: // sdg_Arab_AF
+        case 0x9C72495241726162LLU: // sdh_Arab_IR
+        case 0xA47243474C61746ELLU: // sdj_Latn_CG
+        case 0xA87250474C61746ELLU: // sdk_Latn_PG
+        case 0xB47249544C61746ELLU: // sdn_Latn_IT
+        case 0xB8724D594C61746ELLU: // sdo_Latn_MY
+        case 0xC07249444C61746ELLU: // sdq_Latn_ID
+        case 0xC472424442656E67LLU: // sdr_Beng_BD
+        case 0xC872544E41726162LLU: // sds_Arab_TN
+        case 0xD07249444C61746ELLU: // sdu_Latn_ID
+        case 0xDC724D594C61746ELLU: // sdx_Latn_MY
+        case 0x73654E4F4C61746ELLU: // se_Latn_NO
+        case 0x80924D594C61746ELLU: // sea_Latn_MY
+        case 0x849243494C61746ELLU: // seb_Latn_CI
+        case 0x889243414C61746ELLU: // sec_Latn_CA
+        case 0x8C92564E4C61746ELLU: // sed_Latn_VN
+        case 0x909255534C61746ELLU: // see_Latn_US
+        case 0x949243494C61746ELLU: // sef_Latn_CI
+        case 0x9892545A4C61746ELLU: // seg_Latn_TZ
+        case 0x9C924D5A4C61746ELLU: // seh_Latn_MZ
+        case 0xA0924D584C61746ELLU: // sei_Latn_MX
+        case 0xA49250474C61746ELLU: // sej_Latn_PG
+        case 0xA89243414C61746ELLU: // sek_Latn_CA
+        case 0xAC9252554379726CLLU: // sel_Cyrl_RU
+        case 0xB49242464C61746ELLU: // sen_Latn_BF
+        case 0xB89250474C61746ELLU: // seo_Latn_PG
+        case 0xBC9242464C61746ELLU: // sep_Latn_BF
+        case 0xC09242464C61746ELLU: // seq_Latn_BF
+        case 0xC49255534C61746ELLU: // ser_Latn_US
+        case 0xC8924D4C4C61746ELLU: // ses_Latn_ML
+        case 0xCC9249444C61746ELLU: // set_Latn_ID
+        case 0xD09249444C61746ELLU: // seu_Latn_ID
+        case 0xD49243494C61746ELLU: // sev_Latn_CI
+        case 0xD89250474C61746ELLU: // sew_Latn_PG
+        case 0xE09245434C61746ELLU: // sey_Latn_EC
+        case 0xE4924D4D4C61746ELLU: // sez_Latn_MM
+        case 0x90B250484C61746ELLU: // sfe_Latn_PH
+        case 0xB0B2434E506C7264LLU: // sfm_Plrd_CN
+        case 0xD8B247484C61746ELLU: // sfw_Latn_GH
+        case 0x736743464C61746ELLU: // sg_Latn_CF
+        case 0x80D249454F67616DLLU: // sga_Ogam_IE
+        case 0x84D250484C61746ELLU: // sgb_Latn_PH
+        case 0x88D24B454C61746ELLU: // sgc_Latn_KE
+        case 0x8CD250484C61746ELLU: // sgd_Latn_PH
+        case 0x90D249444C61746ELLU: // sge_Latn_ID
+        case 0x9CD2544A4379726CLLU: // sgh_Cyrl_TJ
+        case 0xA0D2434D4C61746ELLU: // sgi_Latn_CM
+        case 0xA4D2494E44657661LLU: // sgj_Deva_IN
+        case 0xB0D24B454C61746ELLU: // sgm_Latn_KE
+        case 0xBCD2494E4C61746ELLU: // sgp_Latn_IN
+        case 0xC4D2495241726162LLU: // sgr_Arab_IR
+        case 0xC8D24C544C61746ELLU: // sgs_Latn_LT
+        case 0xCCD2425454696274LLU: // sgt_Tibt_BT
+        case 0xD0D249444C61746ELLU: // sgu_Latn_ID
+        case 0xD8D2455445746869LLU: // sgw_Ethi_ET
+        case 0xE0D2414641726162LLU: // sgy_Arab_AF
+        case 0xE4D250474C61746ELLU: // sgz_Latn_PG
+        case 0x80F24E474C61746ELLU: // sha_Latn_NG
+        case 0x84F242524C61746ELLU: // shb_Latn_BR
+        case 0x88F243444C61746ELLU: // shc_Latn_CD
+        case 0x8CF2504B41726162LLU: // shd_Arab_PK
+        case 0x90F245544C61746ELLU: // she_Latn_ET
+        case 0x98F242574C61746ELLU: // shg_Latn_BW
+        case 0x9CF255534C61746ELLU: // shh_Latn_US
+        case 0xA0F24D4154666E67LLU: // shi_Tfng_MA
+        case 0xA4F253444C61746ELLU: // shj_Latn_SD
+        case 0xA8F253534C61746ELLU: // shk_Latn_SS
+        case 0xB0F2495241726162LLU: // shm_Arab_IR
+        case 0xB4F24D4D4D796D72LLU: // shn_Mymr_MM
+        case 0xB8F24E474C61746ELLU: // sho_Latn_NG
+        case 0xBCF250454C61746ELLU: // shp_Latn_PE
+        case 0xC0F25A4D4C61746ELLU: // shq_Latn_ZM
+        case 0xC4F243444C61746ELLU: // shr_Latn_CD
+        case 0xC8F243414C61746ELLU: // shs_Latn_CA
+        case 0xCCF255534C61746ELLU: // sht_Latn_US
+        case 0xD0F2544441726162LLU: // shu_Arab_TD
+        case 0xD4F24F4D41726162LLU: // shv_Arab_OM
+        case 0xD8F253444C61746ELLU: // shw_Latn_SD
+        case 0xE0F2445A4C61746ELLU: // shy_Latn_DZ
+        case 0xE4F24D4C4C61746ELLU: // shz_Latn_ML
+        case 0x73694C4B53696E68LLU: // si_Sinh_LK
+        case 0x811252554379726CLLU: // sia_Cyrl_RU
+        case 0x85124D594C61746ELLU: // sib_Latn_MY
+        case 0x8D1245544C61746ELLU: // sid_Latn_ET
+        case 0x91125A4D4C61746ELLU: // sie_Latn_ZM
+        case 0x951242464C61746ELLU: // sif_Latn_BF
+        case 0x991247484C61746ELLU: // sig_Latn_GH
+        case 0x9D124E434C61746ELLU: // sih_Latn_NC
+        case 0xA112494E4C61746ELLU: // sii_Latn_IN
+        case 0xA51250474C61746ELLU: // sij_Latn_PG
+        case 0xA91242524C61746ELLU: // sik_Latn_BR
+        case 0xAD1247484C61746ELLU: // sil_Latn_GH
+        case 0xB11250474C61746ELLU: // sim_Latn_PG
+        case 0xBD12494E54696274LLU: // sip_Tibt_IN
+        case 0xC11250474C61746ELLU: // siq_Latn_PG
+        case 0xC5124E474C61746ELLU: // sir_Latn_NG
+        case 0xC91255534C61746ELLU: // sis_Latn_US
+        case 0xD11250474C61746ELLU: // siu_Latn_PG
+        case 0xD51250474C61746ELLU: // siv_Latn_PG
+        case 0xD91250474C61746ELLU: // siw_Latn_PG
+        case 0xDD1250474C61746ELLU: // six_Latn_PG
+        case 0xE112495241726162LLU: // siy_Arab_IR
+        case 0xE512454741726162LLU: // siz_Arab_EG
+        case 0x8132434F4C61746ELLU: // sja_Latn_CO
+        case 0x853249444C61746ELLU: // sjb_Latn_ID
+        case 0x8D3252554379726CLLU: // sjd_Cyrl_RU
+        case 0x913253454C61746ELLU: // sje_Latn_SE
+        case 0x993254444C61746ELLU: // sjg_Latn_TD
+        case 0xAD32494E4C61746ELLU: // sjl_Latn_IN
+        case 0xB13250484C61746ELLU: // sjm_Latn_PH
+        case 0xBD32494E44657661LLU: // sjp_Deva_IN
+        case 0xC53250474C61746ELLU: // sjr_Latn_PG
+        case 0xCD3252554379726CLLU: // sjt_Cyrl_RU
+        case 0xD13253454C61746ELLU: // sju_Latn_SE
+        case 0xD93255534C61746ELLU: // sjw_Latn_US
+        case 0x736B534B4C61746ELLU: // sk_Latn_SK
+        case 0x815255534C61746ELLU: // ska_Latn_US
+        case 0x8552544854686169LLU: // skb_Thai_TH
+        case 0x895250474C61746ELLU: // skc_Latn_PG
+        case 0x8D5255534C61746ELLU: // skd_Latn_US
+        case 0x915256554C61746ELLU: // ske_Latn_VU
+        case 0x955242524C61746ELLU: // skf_Latn_BR
+        case 0x99524D474C61746ELLU: // skg_Latn_MG
+        case 0x9D5249444C61746ELLU: // skh_Latn_ID
+        case 0xA15249444C61746ELLU: // ski_Latn_ID
+        case 0xA5524E5044657661LLU: // skj_Deva_NP
+        case 0xB15250474C61746ELLU: // skm_Latn_PG
+        case 0xB55250484C61746ELLU: // skn_Latn_PH
+        case 0xB95249444C61746ELLU: // sko_Latn_ID
+        case 0xBD524D594C61746ELLU: // skp_Latn_MY
+        case 0xC15242464C61746ELLU: // skq_Latn_BF
+        case 0xC552504B41726162LLU: // skr_Arab_PK
+        case 0xC95250474C61746ELLU: // sks_Latn_PG
+        case 0xCD5243444C61746ELLU: // skt_Latn_CD
+        case 0xD15256554C61746ELLU: // sku_Latn_VU
+        case 0xD55249444C61746ELLU: // skv_Latn_ID
+        case 0xD95247594C61746ELLU: // skw_Latn_GY
+        case 0xDD5249444C61746ELLU: // skx_Latn_ID
+        case 0xE15253424C61746ELLU: // sky_Latn_SB
+        case 0xE55249444C61746ELLU: // skz_Latn_ID
+        case 0x736C53494C61746ELLU: // sl_Latn_SI
+        case 0x8972434F4C61746ELLU: // slc_Latn_CO
+        case 0x8D7242464C61746ELLU: // sld_Latn_BF
+        case 0x997249444C61746ELLU: // slg_Latn_ID
+        case 0x9D7255534C61746ELLU: // slh_Latn_US
+        case 0xA172504C4C61746ELLU: // sli_Latn_PL
+        case 0xA57242524C61746ELLU: // slj_Latn_BR
+        case 0xAD7250474C61746ELLU: // sll_Latn_PG
+        case 0xB17250484C61746ELLU: // slm_Latn_PH
+        case 0xB57255534C61746ELLU: // sln_Latn_US
+        case 0xBD7249444C61746ELLU: // slp_Latn_ID
+        case 0xC572434E4C61746ELLU: // slr_Latn_CN
+        case 0xD17249444C61746ELLU: // slu_Latn_ID
+        case 0xD97250474C61746ELLU: // slw_Latn_PG
+        case 0xDD7243444C61746ELLU: // slx_Latn_CD
+        case 0xE17249444C61746ELLU: // sly_Latn_ID
+        case 0xE57249444C61746ELLU: // slz_Latn_ID
+        case 0x736D57534C61746ELLU: // sm_Latn_WS
+        case 0x819253454C61746ELLU: // sma_Latn_SE
+        case 0x859250474C61746ELLU: // smb_Latn_PG
+        case 0x899250474C61746ELLU: // smc_Latn_PG
+        case 0x959250474C61746ELLU: // smf_Latn_PG
+        case 0x999250474C61746ELLU: // smg_Latn_PG
+        case 0x9D92434E59696969LLU: // smh_Yiii_CN
+        case 0xA59253454C61746ELLU: // smj_Latn_SE
+        case 0xA99250484C61746ELLU: // smk_Latn_PH
+        case 0xAD9250484C61746ELLU: // sml_Latn_PH
+        case 0xB59246494C61746ELLU: // smn_Latn_FI
+        case 0xBD92494C53616D72LLU: // smp_Samr_IL
+        case 0xC19250474C61746ELLU: // smq_Latn_PG
+        case 0xC59249444C61746ELLU: // smr_Latn_ID
+        case 0xC99246494C61746ELLU: // sms_Latn_FI
+        case 0xCD92494E4C61746ELLU: // smt_Latn_IN
+        case 0xD1924B484B686D72LLU: // smu_Khmr_KH
+        case 0xD99249444C61746ELLU: // smw_Latn_ID
+        case 0xDD9243444C61746ELLU: // smx_Latn_CD
+        case 0xE192495241726162LLU: // smy_Arab_IR
+        case 0xE59250474C61746ELLU: // smz_Latn_PG
+        case 0x736E5A574C61746ELLU: // sn_Latn_ZW
+        case 0x89B250474C61746ELLU: // snc_Latn_PG
+        case 0x91B24D594C61746ELLU: // sne_Latn_MY
+        case 0x95B2534E4C61746ELLU: // snf_Latn_SN
+        case 0x99B243444C61746ELLU: // sng_Latn_CD
+        case 0xA1B250454C61746ELLU: // sni_Latn_PE
+        case 0xA5B243464C61746ELLU: // snj_Latn_CF
+        case 0xA9B24D4C4C61746ELLU: // snk_Latn_ML
+        case 0xADB250484C61746ELLU: // snl_Latn_PH
+        case 0xB1B255474C61746ELLU: // snm_Latn_UG
+        case 0xB5B2434F4C61746ELLU: // snn_Latn_CO
+        case 0xB9B255534C61746ELLU: // sno_Latn_US
+        case 0xBDB250474C61746ELLU: // snp_Latn_PG
+        case 0xC1B247414C61746ELLU: // snq_Latn_GA
+        case 0xC5B250474C61746ELLU: // snr_Latn_PG
+        case 0xC9B256554C61746ELLU: // sns_Latn_VU
+        case 0xD1B249444C61746ELLU: // snu_Latn_ID
+        case 0xD5B24D594C61746ELLU: // snv_Latn_MY
+        case 0xD9B247484C61746ELLU: // snw_Latn_GH
+        case 0xDDB250474C61746ELLU: // snx_Latn_PG
+        case 0xE1B250474C61746ELLU: // sny_Latn_PG
+        case 0xE5B250474C61746ELLU: // snz_Latn_PG
+        case 0x736F534F4C61746ELLU: // so_Latn_SO
+        case 0x81D2544854617674LLU: // soa_Tavt_TH
+        case 0x85D249444C61746ELLU: // sob_Latn_ID
+        case 0x89D243444C61746ELLU: // soc_Latn_CD
+        case 0x8DD243444C61746ELLU: // sod_Latn_CD
+        case 0x91D243444C61746ELLU: // soe_Latn_CD
+        case 0x99D2555A536F6764LLU: // sog_Sogd_UZ
+        case 0xA1D24E5044657661LLU: // soi_Deva_NP
+        case 0xA9D254444C61746ELLU: // sok_Latn_TD
+        case 0xADD250474C61746ELLU: // sol_Latn_PG
+        case 0xB9D243444C61746ELLU: // soo_Latn_CD
+        case 0xBDD243444C61746ELLU: // sop_Latn_CD
+        case 0xC1D250474C61746ELLU: // soq_Latn_PG
+        case 0xC5D254444C61746ELLU: // sor_Latn_TD
+        case 0xC9D242464C61746ELLU: // sos_Latn_BF
+        case 0xD1D2544854686169LLU: // sou_Thai_TH
+        case 0xD5D250574C61746ELLU: // sov_Latn_PW
+        case 0xD9D250474C61746ELLU: // sow_Latn_PG
+        case 0xDDD2434D4C61746ELLU: // sox_Latn_CM
+        case 0xE1D2424A4C61746ELLU: // soy_Latn_BJ
+        case 0xE5D2545A4C61746ELLU: // soz_Latn_TZ
+        case 0x85F249444C61746ELLU: // spb_Latn_ID
+        case 0x89F256454C61746ELLU: // spc_Latn_VE
+        case 0x8DF250474C61746ELLU: // spd_Latn_PG
+        case 0x91F250474C61746ELLU: // spe_Latn_PG
+        case 0x99F24D594C61746ELLU: // spg_Latn_MY
+        case 0xA1F249444C61746ELLU: // spi_Latn_ID
+        case 0xA9F250474C61746ELLU: // spk_Latn_PG
+        case 0xADF250474C61746ELLU: // spl_Latn_PG
+        case 0xB1F250474C61746ELLU: // spm_Latn_PG
+        case 0xB5F250594C61746ELLU: // spn_Latn_PY
+        case 0xB9F255534C61746ELLU: // spo_Latn_US
+        case 0xBDF24D4C4C61746ELLU: // spp_Latn_ML
+        case 0xC1F250454C61746ELLU: // spq_Latn_PE
+        case 0xC5F249444C61746ELLU: // spr_Latn_ID
+        case 0xC9F250474C61746ELLU: // sps_Latn_PG
+        case 0xCDF2494E54696274LLU: // spt_Tibt_IN
+        case 0xD5F2494E4F727961LLU: // spv_Orya_IN
+        case 0x7371414C4C61746ELLU: // sq_Latn_AL
+        case 0x82124E474C61746ELLU: // sqa_Latn_NG
+        case 0x9E124E474C61746ELLU: // sqh_Latn_NG
+        case 0xB21243464C61746ELLU: // sqm_Latn_CF
+        case 0xBA12495241726162LLU: // sqo_Arab_IR
+        case 0xC2124C414C616F6FLLU: // sqq_Laoo_LA
+        case 0xCE12594541726162LLU: // sqt_Arab_YE
+        case 0xD21243414C61746ELLU: // squ_Latn_CA
+        case 0x737252534379726CLLU: // sr_Cyrl_RS
+        case 0x823250474C61746ELLU: // sra_Latn_PG
+        case 0x8632494E536F7261LLU: // srb_Sora_IN
+        case 0x923249444C61746ELLU: // sre_Latn_ID
+        case 0x963250474C61746ELLU: // srf_Latn_PG
+        case 0x9A3250484C61746ELLU: // srg_Latn_PH
+        case 0x9E32434E41726162LLU: // srh_Arab_CN
+        case 0xA232434F4C61746ELLU: // sri_Latn_CO
+        case 0xAA324D594C61746ELLU: // srk_Latn_MY
+        case 0xAE3249444C61746ELLU: // srl_Latn_ID
+        case 0xB23253524C61746ELLU: // srm_Latn_SR
+        case 0xB63253524C61746ELLU: // srn_Latn_SR
+        case 0xBA3249544C61746ELLU: // sro_Latn_IT
+        case 0xC232424F4C61746ELLU: // srq_Latn_BO
+        case 0xC632534E4C61746ELLU: // srr_Latn_SN
+        case 0xCA3243414C61746ELLU: // srs_Latn_CA
+        case 0xCE3249444C61746ELLU: // srt_Latn_ID
+        case 0xD23242524C61746ELLU: // sru_Latn_BR
+        case 0xD63250484C61746ELLU: // srv_Latn_PH
+        case 0xDA3249444C61746ELLU: // srw_Latn_ID
+        case 0xDE32494E44657661LLU: // srx_Deva_IN
+        case 0xE23250474C61746ELLU: // sry_Latn_PG
+        case 0xE632495241726162LLU: // srz_Arab_IR
+        case 0x73735A414C61746ELLU: // ss_Latn_ZA
+        case 0x865250484C61746ELLU: // ssb_Latn_PH
+        case 0x8A52545A4C61746ELLU: // ssc_Latn_TZ
+        case 0x8E5250474C61746ELLU: // ssd_Latn_PG
+        case 0x925250484C61746ELLU: // sse_Latn_PH
+        case 0x965254574C61746ELLU: // ssf_Latn_TW
+        case 0x9A5250474C61746ELLU: // ssg_Latn_PG
+        case 0x9E52414541726162LLU: // ssh_Arab_AE
+        case 0xA65250474C61746ELLU: // ssj_Latn_PG
+        case 0xAE5247484C61746ELLU: // ssl_Latn_GH
+        case 0xB2524D594C61746ELLU: // ssm_Latn_MY
+        case 0xB6524B454C61746ELLU: // ssn_Latn_KE
+        case 0xBA5250474C61746ELLU: // sso_Latn_PG
+        case 0xC25249444C61746ELLU: // ssq_Latn_ID
+        case 0xCA524C414C616F6FLLU: // sss_Laoo_LA
+        case 0xCE5250474C61746ELLU: // sst_Latn_PG
+        case 0xD25250474C61746ELLU: // ssu_Latn_PG
+        case 0xD65256554C61746ELLU: // ssv_Latn_VU
+        case 0xDE5250474C61746ELLU: // ssx_Latn_PG
+        case 0xE25245524C61746ELLU: // ssy_Latn_ER
+        case 0xE65250474C61746ELLU: // ssz_Latn_PG
+        case 0x73745A414C61746ELLU: // st_Latn_ZA
+        case 0x82725A4D4C61746ELLU: // sta_Latn_ZM
+        case 0x867250484C61746ELLU: // stb_Latn_PH
+        case 0x927249444C61746ELLU: // ste_Latn_ID
+        case 0x967250474C61746ELLU: // stf_Latn_PG
+        case 0x9A72564E4C61746ELLU: // stg_Latn_VN
+        case 0x9E7249454C61746ELLU: // sth_Latn_IE
+        case 0xA272564E4C61746ELLU: // sti_Latn_VN
+        case 0xA67242464C61746ELLU: // stj_Latn_BF
+        case 0xAA7250474C61746ELLU: // stk_Latn_PG
+        case 0xAE724E4C4C61746ELLU: // stl_Latn_NL
+        case 0xB27250474C61746ELLU: // stm_Latn_PG
+        case 0xB67253424C61746ELLU: // stn_Latn_SB
+        case 0xBA7243414C61746ELLU: // sto_Latn_CA
+        case 0xBE724D584C61746ELLU: // stp_Latn_MX
+        case 0xC27244454C61746ELLU: // stq_Latn_DE
+        case 0xC67243414C61746ELLU: // str_Latn_CA
+        case 0xCA72414641726162LLU: // sts_Arab_AF
+        case 0xCE72564E4C61746ELLU: // stt_Latn_VN
+        case 0xD672455445746869LLU: // stv_Ethi_ET
+        case 0xDA72464D4C61746ELLU: // stw_Latn_FM
+        case 0xE27252554379726CLLU: // sty_Cyrl_RU
+        case 0x737549444C61746ELLU: // su_Latn_ID
+        case 0x829250474C61746ELLU: // sua_Latn_PG
+        case 0x869243444C61746ELLU: // sub_Latn_CD
+        case 0x8A9250484C61746ELLU: // suc_Latn_PH
+        case 0x929250474C61746ELLU: // sue_Latn_PG
+        case 0x9A9250474C61746ELLU: // sug_Latn_PG
+        case 0xA29250474C61746ELLU: // sui_Latn_PG
+        case 0xA692545A4C61746ELLU: // suj_Latn_TZ
+        case 0xAA92545A4C61746ELLU: // suk_Latn_TZ
+        case 0xBA9250474C61746ELLU: // suo_Latn_PG
+        case 0xC29245544C61746ELLU: // suq_Latn_ET
+        case 0xC6924E474C61746ELLU: // sur_Latn_NG
+        case 0xCA92474E4C61746ELLU: // sus_Latn_GN
+        case 0xCE924E494C61746ELLU: // sut_Latn_NI
+        case 0xD692494E4C61746ELLU: // suv_Latn_IN
+        case 0xDA92545A4C61746ELLU: // suw_Latn_TZ
+        case 0xE29242524C61746ELLU: // suy_Latn_BR
+        case 0xE6924E5053756E75LLU: // suz_Sunu_NP
+        case 0x737653454C61746ELLU: // sv_Latn_SE
+        case 0x82B2474547656F72LLU: // sva_Geor_GE
+        case 0x86B250474C61746ELLU: // svb_Latn_PG
+        case 0x8AB256434C61746ELLU: // svc_Latn_VC
+        case 0x92B249444C61746ELLU: // sve_Latn_ID
+        case 0xB2B249544C61746ELLU: // svm_Latn_IT
+        case 0xCAB253424C61746ELLU: // svs_Latn_SB
+        case 0x7377545A4C61746ELLU: // sw_Latn_TZ
+        case 0x86D2595441726162LLU: // swb_Arab_YT
+        case 0x96D243444C61746ELLU: // swf_Latn_CD
+        case 0x9AD244454C61746ELLU: // swg_Latn_DE
+        case 0xA2D2434E48616E69LLU: // swi_Hani_CN
+        case 0xA6D247414C61746ELLU: // swj_Latn_GA
+        case 0xAAD24D574C61746ELLU: // swk_Latn_MW
+        case 0xB2D250474C61746ELLU: // swm_Latn_PG
+        case 0xBAD242524C61746ELLU: // swo_Latn_BR
+        case 0xBED250474C61746ELLU: // swp_Latn_PG
+        case 0xC2D2434D4C61746ELLU: // swq_Latn_CM
+        case 0xC6D249444C61746ELLU: // swr_Latn_ID
+        case 0xCAD249444C61746ELLU: // sws_Latn_ID
+        case 0xCED249444C61746ELLU: // swt_Latn_ID
+        case 0xD2D249444C61746ELLU: // swu_Latn_ID
+        case 0xD6D2494E44657661LLU: // swv_Deva_IN
+        case 0xDAD256554C61746ELLU: // sww_Latn_VU
+        case 0xDED242524C61746ELLU: // swx_Latn_BR
+        case 0xE2D254444C61746ELLU: // swy_Latn_TD
+        case 0x86F24B454C61746ELLU: // sxb_Latn_KE
+        case 0x92F247414C61746ELLU: // sxe_Latn_GA
+        case 0xB6F249444C61746ELLU: // sxn_Latn_ID
+        case 0xC6F254574C61746ELLU: // sxr_Latn_TW
+        case 0xCAF24E474C61746ELLU: // sxs_Latn_NG
+        case 0xD2F2444552756E72LLU: // sxu_Runr_DE
+        case 0xDAF2424A4C61746ELLU: // sxw_Latn_BJ
+        case 0x831249444C61746ELLU: // sya_Latn_ID
+        case 0x871250484C61746ELLU: // syb_Latn_PH
+        case 0x8B12545253797263LLU: // syc_Syrc_TR
+        case 0xA31247414C61746ELLU: // syi_Latn_GA
+        case 0xAB124E474C61746ELLU: // syk_Latn_NG
+        case 0xAF12424442656E67LLU: // syl_Beng_BD
+        case 0xB31242464C61746ELLU: // sym_Latn_BF
+        case 0xB712495253797263LLU: // syn_Syrc_IR
+        case 0xBB124B484C61746ELLU: // syo_Latn_KH
+        case 0xC712495153797263LLU: // syr_Syrc_IQ
+        case 0xCB1254444C61746ELLU: // sys_Latn_TD
+        case 0xDB124E5044657661LLU: // syw_Deva_NP
+        case 0xDF1247414C61746ELLU: // syx_Latn_GA
+        case 0x83324D594C61746ELLU: // sza_Latn_MY
+        case 0x873249444C61746ELLU: // szb_Latn_ID
+        case 0x8B324D594C61746ELLU: // szc_Latn_MY
+        case 0x9B3243444C61746ELLU: // szg_Latn_CD
+        case 0xAF32504C4C61746ELLU: // szl_Latn_PL
+        case 0xB73249444C61746ELLU: // szn_Latn_ID
+        case 0xBF3249444C61746ELLU: // szp_Latn_ID
+        case 0xD732434D4C61746ELLU: // szv_Latn_CM
+        case 0xDB3249444C61746ELLU: // szw_Latn_ID
+        case 0xE33254574C61746ELLU: // szy_Latn_TW
+        case 0x7461494E54616D6CLLU: // ta_Taml_IN
+        case 0x801355534C61746ELLU: // taa_Latn_US
+        case 0x841352554379726CLLU: // tab_Cyrl_RU
+        case 0x88134D584C61746ELLU: // tac_Latn_MX
+        case 0x8C1349444C61746ELLU: // tad_Latn_ID
+        case 0x901342524C61746ELLU: // tae_Latn_BR
+        case 0x941342524C61746ELLU: // taf_Latn_BR
+        case 0x981353444C61746ELLU: // tag_Latn_SD
+        case 0xA4134E5044657661LLU: // taj_Deva_NP
+        case 0xA8134E474C61746ELLU: // tak_Latn_NG
+        case 0xAC134E474C61746ELLU: // tal_Latn_NG
+        case 0xB4134E474C61746ELLU: // tan_Latn_NG
+        case 0xB81354574C61746ELLU: // tao_Latn_TW
+        case 0xBC1343444C61746ELLU: // tap_Latn_CD
+        case 0xC0134D4C4C61746ELLU: // taq_Latn_ML
+        case 0xC4134D584C61746ELLU: // tar_Latn_MX
+        case 0xC813564E4C61746ELLU: // tas_Latn_VN
+        case 0xD01355534C61746ELLU: // tau_Latn_US
+        case 0xD413434F4C61746ELLU: // tav_Latn_CO
+        case 0xD81350474C61746ELLU: // taw_Latn_PG
+        case 0xDC1354444C61746ELLU: // tax_Latn_TD
+        case 0xE01354574C61746ELLU: // tay_Latn_TW
+        case 0xE41353444C61746ELLU: // taz_Latn_SD
+        case 0x803342524C61746ELLU: // tba_Latn_BR
+        case 0x883350474C61746ELLU: // tbc_Latn_PG
+        case 0x8C3350474C61746ELLU: // tbd_Latn_PG
+        case 0x903353424C61746ELLU: // tbe_Latn_SB
+        case 0x943350474C61746ELLU: // tbf_Latn_PG
+        case 0x983350474C61746ELLU: // tbg_Latn_PG
+        case 0x9C3341554C61746ELLU: // tbh_Latn_AU
+        case 0xA03353444C61746ELLU: // tbi_Latn_SD
+        case 0xA43350474C61746ELLU: // tbj_Latn_PG
+        case 0xA833504854616762LLU: // tbk_Tagb_PH
+        case 0xAC3350484C61746ELLU: // tbl_Latn_PH
+        case 0xB03343444C61746ELLU: // tbm_Latn_CD
+        case 0xB433434F4C61746ELLU: // tbn_Latn_CO
+        case 0xB83350474C61746ELLU: // tbo_Latn_PG
+        case 0xBC3349444C61746ELLU: // tbp_Latn_ID
+        case 0xC83350474C61746ELLU: // tbs_Latn_PG
+        case 0xCC3343444C61746ELLU: // tbt_Latn_CD
+        case 0xD0334D584C61746ELLU: // tbu_Latn_MX
+        case 0xD43350474C61746ELLU: // tbv_Latn_PG
+        case 0xD83350484C61746ELLU: // tbw_Latn_PH
+        case 0xDC3350474C61746ELLU: // tbx_Latn_PG
+        case 0xE03349444C61746ELLU: // tby_Latn_ID
+        case 0xE433424A4C61746ELLU: // tbz_Latn_BJ
+        case 0x805342524C61746ELLU: // tca_Latn_BR
+        case 0x845355534C61746ELLU: // tcb_Latn_US
+        case 0x8853545A4C61746ELLU: // tcc_Latn_TZ
+        case 0x8C5347484C61746ELLU: // tcd_Latn_GH
+        case 0x905343414C61746ELLU: // tce_Latn_CA
+        case 0x94534D584C61746ELLU: // tcf_Latn_MX
+        case 0x985349444C61746ELLU: // tcg_Latn_ID
+        case 0x9C5354434C61746ELLU: // tch_Latn_TC
+        case 0xA05350474C61746ELLU: // tci_Latn_PG
+        case 0xA85347414C61746ELLU: // tck_Latn_GA
+        case 0xB05349444C61746ELLU: // tcm_Latn_ID
+        case 0xB4534E5054696274LLU: // tcn_Tibt_NP
+        case 0xB8534D4D4D796D72LLU: // tco_Mymr_MM
+        case 0xBC534D4D4C61746ELLU: // tcp_Latn_MM
+        case 0xC05349444C61746ELLU: // tcq_Latn_ID
+        case 0xC85341554C61746ELLU: // tcs_Latn_AU
+        case 0xD0534D584C61746ELLU: // tcu_Latn_MX
+        case 0xD8534D584C61746ELLU: // tcw_Latn_MX
+        case 0xDC53494E54616D6CLLU: // tcx_Taml_IN
+        case 0xE053494E4B6E6461LLU: // tcy_Knda_IN
+        case 0xE453494E4C61746ELLU: // tcz_Latn_IN
+        case 0x80734E4554666E67LLU: // tda_Tfng_NE
+        case 0x8473494E44657661LLU: // tdb_Deva_IN
+        case 0x8873434F4C61746ELLU: // tdc_Latn_CO
+        case 0x8C73434E54616C65LLU: // tdd_Tale_CN
+        case 0x90734D4C4C61746ELLU: // tde_Latn_ML
+        case 0x98734E5044657661LLU: // tdg_Deva_NP
+        case 0x9C734E5044657661LLU: // tdh_Deva_NP
+        case 0xA07349444C61746ELLU: // tdi_Latn_ID
+        case 0xA47349444C61746ELLU: // tdj_Latn_ID
+        case 0xA8734E474C61746ELLU: // tdk_Latn_NG
+        case 0xAC734E474C61746ELLU: // tdl_Latn_NG
+        case 0xB07347594C61746ELLU: // tdm_Latn_GY
+        case 0xB47349444C61746ELLU: // tdn_Latn_ID
+        case 0xB8734E474C61746ELLU: // tdo_Latn_NG
+        case 0xC0734E474C61746ELLU: // tdq_Latn_NG
+        case 0xC473564E4C61746ELLU: // tdr_Latn_VN
+        case 0xC87349444C61746ELLU: // tds_Latn_ID
+        case 0xCC73544C4C61746ELLU: // tdt_Latn_TL
+        case 0xD4734E474C61746ELLU: // tdv_Latn_NG
+        case 0xDC734D474C61746ELLU: // tdx_Latn_MG
+        case 0xE07350484C61746ELLU: // tdy_Latn_PH
+        case 0x7465494E54656C75LLU: // te_Telu_IN
+        case 0x80934D594C61746ELLU: // tea_Latn_MY
+        case 0x849345434C61746ELLU: // teb_Latn_EC
+        case 0x88934B454C61746ELLU: // tec_Latn_KE
+        case 0x8C9343494C61746ELLU: // ted_Latn_CI
+        case 0x90934D584C61746ELLU: // tee_Latn_MX
+        case 0x989347414C61746ELLU: // teg_Latn_GA
+        case 0x9C9341524C61746ELLU: // teh_Latn_AR
+        case 0xA09350474C61746ELLU: // tei_Latn_PG
+        case 0xA89343444C61746ELLU: // tek_Latn_CD
+        case 0xB093534C4C61746ELLU: // tem_Latn_SL
+        case 0xB493434F4C61746ELLU: // ten_Latn_CO
+        case 0xB89355474C61746ELLU: // teo_Latn_UG
+        case 0xBC934D584C61746ELLU: // tep_Latn_MX
+        case 0xC09353444C61746ELLU: // teq_Latn_SD
+        case 0xC49342524C61746ELLU: // ter_Latn_BR
+        case 0xC89349444A617661LLU: // tes_Java_ID
+        case 0xCC93544C4C61746ELLU: // tet_Latn_TL
+        case 0xD09355474C61746ELLU: // teu_Latn_UG
+        case 0xD49349444C61746ELLU: // tev_Latn_ID
+        case 0xD89355534C61746ELLU: // tew_Latn_US
+        case 0xDC9353534C61746ELLU: // tex_Latn_SS
+        case 0xE09353444C61746ELLU: // tey_Latn_SD
+        case 0xE4934E454C61746ELLU: // tez_Latn_NE
+        case 0xA0B3424A4C61746ELLU: // tfi_Latn_BJ
+        case 0xB4B355534C61746ELLU: // tfn_Latn_US
+        case 0xB8B349444C61746ELLU: // tfo_Latn_ID
+        case 0xC4B350414C61746ELLU: // tfr_Latn_PA
+        case 0xCCB349444C61746ELLU: // tft_Latn_ID
+        case 0x7467504B41726162LLU: // tg_Arab_PK
+        case 0x7467544A4379726CLLU: // tg_Cyrl_TJ
+        case 0x80D34B454C61746ELLU: // tga_Latn_KE
+        case 0x84D34D594C61746ELLU: // tgb_Latn_MY
+        case 0x88D350474C61746ELLU: // tgc_Latn_PG
+        case 0x8CD34E474C61746ELLU: // tgd_Latn_NG
+        case 0x90D34E5044657661LLU: // tge_Deva_NP
+        case 0x94D3425454696274LLU: // tgf_Tibt_BT
+        case 0x9CD354544C61746ELLU: // tgh_Latn_TT
+        case 0xA0D350474C61746ELLU: // tgi_Latn_PG
+        case 0xA4D3494E4C61746ELLU: // tgj_Latn_IN
+        case 0xB4D350484C61746ELLU: // tgn_Latn_PH
+        case 0xB8D350474C61746ELLU: // tgo_Latn_PG
+        case 0xBCD356554C61746ELLU: // tgp_Latn_VU
+        case 0xC0D34D594C61746ELLU: // tgq_Latn_MY
+        case 0xC8D356554C61746ELLU: // tgs_Latn_VU
+        case 0xCCD350484C61746ELLU: // tgt_Latn_PH
+        case 0xD0D350474C61746ELLU: // tgu_Latn_PG
+        case 0xD4D342524C61746ELLU: // tgv_Latn_BR
+        case 0xD8D343494C61746ELLU: // tgw_Latn_CI
+        case 0xDCD343414C61746ELLU: // tgx_Latn_CA
+        case 0xE0D353534C61746ELLU: // tgy_Latn_SS
+        case 0xE4D341554C61746ELLU: // tgz_Latn_AU
+        case 0x7468544854686169LLU: // th_Thai_TH
+        case 0x8CF341554C61746ELLU: // thd_Latn_AU
+        case 0x90F34E5044657661LLU: // the_Deva_NP
+        case 0x94F34E5044657661LLU: // thf_Deva_NP
+        case 0x9CF34D584C61746ELLU: // thh_Latn_MX
+        case 0xA0F34C4154616C65LLU: // thi_Tale_LA
+        case 0xA8F34B454C61746ELLU: // thk_Latn_KE
+        case 0xACF34E5044657661LLU: // thl_Deva_NP
+        case 0xB0F3544854686169LLU: // thm_Thai_TH
+        case 0xBCF343414C61746ELLU: // thp_Latn_CA
+        case 0xC0F34E5044657661LLU: // thq_Deva_NP
+        case 0xC4F34E5044657661LLU: // thr_Deva_NP
+        case 0xC8F34E5044657661LLU: // ths_Deva_NP
+        case 0xCCF343414C61746ELLU: // tht_Latn_CA
+        case 0xD0F353534C61746ELLU: // thu_Latn_SS
+        case 0xD4F3445A4C61746ELLU: // thv_Latn_DZ
+        case 0xE0F34E474C61746ELLU: // thy_Latn_NG
+        case 0xE4F34E454C61746ELLU: // thz_Latn_NE
+        case 0x7469455445746869LLU: // ti_Ethi_ET
+        case 0x891353444C61746ELLU: // tic_Latn_SD
+        case 0x951350474C61746ELLU: // tif_Latn_PG
+        case 0x9913455245746869LLU: // tig_Ethi_ER
+        case 0x9D134D594C61746ELLU: // tih_Latn_MY
+        case 0xA11343444C61746ELLU: // tii_Latn_CD
+        case 0xA5134E5044657661LLU: // tij_Deva_NP
+        case 0xA913434D4C61746ELLU: // tik_Latn_CM
+        case 0xAD1355534C61746ELLU: // til_Latn_US
+        case 0xB11350474C61746ELLU: // tim_Latn_PG
+        case 0xB51352554379726CLLU: // tin_Cyrl_RU
+        case 0xB91350474C61746ELLU: // tio_Latn_PG
+        case 0xBD1349444C61746ELLU: // tip_Latn_ID
+        case 0xC11342464C61746ELLU: // tiq_Latn_BF
+        case 0xC91350484C61746ELLU: // tis_Latn_PH
+        case 0xCD13434F4C61746ELLU: // tit_Latn_CO
+        case 0xD11350484C61746ELLU: // tiu_Latn_PH
+        case 0xD5134E474C61746ELLU: // tiv_Latn_NG
+        case 0xD91341554C61746ELLU: // tiw_Latn_AU
+        case 0xDD1355534C61746ELLU: // tix_Latn_US
+        case 0xE11350484C61746ELLU: // tiy_Latn_PH
+        case 0x81334C524C61746ELLU: // tja_Latn_LR
+        case 0x993349444C61746ELLU: // tjg_Latn_ID
+        case 0xA133434E4C61746ELLU: // tji_Latn_CN
+        case 0xA53341554C61746ELLU: // tjj_Latn_AU
+        case 0xAD334D4D4D796D72LLU: // tjl_Mymr_MM
+        case 0xB53343494C61746ELLU: // tjn_Latn_CI
+        case 0xB933445A41726162LLU: // tjo_Arab_DZ
+        case 0xBD3341554C61746ELLU: // tjp_Latn_AU
+        case 0xC933434E4C61746ELLU: // tjs_Latn_CN
+        case 0xD13341554C61746ELLU: // tju_Latn_AU
+        case 0xD93341554C61746ELLU: // tjw_Latn_AU
+        case 0x746B544D4C61746ELLU: // tk_Latn_TM
+        case 0x815342524C61746ELLU: // tka_Latn_BR
+        case 0x8553494E44657661LLU: // tkb_Deva_IN
+        case 0x8D53544C4C61746ELLU: // tkd_Latn_TL
+        case 0x91534D5A4C61746ELLU: // tke_Latn_MZ
+        case 0x955342524C61746ELLU: // tkf_Latn_BR
+        case 0x99534D474C61746ELLU: // tkg_Latn_MG
+        case 0xAD53544B4C61746ELLU: // tkl_Latn_TK
+        case 0xBD5353424C61746ELLU: // tkp_Latn_SB
+        case 0xC1534E474C61746ELLU: // tkq_Latn_NG
+        case 0xC553415A4C61746ELLU: // tkr_Latn_AZ
+        case 0xC953495241726162LLU: // tks_Arab_IR
+        case 0xCD534E5044657661LLU: // tkt_Deva_NP
+        case 0xD1534D584C61746ELLU: // tku_Latn_MX
+        case 0xD55350474C61746ELLU: // tkv_Latn_PG
+        case 0xD95353424C61746ELLU: // tkw_Latn_SB
+        case 0xDD5349444C61746ELLU: // tkx_Latn_ID
+        case 0xE553564E4C61746ELLU: // tkz_Latn_VN
+        case 0x746C50484C61746ELLU: // tl_Latn_PH
+        case 0x81734D584C61746ELLU: // tla_Latn_MX
+        case 0x857349444C61746ELLU: // tlb_Latn_ID
+        case 0x89734D584C61746ELLU: // tlc_Latn_MX
+        case 0x8D7349444C61746ELLU: // tld_Latn_ID
+        case 0x957350474C61746ELLU: // tlf_Latn_PG
+        case 0x997349444C61746ELLU: // tlg_Latn_ID
+        case 0xA17355534C61746ELLU: // tli_Latn_US
+        case 0xA57355474C61746ELLU: // tlj_Latn_UG
+        case 0xA97349444C61746ELLU: // tlk_Latn_ID
+        case 0xAD7343444C61746ELLU: // tll_Latn_CD
+        case 0xB17356554C61746ELLU: // tlm_Latn_VU
+        case 0xB57349444C61746ELLU: // tln_Latn_ID
+        case 0xBD734D584C61746ELLU: // tlp_Latn_MX
+        case 0xC1734D4D4C61746ELLU: // tlq_Latn_MM
+        case 0xC57353424C61746ELLU: // tlr_Latn_SB
+        case 0xC97356554C61746ELLU: // tls_Latn_VU
+        case 0xCD7349444C61746ELLU: // tlt_Latn_ID
+        case 0xD17349444C61746ELLU: // tlu_Latn_ID
+        case 0xD57349444C61746ELLU: // tlv_Latn_ID
+        case 0xDD7350474C61746ELLU: // tlx_Latn_PG
+        case 0xE173415A4C61746ELLU: // tly_Latn_AZ
+        case 0x819354444C61746ELLU: // tma_Latn_TD
+        case 0x859356554C61746ELLU: // tmb_Latn_VU
+        case 0x899354444C61746ELLU: // tmc_Latn_TD
+        case 0x8D9350474C61746ELLU: // tmd_Latn_PG
+        case 0x919342524C61746ELLU: // tme_Latn_BR
+        case 0x959350594C61746ELLU: // tmf_Latn_PY
+        case 0x999349444C61746ELLU: // tmg_Latn_ID
+        case 0x9D934E454C61746ELLU: // tmh_Latn_NE
+        case 0xA19356554C61746ELLU: // tmi_Latn_VU
+        case 0xA59349444C61746ELLU: // tmj_Latn_ID
+        case 0xAD9349444C61746ELLU: // tml_Latn_ID
+        case 0xB193564E4C61746ELLU: // tmm_Latn_VN
+        case 0xB59349444C61746ELLU: // tmn_Latn_ID
+        case 0xB9934D594C61746ELLU: // tmo_Latn_MY
+        case 0xC19350474C61746ELLU: // tmq_Latn_PG
+        case 0xC593494C53797263LLU: // tmr_Syrc_IL
+        case 0xCD9356554C61746ELLU: // tmt_Latn_VU
+        case 0xD19349444C61746ELLU: // tmu_Latn_ID
+        case 0xD59343444C61746ELLU: // tmv_Latn_CD
+        case 0xD9934D594C61746ELLU: // tmw_Latn_MY
+        case 0xE19350474C61746ELLU: // tmy_Latn_PG
+        case 0xE59356454C61746ELLU: // tmz_Latn_VE
+        case 0x746E5A414C61746ELLU: // tn_Latn_ZA
+        case 0x81B3424F4C61746ELLU: // tna_Latn_BO
+        case 0x85B3434F4C61746ELLU: // tnb_Latn_CO
+        case 0x89B3434F4C61746ELLU: // tnc_Latn_CO
+        case 0x8DB3434F4C61746ELLU: // tnd_Latn_CO
+        case 0x99B354444C61746ELLU: // tng_Latn_TD
+        case 0x9DB350474C61746ELLU: // tnh_Latn_PG
+        case 0xA1B349444C61746ELLU: // tni_Latn_ID
+        case 0xA9B356554C61746ELLU: // tnk_Latn_VU
+        case 0xADB356554C61746ELLU: // tnl_Latn_VU
+        case 0xB1B349444C61746ELLU: // tnm_Latn_ID
+        case 0xB5B356554C61746ELLU: // tnn_Latn_VU
+        case 0xB9B3424F4C61746ELLU: // tno_Latn_BO
+        case 0xBDB356554C61746ELLU: // tnp_Latn_VU
+        case 0xC1B350524C61746ELLU: // tnq_Latn_PR
+        case 0xC5B3534E4C61746ELLU: // tnr_Latn_SN
+        case 0xC9B350474C61746ELLU: // tns_Latn_PG
+        case 0xCDB349444C61746ELLU: // tnt_Latn_ID
+        case 0xD5B3424443616B6DLLU: // tnv_Cakm_BD
+        case 0xD9B349444C61746ELLU: // tnw_Latn_ID
+        case 0xDDB353424C61746ELLU: // tnx_Latn_SB
+        case 0xE1B3545A4C61746ELLU: // tny_Latn_TZ
+        case 0x746F544F4C61746ELLU: // to_Latn_TO
+        case 0x85D341524C61746ELLU: // tob_Latn_AR
+        case 0x89D34D584C61746ELLU: // toc_Latn_MX
+        case 0x8DD3474E4C61746ELLU: // tod_Latn_GN
+        case 0x95D350474C61746ELLU: // tof_Latn_PG
+        case 0x99D34D574C61746ELLU: // tog_Latn_MW
+        case 0x9DD34D5A4C61746ELLU: // toh_Latn_MZ
+        case 0xA1D35A4D4C61746ELLU: // toi_Latn_ZM
+        case 0xA5D34D584C61746ELLU: // toj_Latn_MX
+        case 0xADD355534C61746ELLU: // tol_Latn_US
+        case 0xB1D349444C61746ELLU: // tom_Latn_ID
+        case 0xB9D34D584C61746ELLU: // too_Latn_MX
+        case 0xBDD34D584C61746ELLU: // top_Latn_MX
+        case 0xC1D353534C61746ELLU: // toq_Latn_SS
+        case 0xC5D343444C61746ELLU: // tor_Latn_CD
+        case 0xC9D34D584C61746ELLU: // tos_Latn_MX
+        case 0xD1D3564E4C61746ELLU: // tou_Latn_VN
+        case 0xD5D3495241726162LLU: // tov_Arab_IR
+        case 0xD9D355534C61746ELLU: // tow_Latn_US
+        case 0xDDD350574C61746ELLU: // tox_Latn_PW
+        case 0xE1D349444C61746ELLU: // toy_Latn_ID
+        case 0xE5D3434D4C61746ELLU: // toz_Latn_CM
+        case 0x81F350474C61746ELLU: // tpa_Latn_PG
+        case 0x89F34D584C61746ELLU: // tpc_Latn_MX
+        case 0x91F342444C61746ELLU: // tpe_Latn_BD
+        case 0x95F349444C61746ELLU: // tpf_Latn_ID
+        case 0x99F349444C61746ELLU: // tpg_Latn_ID
+        case 0xA1F350474C61746ELLU: // tpi_Latn_PG
+        case 0xA5F350594C61746ELLU: // tpj_Latn_PY
+        case 0xA9F342524C61746ELLU: // tpk_Latn_BR
+        case 0xADF34D584C61746ELLU: // tpl_Latn_MX
+        case 0xB1F347484C61746ELLU: // tpm_Latn_GH
+        case 0xB5F342524C61746ELLU: // tpn_Latn_BR
+        case 0xBDF34D584C61746ELLU: // tpp_Latn_MX
+        case 0xC5F342524C61746ELLU: // tpr_Latn_BR
+        case 0xCDF34D584C61746ELLU: // tpt_Latn_MX
+        case 0xD1F34B484B686D72LLU: // tpu_Khmr_KH
+        case 0xD5F34D504C61746ELLU: // tpv_Latn_MP
+        case 0xDDF34D584C61746ELLU: // tpx_Latn_MX
+        case 0xE1F342524C61746ELLU: // tpy_Latn_BR
+        case 0xE5F350474C61746ELLU: // tpz_Latn_PG
+        case 0x861342524C61746ELLU: // tqb_Latn_BR
+        case 0xAE1356554C61746ELLU: // tql_Latn_VU
+        case 0xB21350474C61746ELLU: // tqm_Latn_PG
+        case 0xB61355534C61746ELLU: // tqn_Latn_US
+        case 0xBA1350474C61746ELLU: // tqo_Latn_PG
+        case 0xBE1350474C61746ELLU: // tqp_Latn_PG
+        case 0xCE134D584C61746ELLU: // tqt_Latn_MX
+        case 0xD21353424C61746ELLU: // tqu_Latn_SB
+        case 0xDA1355534C61746ELLU: // tqw_Latn_US
+        case 0x747254524C61746ELLU: // tr_Latn_TR
+        case 0x8233414641726162LLU: // tra_Arab_AF
+        case 0x863350474C61746ELLU: // trb_Latn_PG
+        case 0x8A334D584C61746ELLU: // trc_Latn_MX
+        case 0x923349444C61746ELLU: // tre_Latn_ID
+        case 0x963354544C61746ELLU: // trf_Latn_TT
+        case 0x9A33494C48656272LLU: // trg_Hebr_IL
+        case 0x9E3350474C61746ELLU: // trh_Latn_PG
+        case 0xA23353524C61746ELLU: // tri_Latn_SR
+        case 0xA63354444C61746ELLU: // trj_Latn_TD
+        case 0xAE3347424C61746ELLU: // trl_Latn_GB
+        case 0xB233414641726162LLU: // trm_Arab_AF
+        case 0xB633424F4C61746ELLU: // trn_Latn_BO
+        case 0xBA33494E4C61746ELLU: // tro_Latn_IN
+        case 0xBE33494E4C61746ELLU: // trp_Latn_IN
+        case 0xC2334D584C61746ELLU: // trq_Latn_MX
+        case 0xC63350454C61746ELLU: // trr_Latn_PE
+        case 0xCA334D584C61746ELLU: // trs_Latn_MX
+        case 0xCE3349444C61746ELLU: // trt_Latn_ID
+        case 0xD23354524C61746ELLU: // tru_Latn_TR
+        case 0xD63354574C61746ELLU: // trv_Latn_TW
+        case 0xDA33504B41726162LLU: // trw_Arab_PK
+        case 0xDE334D594C61746ELLU: // trx_Latn_MY
+        case 0xE233494E4C61746ELLU: // try_Latn_IN
+        case 0xE63342524C61746ELLU: // trz_Latn_BR
+        case 0x74735A414C61746ELLU: // ts_Latn_ZA
+        case 0x825343474C61746ELLU: // tsa_Latn_CG
+        case 0x865345544C61746ELLU: // tsb_Latn_ET
+        case 0x8A534D5A4C61746ELLU: // tsc_Latn_MZ
+        case 0x8E5347524772656BLLU: // tsd_Grek_GR
+        case 0x9A5350484C61746ELLU: // tsg_Latn_PH
+        case 0x9E53434D4C61746ELLU: // tsh_Latn_CM
+        case 0xA25343414C61746ELLU: // tsi_Latn_CA
+        case 0xA653425454696274LLU: // tsj_Tibt_BT
+        case 0xAE53564E4C61746ELLU: // tsl_Latn_VN
+        case 0xBE5342464C61746ELLU: // tsp_Latn_BF
+        case 0xC65356554C61746ELLU: // tsr_Latn_VU
+        case 0xCE534D4C4C61746ELLU: // tst_Latn_ML
+        case 0xD25354574C61746ELLU: // tsu_Latn_TW
+        case 0xD65347414C61746ELLU: // tsv_Latn_GA
+        case 0xDA534E474C61746ELLU: // tsw_Latn_NG
+        case 0xDE5350474C61746ELLU: // tsx_Latn_PG
+        case 0xE6534D584C61746ELLU: // tsz_Latn_MX
+        case 0x747452554379726CLLU: // tt_Cyrl_RU
+        case 0x86734E474C61746ELLU: // ttb_Latn_NG
+        case 0x8A7347544C61746ELLU: // ttc_Latn_GT
+        case 0x8E7350474C61746ELLU: // ttd_Latn_PG
+        case 0x927350474C61746ELLU: // tte_Latn_PG
+        case 0x9673434D4C61746ELLU: // ttf_Latn_CM
+        case 0x9E734C414C616F6FLLU: // tth_Laoo_LA
+        case 0xA27349444C61746ELLU: // tti_Latn_ID
+        case 0xA67355474C61746ELLU: // ttj_Latn_UG
+        case 0xAA73434F4C61746ELLU: // ttk_Latn_CO
+        case 0xAE735A4D4C61746ELLU: // ttl_Latn_ZM
+        case 0xB27343414C61746ELLU: // ttm_Latn_CA
+        case 0xB67349444C61746ELLU: // ttn_Latn_ID
+        case 0xBA734C414C616F6FLLU: // tto_Laoo_LA
+        case 0xBE7349444C61746ELLU: // ttp_Latn_ID
+        case 0xC6734E474C61746ELLU: // ttr_Latn_NG
+        case 0xCA73544854686169LLU: // tts_Thai_TH
+        case 0xCE73415A4C61746ELLU: // ttt_Latn_AZ
+        case 0xD27350474C61746ELLU: // ttu_Latn_PG
+        case 0xD67350474C61746ELLU: // ttv_Latn_PG
+        case 0xDA734D594C61746ELLU: // ttw_Latn_MY
+        case 0xE27349444C61746ELLU: // tty_Latn_ID
+        case 0xE6734E5044657661LLU: // ttz_Deva_NP
+        case 0x829350474C61746ELLU: // tua_Latn_PG
+        case 0x869355534C61746ELLU: // tub_Latn_US
+        case 0x8A9350474C61746ELLU: // tuc_Latn_PG
+        case 0x8E9342524C61746ELLU: // tud_Latn_BR
+        case 0x9293434F4C61746ELLU: // tue_Latn_CO
+        case 0x9693434F4C61746ELLU: // tuf_Latn_CO
+        case 0x9A9354444C61746ELLU: // tug_Latn_TD
+        case 0x9E9350474C61746ELLU: // tuh_Latn_PG
+        case 0xA293434D4C61746ELLU: // tui_Latn_CM
+        case 0xA69349444C61746ELLU: // tuj_Latn_ID
+        case 0xAE934E474C61746ELLU: // tul_Latn_NG
+        case 0xB2934D574C61746ELLU: // tum_Latn_MW
+        case 0xB69355534C61746ELLU: // tun_Latn_US
+        case 0xBA9342524C61746ELLU: // tuo_Latn_BR
+        case 0xC29354444C61746ELLU: // tuq_Latn_TD
+        case 0xCA9343414C61746ELLU: // tus_Latn_CA
+        case 0xD29355534C61746ELLU: // tuu_Latn_US
+        case 0xD6934B454C61746ELLU: // tuv_Latn_KE
+        case 0xDE9342524C61746ELLU: // tux_Latn_BR
+        case 0xE2934B454C61746ELLU: // tuy_Latn_KE
+        case 0xE69342464C61746ELLU: // tuz_Latn_BF
+        case 0x82B353424C61746ELLU: // tva_Latn_SB
+        case 0x8EB34E474C61746ELLU: // tvd_Latn_NG
+        case 0x92B349444C61746ELLU: // tve_Latn_ID
+        case 0xA2B34E474C61746ELLU: // tvi_Latn_NG
+        case 0xAAB356554C61746ELLU: // tvk_Latn_VU
+        case 0xAEB354564C61746ELLU: // tvl_Latn_TV
+        case 0xB2B349444C61746ELLU: // tvm_Latn_ID
+        case 0xB6B34D4D4D796D72LLU: // tvn_Mymr_MM
+        case 0xBAB349444C61746ELLU: // tvo_Latn_ID
+        case 0xCAB34B454C61746ELLU: // tvs_Latn_KE
+        case 0xCEB3494E4C61746ELLU: // tvt_Latn_IN
+        case 0xD2B3434D4C61746ELLU: // tvu_Latn_CM
+        case 0xDAB349444C61746ELLU: // tvw_Latn_ID
+        case 0xDEB354574C61746ELLU: // tvx_Latn_TW
+        case 0x82D355534C61746ELLU: // twa_Latn_US
+        case 0x86D350484C61746ELLU: // twb_Latn_PH
+        case 0x8ED34E4C4C61746ELLU: // twd_Latn_NL
+        case 0x92D349444C61746ELLU: // twe_Latn_ID
+        case 0x96D355534C61746ELLU: // twf_Latn_US
+        case 0x9AD349444C61746ELLU: // twg_Latn_ID
+        case 0x9ED3564E4C61746ELLU: // twh_Latn_VN
+        case 0xAED34D5A4C61746ELLU: // twl_Latn_MZ
+        case 0xB2D3494E44657661LLU: // twm_Deva_IN
+        case 0xB6D3434D4C61746ELLU: // twn_Latn_CM
+        case 0xBAD342574C61746ELLU: // two_Latn_BW
+        case 0xBED350474C61746ELLU: // twp_Latn_PG
+        case 0xC2D34E454C61746ELLU: // twq_Latn_NE
+        case 0xC6D34D584C61746ELLU: // twr_Latn_MX
+        case 0xCED342524C61746ELLU: // twt_Latn_BR
+        case 0xD2D349444C61746ELLU: // twu_Latn_ID
+        case 0xDAD350474C61746ELLU: // tww_Latn_PG
+        case 0xDED34D5A4C61746ELLU: // twx_Latn_MZ
+        case 0xE2D349444C61746ELLU: // twy_Latn_ID
+        case 0x82F34D594C61746ELLU: // txa_Latn_MY
+        case 0x92F349444C61746ELLU: // txe_Latn_ID
+        case 0x9AF3434E54616E67LLU: // txg_Tang_CN
+        case 0xA2F342524C61746ELLU: // txi_Latn_BR
+        case 0xA6F34E474C61746ELLU: // txj_Latn_NG
+        case 0xB2F349444C61746ELLU: // txm_Latn_ID
+        case 0xB6F349444C61746ELLU: // txn_Latn_ID
+        case 0xBAF3494E546F746FLLU: // txo_Toto_IN
+        case 0xC2F349444C61746ELLU: // txq_Latn_ID
+        case 0xCAF349444C61746ELLU: // txs_Latn_ID
+        case 0xCEF349444C61746ELLU: // txt_Latn_ID
+        case 0xD2F342524C61746ELLU: // txu_Latn_BR
+        case 0xDEF34D594C61746ELLU: // txx_Latn_MY
+        case 0xE2F34D474C61746ELLU: // txy_Latn_MG
+        case 0x747950464C61746ELLU: // ty_Latn_PF
+        case 0x831350474C61746ELLU: // tya_Latn_PG
+        case 0x93134E474C61746ELLU: // tye_Latn_NG
+        case 0x9F13564E4C61746ELLU: // tyh_Latn_VN
+        case 0xA31343474C61746ELLU: // tyi_Latn_CG
+        case 0xA713564E4C61746ELLU: // tyj_Latn_VN
+        case 0xAF13564E4C61746ELLU: // tyl_Latn_VN
+        case 0xB71349444C61746ELLU: // tyn_Latn_ID
+        case 0xBF1341554C61746ELLU: // typ_Latn_AU
+        case 0xC713564E54617674LLU: // tyr_Tavt_VN
+        case 0xCB13564E4C61746ELLU: // tys_Latn_VN
+        case 0xCF13564E4C61746ELLU: // tyt_Latn_VN
+        case 0xD31342574C61746ELLU: // tyu_Latn_BW
+        case 0xD71352554379726CLLU: // tyv_Cyrl_RU
+        case 0xDF1343474C61746ELLU: // tyx_Latn_CG
+        case 0xE3134E474C61746ELLU: // tyy_Latn_NG
+        case 0xE713564E4C61746ELLU: // tyz_Latn_VN
+        case 0x9F334D584C61746ELLU: // tzh_Latn_MX
+        case 0xA73347544C61746ELLU: // tzj_Latn_GT
+        case 0xB3334D414C61746ELLU: // tzm_Latn_MA
+        case 0xB73349444C61746ELLU: // tzn_Latn_ID
+        case 0xBB334D584C61746ELLU: // tzo_Latn_MX
+        case 0xDF3350474C61746ELLU: // tzx_Latn_PG
+        case 0xB01442524C61746ELLU: // uam_Latn_BR
+        case 0xC41450474C61746ELLU: // uar_Latn_PG
+        case 0x80344E474C61746ELLU: // uba_Latn_NG
+        case 0xA03454444C61746ELLU: // ubi_Latn_TD
+        case 0xAC3450484C61746ELLU: // ubl_Latn_PH
+        case 0xC43450474C61746ELLU: // ubr_Latn_PG
+        case 0xD03450474C61746ELLU: // ubu_Latn_PG
+        case 0xE03454524C61746ELLU: // uby_Latn_TR
+        case 0x80744E474C61746ELLU: // uda_Latn_NG
+        case 0x907452554379726CLLU: // ude_Cyrl_RU
+        case 0x9874494E4D6C796DLLU: // udg_Mlym_IN
+        case 0xA07452554379726CLLU: // udi_Cyrl_RU
+        case 0xA47449444C61746ELLU: // udj_Latn_ID
+        case 0xAC74434D4C61746ELLU: // udl_Latn_CM
+        case 0xB07452554379726CLLU: // udm_Cyrl_RU
+        case 0xD07453444C61746ELLU: // udu_Latn_SD
+        case 0xC89449444C61746ELLU: // ues_Latn_ID
+        case 0xA0B450474C61746ELLU: // ufi_Latn_PG
+        case 0x7567434E41726162LLU: // ug_Arab_CN
+        case 0x75674B5A4379726CLLU: // ug_Cyrl_KZ
+        case 0x80D4535955676172LLU: // uga_Ugar_SY
+        case 0x84D441554C61746ELLU: // ugb_Latn_AU
+        case 0x90D453424C61746ELLU: // uge_Latn_SB
+        case 0x9CD452554379726CLLU: // ugh_Cyrl_RU
+        case 0xB8D4544854686169LLU: // ugo_Thai_TH
+        case 0x80F44E474C61746ELLU: // uha_Latn_NG
+        case 0xB4F449444C61746ELLU: // uhn_Latn_ID
+        case 0xC91450474C61746ELLU: // uis_Latn_PG
+        case 0xD514434D4C61746ELLU: // uiv_Latn_CM
+        case 0xA1344E474C61746ELLU: // uji_Latn_NG
+        case 0x756B55414379726CLLU: // uk_Cyrl_UA
+        case 0x815449444C61746ELLU: // uka_Latn_ID
+        case 0x995450474C61746ELLU: // ukg_Latn_PG
+        case 0x9D5443464C61746ELLU: // ukh_Latn_CF
+        case 0xA154494E4F727961LLU: // uki_Orya_IN
+        case 0xA9544D4D4C61746ELLU: // ukk_Latn_MM
+        case 0xBD544E474C61746ELLU: // ukp_Latn_NG
+        case 0xC1544E474C61746ELLU: // ukq_Latn_NG
+        case 0xD1544E474C61746ELLU: // uku_Latn_NG
+        case 0xD55453534C61746ELLU: // ukv_Latn_SS
+        case 0xD9544E474C61746ELLU: // ukw_Latn_NG
+        case 0xE15441554C61746ELLU: // uky_Latn_AU
+        case 0x81744E474C61746ELLU: // ula_Latn_NG
+        case 0x85744E474C61746ELLU: // ulb_Latn_NG
+        case 0x897452554379726CLLU: // ulc_Cyrl_RU
+        case 0x917441524C61746ELLU: // ule_Latn_AR
+        case 0x957449444C61746ELLU: // ulf_Latn_ID
+        case 0xA174464D4C61746ELLU: // uli_Latn_FM
+        case 0xA97441554C61746ELLU: // ulk_Latn_AU
+        case 0xB17449444C61746ELLU: // ulm_Latn_ID
+        case 0xB57450474C61746ELLU: // uln_Latn_PG
+        case 0xD17449444C61746ELLU: // ulu_Latn_ID
+        case 0xD9744E494C61746ELLU: // ulw_Latn_NI
+        case 0xE1744E474C61746ELLU: // uly_Latn_NG
+        case 0x819455534C61746ELLU: // uma_Latn_US
+        case 0x8594414F4C61746ELLU: // umb_Latn_AO
+        case 0x8D9441554C61746ELLU: // umd_Latn_AU
+        case 0x999441554C61746ELLU: // umg_Latn_AU
+        case 0xA1944D594C61746ELLU: // umi_Latn_MY
+        case 0xB1944E474C61746ELLU: // umm_Latn_NG
+        case 0xB5944D4D4C61746ELLU: // umn_Latn_MM
+        case 0xB99442524C61746ELLU: // umo_Latn_BR
+        case 0xBD9441554C61746ELLU: // ump_Latn_AU
+        case 0xC59441554C61746ELLU: // umr_Latn_AU
+        case 0xC99449444C61746ELLU: // ums_Latn_ID
+        case 0x81B450474C61746ELLU: // una_Latn_PG
+        case 0x91B44E474C61746ELLU: // une_Latn_NG
+        case 0x99B441554C61746ELLU: // ung_Latn_AU
+        case 0xA1B450474C61746ELLU: // uni_Latn_PG
+        case 0xA9B442524C61746ELLU: // unk_Latn_BR
+        case 0xB1B455534C61746ELLU: // unm_Latn_US
+        case 0xB5B441554C61746ELLU: // unn_Latn_AU
+        case 0xC5B4494E42656E67LLU: // unr_Beng_IN
+        case 0xC5B44E5044657661LLU: // unr_Deva_NP
+        case 0xD1B450474C61746ELLU: // unu_Latn_PG
+        case 0xDDB4494E42656E67LLU: // unx_Beng_IN
+        case 0xE5B449444C61746ELLU: // unz_Latn_ID
+        case 0xB5D454574C61746ELLU: // uon_Latn_TW
+        case 0xA1F450474C61746ELLU: // upi_Latn_PG
+        case 0xD5F456554C61746ELLU: // upv_Latn_VU
+        case 0x7572504B41726162LLU: // ur_Arab_PK
+        case 0x823450454C61746ELLU: // ura_Latn_PE
+        case 0x863442524C61746ELLU: // urb_Latn_BR
+        case 0x8A3441554C61746ELLU: // urc_Latn_AU
+        case 0x9234424F4C61746ELLU: // ure_Latn_BO
+        case 0x963441554C61746ELLU: // urf_Latn_AU
+        case 0x9A3450474C61746ELLU: // urg_Latn_PG
+        case 0x9E344E474C61746ELLU: // urh_Latn_NG
+        case 0xA23450474C61746ELLU: // uri_Latn_PG
+        case 0xAA34544854686169LLU: // urk_Thai_TH
+        case 0xB23450474C61746ELLU: // urm_Latn_PG
+        case 0xB63449444C61746ELLU: // urn_Latn_ID
+        case 0xBA3450474C61746ELLU: // uro_Latn_PG
+        case 0xBE3442524C61746ELLU: // urp_Latn_BR
+        case 0xC63456554C61746ELLU: // urr_Latn_VU
+        case 0xCE3450474C61746ELLU: // urt_Latn_PG
+        case 0xD23442524C61746ELLU: // uru_Latn_BR
+        case 0xD63450474C61746ELLU: // urv_Latn_PG
+        case 0xDA3450474C61746ELLU: // urw_Latn_PG
+        case 0xDE3450474C61746ELLU: // urx_Latn_PG
+        case 0xE23449444C61746ELLU: // ury_Latn_ID
+        case 0xE63442524C61746ELLU: // urz_Latn_BR
+        case 0x825450474C61746ELLU: // usa_Latn_PG
+        case 0x9E54504B41726162LLU: // ush_Arab_PK
+        case 0xA25442444C61746ELLU: // usi_Latn_BD
+        case 0xAA54434D4C61746ELLU: // usk_Latn_CM
+        case 0xBE5447544C61746ELLU: // usp_Latn_GT
+        case 0xCA544E474C61746ELLU: // uss_Latn_NG
+        case 0xD25450474C61746ELLU: // usu_Latn_PG
+        case 0x82744E474C61746ELLU: // uta_Latn_NG
+        case 0x927455534C61746ELLU: // ute_Latn_US
+        case 0x9E744E474C61746ELLU: // uth_Latn_NG
+        case 0xBE7453424C61746ELLU: // utp_Latn_SB
+        case 0xC6744E474C61746ELLU: // utr_Latn_NG
+        case 0xD27450474C61746ELLU: // utu_Latn_PG
+        case 0xB29447454772656BLLU: // uum_Grek_GE
+        case 0xC69456554C61746ELLU: // uur_Latn_VU
+        case 0x92B44E434C61746ELLU: // uve_Latn_NC
+        case 0x9EB450474C61746ELLU: // uvh_Latn_PG
+        case 0xAEB450474C61746ELLU: // uvl_Latn_PG
+        case 0x82D441554C61746ELLU: // uwa_Latn_AU
+        case 0x83144E474C61746ELLU: // uya_Latn_NG
+        case 0x757A414641726162LLU: // uz_Arab_AF
+        case 0x757A555A4C61746ELLU: // uz_Latn_UZ
+        case 0xCB34414641726162LLU: // uzs_Arab_AF
+        case 0x8015494E54616D6CLLU: // vaa_Taml_IN
+        case 0x901543464C61746ELLU: // vae_Latn_CF
+        case 0x9415495241726162LLU: // vaf_Arab_IR
+        case 0x981547484C61746ELLU: // vag_Latn_GH
+        case 0x9C15494E44657661LLU: // vah_Deva_IN
+        case 0xA0154C5256616969LLU: // vai_Vaii_LR
+        case 0xA4154E414C61746ELLU: // vaj_Latn_NA
+        case 0xAC1550474C61746ELLU: // val_Latn_PG
+        case 0xB01550474C61746ELLU: // vam_Latn_PG
+        case 0xB41550474C61746ELLU: // van_Latn_PG
+        case 0xB81556554C61746ELLU: // vao_Latn_VU
+        case 0xBC15494E4C61746ELLU: // vap_Latn_IN
+        case 0xC4154D584C61746ELLU: // var_Latn_MX
+        case 0xC815494E44657661LLU: // vas_Deva_IN
+        case 0xD01543444C61746ELLU: // vau_Latn_CD
+        case 0xD415494E44657661LLU: // vav_Deva_IN
+        case 0xE0154E5044657661LLU: // vay_Deva_NP
+        case 0x843549444C61746ELLU: // vbb_Latn_ID
+        case 0xA83550484C61746ELLU: // vbk_Latn_PH
+        case 0x76655A414C61746ELLU: // ve_Latn_ZA
+        case 0x889549544C61746ELLU: // vec_Latn_IT
+        case 0xB0954E474C61746ELLU: // vem_Latn_NG
+        case 0xB89555534C61746ELLU: // veo_Latn_US
+        case 0xBC9552554C61746ELLU: // vep_Latn_RU
+        case 0xC4954E474C61746ELLU: // ver_Latn_NG
+        case 0xC4D5504B41726162LLU: // vgr_Arab_PK
+        case 0x7669564E4C61746ELLU: // vi_Latn_VN
+        case 0x891553584C61746ELLU: // vic_Latn_SX
+        case 0x8D15545A4C61746ELLU: // vid_Latn_TZ
+        case 0x951543474C61746ELLU: // vif_Latn_CG
+        case 0x991542464C61746ELLU: // vig_Latn_BF
+        case 0xAD1541524C61746ELLU: // vil_Latn_AR
+        case 0xB515545A4C61746ELLU: // vin_Latn_TZ
+        case 0xCD154E474C61746ELLU: // vit_Latn_NG
+        case 0xD51550474C61746ELLU: // viv_Latn_PG
+        case 0xA935494E44657661LLU: // vjk_Deva_IN
+        case 0x815541554C61746ELLU: // vka_Latn_AU
+        case 0xA55554444C61746ELLU: // vkj_Latn_TD
+        case 0xA95549444C61746ELLU: // vkk_Latn_ID
+        case 0xAD5549444C61746ELLU: // vkl_Latn_ID
+        case 0xB15542524C61746ELLU: // vkm_Latn_BR
+        case 0xB5554E474C61746ELLU: // vkn_Latn_NG
+        case 0xB95549444C61746ELLU: // vko_Latn_ID
+        case 0xBD55494E4C61746ELLU: // vkp_Latn_IN
+        case 0xCD5549444C61746ELLU: // vkt_Latn_ID
+        case 0xD15541554C61746ELLU: // vku_Latn_AU
+        case 0xE5554E474C61746ELLU: // vkz_Latn_NG
+        case 0xBD7556554C61746ELLU: // vlp_Latn_VU
+        case 0xC97542454C61746ELLU: // vls_Latn_BE
+        case 0x819541554C61746ELLU: // vma_Latn_AU
+        case 0x859541554C61746ELLU: // vmb_Latn_AU
+        case 0x89954D584C61746ELLU: // vmc_Latn_MX
+        case 0x8D95494E4B6E6461LLU: // vmd_Knda_IN
+        case 0x919549444C61746ELLU: // vme_Latn_ID
+        case 0x959544454C61746ELLU: // vmf_Latn_DE
+        case 0x999550474C61746ELLU: // vmg_Latn_PG
+        case 0x9D95495241726162LLU: // vmh_Arab_IR
+        case 0xA19541554C61746ELLU: // vmi_Latn_AU
+        case 0xA5954D584C61746ELLU: // vmj_Latn_MX
+        case 0xA9954D5A4C61746ELLU: // vmk_Latn_MZ
+        case 0xAD9541554C61746ELLU: // vml_Latn_AU
+        case 0xB1954D584C61746ELLU: // vmm_Latn_MX
+        case 0xBD954D584C61746ELLU: // vmp_Latn_MX
+        case 0xC1954D584C61746ELLU: // vmq_Latn_MX
+        case 0xC5954D5A4C61746ELLU: // vmr_Latn_MZ
+        case 0xC99549444C61746ELLU: // vms_Latn_ID
+        case 0xD19541554C61746ELLU: // vmu_Latn_AU
+        case 0xD9954D5A4C61746ELLU: // vmw_Latn_MZ
+        case 0xDD954D584C61746ELLU: // vmx_Latn_MX
+        case 0xE1954D584C61746ELLU: // vmy_Latn_MX
+        case 0xE5954D584C61746ELLU: // vmz_Latn_MX
+        case 0xA9B553424C61746ELLU: // vnk_Latn_SB
+        case 0xB1B556554C61746ELLU: // vnm_Latn_VU
+        case 0xBDB556554C61746ELLU: // vnp_Latn_VU
+        case 0xC5D54E474C61746ELLU: // vor_Latn_NG
+        case 0xCDD552554C61746ELLU: // vot_Latn_RU
+        case 0x823556554C61746ELLU: // vra_Latn_VU
+        case 0xBA3545454C61746ELLU: // vro_Latn_EE
+        case 0xCA3553424C61746ELLU: // vrs_Latn_SB
+        case 0xCE3556554C61746ELLU: // vrt_Latn_VU
+        case 0xBA7549444C61746ELLU: // vto_Latn_ID
+        case 0xB29547414C61746ELLU: // vum_Latn_GA
+        case 0xB695545A4C61746ELLU: // vun_Latn_TZ
+        case 0xCE95434D4C61746ELLU: // vut_Latn_CM
+        case 0x82D5434E4C61746ELLU: // vwa_Latn_CN
+        case 0x776142454C61746ELLU: // wa_Latn_BE
+        case 0x801655534C61746ELLU: // waa_Latn_US
+        case 0x841650474C61746ELLU: // wab_Latn_PG
+        case 0x881655534C61746ELLU: // wac_Latn_US
+        case 0x8C1649444C61746ELLU: // wad_Latn_ID
+        case 0x901643484C61746ELLU: // wae_Latn_CH
+        case 0x941642524C61746ELLU: // waf_Latn_BR
+        case 0x981650474C61746ELLU: // wag_Latn_PG
+        case 0x9C1649444C61746ELLU: // wah_Latn_ID
+        case 0xA01649444C61746ELLU: // wai_Latn_ID
+        case 0xA41650474C61746ELLU: // waj_Latn_PG
+        case 0xAC16455445746869LLU: // wal_Ethi_ET
+        case 0xB01655534C61746ELLU: // wam_Latn_US
+        case 0xB41643494C61746ELLU: // wan_Latn_CI
+        case 0xBC1647594C61746ELLU: // wap_Latn_GY
+        case 0xC01641554C61746ELLU: // waq_Latn_AU
+        case 0xC41650484C61746ELLU: // war_Latn_PH
+        case 0xC81655534C61746ELLU: // was_Latn_US
+        case 0xCC1650474C61746ELLU: // wat_Latn_PG
+        case 0xD01642524C61746ELLU: // wau_Latn_BR
+        case 0xD4164E474C61746ELLU: // wav_Latn_NG
+        case 0xD81642524C61746ELLU: // waw_Latn_BR
+        case 0xDC1650474C61746ELLU: // wax_Latn_PG
+        case 0xE01653524C61746ELLU: // way_Latn_SR
+        case 0xE41650474C61746ELLU: // waz_Latn_PG
+        case 0x803656454C61746ELLU: // wba_Latn_VE
+        case 0x843649444C61746ELLU: // wbb_Latn_ID
+        case 0x903649444C61746ELLU: // wbe_Latn_ID
+        case 0x943642464C61746ELLU: // wbf_Latn_BF
+        case 0x9C36545A4C61746ELLU: // wbh_Latn_TZ
+        case 0xA036545A4C61746ELLU: // wbi_Latn_TZ
+        case 0xA436545A4C61746ELLU: // wbj_Latn_TZ
+        case 0xA836414641726162LLU: // wbk_Arab_AF
+        case 0xAC36504B4C61746ELLU: // wbl_Latn_PK
+        case 0xB036434E4C61746ELLU: // wbm_Latn_CN
+        case 0xBC3641554C61746ELLU: // wbp_Latn_AU
+        case 0xC036494E54656C75LLU: // wbq_Telu_IN
+        case 0xC436494E44657661LLU: // wbr_Deva_IN
+        case 0xCC3641554C61746ELLU: // wbt_Latn_AU
+        case 0xD43641554C61746ELLU: // wbv_Latn_AU
+        case 0xD83649444C61746ELLU: // wbw_Latn_ID
+        case 0x805642524C61746ELLU: // wca_Latn_BR
+        case 0xA05654474C61746ELLU: // wci_Latn_TG
+        case 0x8C7647414C61746ELLU: // wdd_Latn_GA
+        case 0x987650474C61746ELLU: // wdg_Latn_PG
+        case 0xA47641554C61746ELLU: // wdj_Latn_AU
+        case 0xA87641554C61746ELLU: // wdk_Latn_AU
+        case 0xCC7643414C61746ELLU: // wdt_Latn_CA
+        case 0xD07641554C61746ELLU: // wdu_Latn_AU
+        case 0xE07641554C61746ELLU: // wdy_Latn_AU
+        case 0x889643494C61746ELLU: // wec_Latn_CI
+        case 0x8C9650474C61746ELLU: // wed_Latn_PG
+        case 0x989641554C61746ELLU: // weg_Latn_AU
+        case 0x9C96434D4C61746ELLU: // weh_Latn_CM
+        case 0xA09650474C61746ELLU: // wei_Latn_PG
+        case 0xB096424A4C61746ELLU: // wem_Latn_BJ
+        case 0xB89649444C61746ELLU: // weo_Latn_ID
+        case 0xBC9644454C61746ELLU: // wep_Latn_DE
+        case 0xC49650474C61746ELLU: // wer_Latn_PG
+        case 0xC896434D4C61746ELLU: // wes_Latn_CM
+        case 0xCC9649444C61746ELLU: // wet_Latn_ID
+        case 0xD0964D4D4C61746ELLU: // weu_Latn_MM
+        case 0xD89649444C61746ELLU: // wew_Latn_ID
+        case 0x98B649444C61746ELLU: // wfg_Latn_ID
+        case 0x80D641554C61746ELLU: // wga_Latn_AU
+        case 0x84D650474C61746ELLU: // wgb_Latn_PG
+        case 0x98D641554C61746ELLU: // wgg_Latn_AU
+        case 0xA0D650474C61746ELLU: // wgi_Latn_PG
+        case 0xB8D649444C61746ELLU: // wgo_Latn_ID
+        case 0xD0D641554C61746ELLU: // wgu_Latn_AU
+        case 0xE0D641554C61746ELLU: // wgy_Latn_AU
+        case 0x80F649444C61746ELLU: // wha_Latn_ID
+        case 0x98F650474C61746ELLU: // whg_Latn_PG
+        case 0xA8F649444C61746ELLU: // whk_Latn_ID
+        case 0xD0F649444C61746ELLU: // whu_Latn_ID
+        case 0x851642464C61746ELLU: // wib_Latn_BF
+        case 0x891655534C61746ELLU: // wic_Latn_US
+        case 0x911641554C61746ELLU: // wie_Latn_AU
+        case 0x951641554C61746ELLU: // wif_Latn_AU
+        case 0x991641554C61746ELLU: // wig_Latn_AU
+        case 0x9D1641554C61746ELLU: // wih_Latn_AU
+        case 0xA11650474C61746ELLU: // wii_Latn_PG
+        case 0xA51641554C61746ELLU: // wij_Latn_AU
+        case 0xA91641554C61746ELLU: // wik_Latn_AU
+        case 0xAD1641554C61746ELLU: // wil_Latn_AU
+        case 0xB11641554C61746ELLU: // wim_Latn_AU
+        case 0xB51655534C61746ELLU: // win_Latn_US
+        case 0xC51642524C61746ELLU: // wir_Latn_BR
+        case 0xD11650474C61746ELLU: // wiu_Latn_PG
+        case 0xD51650474C61746ELLU: // wiv_Latn_PG
+        case 0xE11655534C61746ELLU: // wiy_Latn_US
+        case 0x81364E474C61746ELLU: // wja_Latn_NG
+        case 0xA1364E474C61746ELLU: // wji_Latn_NG
+        case 0x8156545A4C61746ELLU: // wka_Latn_TZ
+        case 0x8D5649444C61746ELLU: // wkd_Latn_ID
+        case 0xC55641554C61746ELLU: // wkr_Latn_AU
+        case 0xD95641554C61746ELLU: // wkw_Latn_AU
+        case 0xE15641554C61746ELLU: // wky_Latn_AU
+        case 0x817650474C61746ELLU: // wla_Latn_PG
+        case 0x9176455445746869LLU: // wle_Ethi_ET
+        case 0x997641554C61746ELLU: // wlg_Latn_AU
+        case 0x9D76544C4C61746ELLU: // wlh_Latn_TL
+        case 0xA17649444C61746ELLU: // wli_Latn_ID
+        case 0xB17647424C61746ELLU: // wlm_Latn_GB
+        case 0xB976494441726162LLU: // wlo_Arab_ID
+        case 0xC57656554C61746ELLU: // wlr_Latn_VU
+        case 0xC97657464C61746ELLU: // wls_Latn_WF
+        case 0xD17641554C61746ELLU: // wlu_Latn_AU
+        case 0xD57641524C61746ELLU: // wlv_Latn_AR
+        case 0xD97649444C61746ELLU: // wlw_Latn_ID
+        case 0xDD7647484C61746ELLU: // wlx_Latn_GH
+        case 0x81964E474C61746ELLU: // wma_Latn_NG
+        case 0x859641554C61746ELLU: // wmb_Latn_AU
+        case 0x899650474C61746ELLU: // wmc_Latn_PG
+        case 0x8D9642524C61746ELLU: // wmd_Latn_BR
+        case 0x91964E5044657661LLU: // wme_Deva_NP
+        case 0x9D96544C4C61746ELLU: // wmh_Latn_TL
+        case 0xA19641554C61746ELLU: // wmi_Latn_AU
+        case 0xB19649444C61746ELLU: // wmm_Latn_ID
+        case 0xB5964E434C61746ELLU: // wmn_Latn_NC
+        case 0xB99650474C61746ELLU: // wmo_Latn_PG
+        case 0xC99649444C61746ELLU: // wms_Latn_ID
+        case 0xCD9641554C61746ELLU: // wmt_Latn_AU
+        case 0xD9964D5A4C61746ELLU: // wmw_Latn_MZ
+        case 0xDD9650474C61746ELLU: // wmx_Latn_PG
+        case 0x85B650474C61746ELLU: // wnb_Latn_PG
+        case 0x89B650474C61746ELLU: // wnc_Latn_PG
+        case 0x8DB641554C61746ELLU: // wnd_Latn_AU
+        case 0x91B6504B41726162LLU: // wne_Arab_PK
+        case 0x99B649444C61746ELLU: // wng_Latn_ID
+        case 0xA1B64B4D41726162LLU: // wni_Arab_KM
+        case 0xA9B649444C61746ELLU: // wnk_Latn_ID
+        case 0xB1B641554C61746ELLU: // wnm_Latn_AU
+        case 0xB5B641554C61746ELLU: // wnn_Latn_AU
+        case 0xB9B649444C61746ELLU: // wno_Latn_ID
+        case 0xBDB650474C61746ELLU: // wnp_Latn_PG
+        case 0xD1B650474C61746ELLU: // wnu_Latn_PG
+        case 0xD9B655534C61746ELLU: // wnw_Latn_US
+        case 0xE1B641554C61746ELLU: // wny_Latn_AU
+        case 0x776F534E4C61746ELLU: // wo_Latn_SN
+        case 0x81D641554C61746ELLU: // woa_Latn_AU
+        case 0x85D643494C61746ELLU: // wob_Latn_CI
+        case 0x89D650474C61746ELLU: // woc_Latn_PG
+        case 0x8DD649444C61746ELLU: // wod_Latn_ID
+        case 0x91D6464D4C61746ELLU: // woe_Latn_FM
+        case 0x95D6474D4C61746ELLU: // wof_Latn_GM
+        case 0x99D650474C61746ELLU: // wog_Latn_PG
+        case 0xA1D649444C61746ELLU: // woi_Latn_ID
+        case 0xA9D6434D4C61746ELLU: // wok_Latn_CM
+        case 0xB1D64E474C61746ELLU: // wom_Latn_NG
+        case 0xB5D643444C61746ELLU: // won_Latn_CD
+        case 0xB9D649444C61746ELLU: // woo_Latn_ID
+        case 0xC5D649444C61746ELLU: // wor_Latn_ID
+        case 0xC9D650474C61746ELLU: // wos_Latn_PG
+        case 0xD9D649444C61746ELLU: // wow_Latn_ID
+        case 0x89F656454C61746ELLU: // wpc_Latn_VE
+        case 0x863641554C61746ELLU: // wrb_Latn_AU
+        case 0x9A3641554C61746ELLU: // wrg_Latn_AU
+        case 0x9E3641554C61746ELLU: // wrh_Latn_AU
+        case 0xA23641554C61746ELLU: // wri_Latn_AU
+        case 0xAA3641554C61746ELLU: // wrk_Latn_AU
+        case 0xAE3641554C61746ELLU: // wrl_Latn_AU
+        case 0xB23641554C61746ELLU: // wrm_Latn_AU
+        case 0xBA3641554C61746ELLU: // wro_Latn_AU
+        case 0xBE3649444C61746ELLU: // wrp_Latn_ID
+        case 0xC63641554C61746ELLU: // wrr_Latn_AU
+        case 0xCA3650474C61746ELLU: // wrs_Latn_PG
+        case 0xD23649444C61746ELLU: // wru_Latn_ID
+        case 0xD63650474C61746ELLU: // wrv_Latn_PG
+        case 0xDA3641554C61746ELLU: // wrw_Latn_AU
+        case 0xDE3649444C61746ELLU: // wrx_Latn_ID
+        case 0xE63641554C61746ELLU: // wrz_Latn_AU
+        case 0x825649444C61746ELLU: // wsa_Latn_ID
+        case 0x9A56494E476F6E67LLU: // wsg_Gong_IN
+        case 0xA25656554C61746ELLU: // wsi_Latn_VU
+        case 0xAA5650474C61746ELLU: // wsk_Latn_PG
+        case 0xC65650474C61746ELLU: // wsr_Latn_PG
+        case 0xCA5647484C61746ELLU: // wss_Latn_GH
+        case 0xD25642524C61746ELLU: // wsu_Latn_BR
+        case 0xD656414641726162LLU: // wsv_Arab_AF
+        case 0x8676545A4C61746ELLU: // wtb_Latn_TZ
+        case 0x967650474C61746ELLU: // wtf_Latn_PG
+        case 0x9E7641554C61746ELLU: // wth_Latn_AU
+        case 0xA27645544C61746ELLU: // wti_Latn_ET
+        case 0xAA7650474C61746ELLU: // wtk_Latn_PG
+        case 0xB276494E44657661LLU: // wtm_Deva_IN
+        case 0xDA7649444C61746ELLU: // wtw_Latn_ID
+        case 0x829641554C61746ELLU: // wua_Latn_AU
+        case 0x869641554C61746ELLU: // wub_Latn_AU
+        case 0x8E9654474C61746ELLU: // wud_Latn_TG
+        case 0xAE9649444C61746ELLU: // wul_Latn_ID
+        case 0xB29647414C61746ELLU: // wum_Latn_GA
+        case 0xB696545A4C61746ELLU: // wun_Latn_TZ
+        case 0xC69641554C61746ELLU: // wur_Latn_AU
+        case 0xCE9650474C61746ELLU: // wut_Latn_PG
+        case 0xD296434E48616E73LLU: // wuu_Hans_CN
+        case 0xD69650474C61746ELLU: // wuv_Latn_PG
+        case 0xDE9641554C61746ELLU: // wux_Latn_AU
+        case 0xE29649444C61746ELLU: // wuy_Latn_ID
+        case 0x82D6424A4C61746ELLU: // wwa_Latn_BJ
+        case 0x86D641554C61746ELLU: // wwb_Latn_AU
+        case 0xBAD656554C61746ELLU: // wwo_Latn_VU
+        case 0xC6D641554C61746ELLU: // wwr_Latn_AU
+        case 0xDAD6434D4C61746ELLU: // www_Latn_CM
+        case 0xDAF641554C61746ELLU: // wxw_Latn_AU
+        case 0x871641554C61746ELLU: // wyb_Latn_AU
+        case 0xA31641554C61746ELLU: // wyi_Latn_AU
+        case 0xB316504C4C61746ELLU: // wym_Latn_PL
+        case 0xB71655534C61746ELLU: // wyn_Latn_US
+        case 0xC71642524C61746ELLU: // wyr_Latn_BR
+        case 0xE316464A4C61746ELLU: // wyy_Latn_FJ
+        case 0x801745534C61746ELLU: // xaa_Latn_ES
+        case 0x84174E474C61746ELLU: // xab_Latn_NG
+        case 0x9817415A41676862LLU: // xag_Aghb_AZ
+        case 0xA01742524C61746ELLU: // xai_Latn_BR
+        case 0xA41742524C61746ELLU: // xaj_Latn_BR
+        case 0xA81756454C61746ELLU: // xak_Latn_VE
+        case 0xAC1752554379726CLLU: // xal_Cyrl_RU
+        case 0xB0175A414C61746ELLU: // xam_Latn_ZA
+        case 0xB417455445746869LLU: // xan_Ethi_ET
+        case 0xB817564E4C61746ELLU: // xao_Latn_VN
+        case 0xC41750474C61746ELLU: // xar_Latn_PG
+        case 0xC81752554379726CLLU: // xas_Cyrl_RU
+        case 0xCC1742524C61746ELLU: // xat_Latn_BR
+        case 0xD01749444C61746ELLU: // xau_Latn_ID
+        case 0xD41742524C61746ELLU: // xav_Latn_BR
+        case 0xD81755534C61746ELLU: // xaw_Latn_US
+        case 0xE01749444C61746ELLU: // xay_Latn_ID
+        case 0x843741554C61746ELLU: // xbb_Latn_AU
+        case 0x8C3741554C61746ELLU: // xbd_Latn_AU
+        case 0x903741554C61746ELLU: // xbe_Latn_AU
+        case 0x983741554C61746ELLU: // xbg_Latn_AU
+        case 0xA03750474C61746ELLU: // xbi_Latn_PG
+        case 0xA43741554C61746ELLU: // xbj_Latn_AU
+        case 0xB03746524C61746ELLU: // xbm_Latn_FR
+        case 0xB4374D594C61746ELLU: // xbn_Latn_MY
+        case 0xBC3741554C61746ELLU: // xbp_Latn_AU
+        case 0xC43749444C61746ELLU: // xbr_Latn_ID
+        case 0xD83742524C61746ELLU: // xbw_Latn_BR
+        case 0xE03741554C61746ELLU: // xby_Latn_AU
+        case 0x9C5755534C61746ELLU: // xch_Latn_US
+        case 0xB857555A43687273LLU: // xco_Chrs_UZ
+        case 0xC457545243617269LLU: // xcr_Cari_TR
+        case 0x807741554C61746ELLU: // xda_Latn_AU
+        case 0xA87741554C61746ELLU: // xdk_Latn_AU
+        case 0xB877414F4C61746ELLU: // xdo_Latn_AO
+        case 0xC07752554379726CLLU: // xdq_Cyrl_RU
+        case 0xE07749444C61746ELLU: // xdy_Latn_ID
+        case 0x8C97434D4C61746ELLU: // xed_Latn_CM
+        case 0x98975A414C61746ELLU: // xeg_Latn_ZA
+        case 0xB09749444C61746ELLU: // xem_Latn_ID
+        case 0xC49742524C61746ELLU: // xer_Latn_BR
+        case 0xC89750474C61746ELLU: // xes_Latn_PG
+        case 0xCC9742524C61746ELLU: // xet_Latn_BR
+        case 0xD09750474C61746ELLU: // xeu_Latn_PG
+        case 0x84D743494C61746ELLU: // xgb_Latn_CI
+        case 0x8CD741554C61746ELLU: // xgd_Latn_AU
+        case 0x98D741554C61746ELLU: // xgg_Latn_AU
+        case 0xA0D741554C61746ELLU: // xgi_Latn_AU
+        case 0xB0D741554C61746ELLU: // xgm_Latn_AU
+        case 0xD0D741554C61746ELLU: // xgu_Latn_AU
+        case 0xD8D741554C61746ELLU: // xgw_Latn_AU
+        case 0x78685A414C61746ELLU: // xh_Latn_ZA
+        case 0x90F7504B41726162LLU: // xhe_Arab_PK
+        case 0xB0F74B484B686D72LLU: // xhm_Khmr_KH
+        case 0xD4F7564E4C61746ELLU: // xhv_Latn_VN
+        case 0xA1175A414C61746ELLU: // xii_Latn_ZA
+        case 0xB51747544C61746ELLU: // xin_Latn_GT
+        case 0xC51742524C61746ELLU: // xir_Latn_BR
+        case 0xC917494E4F727961LLU: // xis_Orya_IN
+        case 0xE11742524C61746ELLU: // xiy_Latn_BR
+        case 0x853741554C61746ELLU: // xjb_Latn_AU
+        case 0xCD3741554C61746ELLU: // xjt_Latn_AU
+        case 0x8157504B41726162LLU: // xka_Arab_PK
+        case 0x8557424A4C61746ELLU: // xkb_Latn_BJ
+        case 0x8957495241726162LLU: // xkc_Arab_IR
+        case 0x8D5749444C61746ELLU: // xkd_Latn_ID
+        case 0x915749444C61746ELLU: // xke_Latn_ID
+        case 0x9557425454696274LLU: // xkf_Tibt_BT
+        case 0x99574D4C4C61746ELLU: // xkg_Latn_ML
+        case 0xA557495241726162LLU: // xkj_Arab_IR
+        case 0xAD5749444C61746ELLU: // xkl_Latn_ID
+        case 0xB55749444C61746ELLU: // xkn_Latn_ID
+        case 0xBD57495241726162LLU: // xkp_Arab_IR
+        case 0xC15749444C61746ELLU: // xkq_Latn_ID
+        case 0xC55742524C61746ELLU: // xkr_Latn_BR
+        case 0xC95749444C61746ELLU: // xks_Latn_ID
+        case 0xCD5747484C61746ELLU: // xkt_Latn_GH
+        case 0xD15743474C61746ELLU: // xku_Latn_CG
+        case 0xD55742574C61746ELLU: // xkv_Latn_BW
+        case 0xD95749444C61746ELLU: // xkw_Latn_ID
+        case 0xDD5750474C61746ELLU: // xkx_Latn_PG
+        case 0xE1574D594C61746ELLU: // xky_Latn_MY
+        case 0xE55742544C61746ELLU: // xkz_Latn_BT
+        case 0x817750474C61746ELLU: // xla_Latn_PG
+        case 0x897754524C796369LLU: // xlc_Lyci_TR
+        case 0x8D7754524C796469LLU: // xld_Lydi_TR
+        case 0xE1774952456C796DLLU: // xly_Elym_IR
+        case 0x8197534F4C61746ELLU: // xma_Latn_SO
+        case 0x8597434D4C61746ELLU: // xmb_Latn_CM
+        case 0x89974D5A4C61746ELLU: // xmc_Latn_MZ
+        case 0x8D97434D4C61746ELLU: // xmd_Latn_CM
+        case 0x9597474547656F72LLU: // xmf_Geor_GE
+        case 0x9997434D4C61746ELLU: // xmg_Latn_CM
+        case 0x9D9741554C61746ELLU: // xmh_Latn_AU
+        case 0xA597434D4C61746ELLU: // xmj_Latn_CM
+        case 0xB19749444C61746ELLU: // xmm_Latn_ID
+        case 0xB597434E4D616E69LLU: // xmn_Mani_CN
+        case 0xB99742524C61746ELLU: // xmo_Latn_BR
+        case 0xBD9741554C61746ELLU: // xmp_Latn_AU
+        case 0xC19741554C61746ELLU: // xmq_Latn_AU
+        case 0xC59753444D657263LLU: // xmr_Merc_SD
+        case 0xCD9749444C61746ELLU: // xmt_Latn_ID
+        case 0xD19741554C61746ELLU: // xmu_Latn_AU
+        case 0xD5974D474C61746ELLU: // xmv_Latn_MG
+        case 0xD9974D474C61746ELLU: // xmw_Latn_MG
+        case 0xDD9749444C61746ELLU: // xmx_Latn_ID
+        case 0xE19741554C61746ELLU: // xmy_Latn_AU
+        case 0xE59749444C61746ELLU: // xmz_Latn_ID
+        case 0x81B753414E617262LLU: // xna_Narb_SA
+        case 0x85B754574C61746ELLU: // xnb_Latn_TW
+        case 0xA1B741554C61746ELLU: // xni_Latn_AU
+        case 0xA5B7545A4C61746ELLU: // xnj_Latn_TZ
+        case 0xA9B741554C61746ELLU: // xnk_Latn_AU
+        case 0xB1B741554C61746ELLU: // xnm_Latn_AU
+        case 0xB5B750484C61746ELLU: // xnn_Latn_PH
+        case 0xC1B74D5A4C61746ELLU: // xnq_Latn_MZ
+        case 0xC5B7494E44657661LLU: // xnr_Deva_IN
+        case 0xCDB755534C61746ELLU: // xnt_Latn_US
+        case 0xD1B741554C61746ELLU: // xnu_Latn_AU
+        case 0xE1B741554C61746ELLU: // xny_Latn_AU
+        case 0xE5B745474C61746ELLU: // xnz_Latn_EG
+        case 0x89D74E474C61746ELLU: // xoc_Latn_NG
+        case 0x8DD749444C61746ELLU: // xod_Latn_ID
+        case 0x99D755474C61746ELLU: // xog_Latn_UG
+        case 0xA1D750474C61746ELLU: // xoi_Latn_PG
+        case 0xA9D742524C61746ELLU: // xok_Latn_BR
+        case 0xB1D753444C61746ELLU: // xom_Latn_SD
+        case 0xB5D747484C61746ELLU: // xon_Latn_GH
+        case 0xB9D742524C61746ELLU: // xoo_Latn_BR
+        case 0xBDD750474C61746ELLU: // xop_Latn_PG
+        case 0xC5D742524C61746ELLU: // xor_Latn_BR
+        case 0xD9D750474C61746ELLU: // xow_Latn_PG
+        case 0x81F741554C61746ELLU: // xpa_Latn_AU
+        case 0x85F741554C61746ELLU: // xpb_Latn_AU
+        case 0x8DF741554C61746ELLU: // xpd_Latn_AU
+        case 0x95F741554C61746ELLU: // xpf_Latn_AU
+        case 0x99F754524772656BLLU: // xpg_Grek_TR
+        case 0x9DF741554C61746ELLU: // xph_Latn_AU
+        case 0xA1F747424F67616DLLU: // xpi_Ogam_GB
+        case 0xA5F741554C61746ELLU: // xpj_Latn_AU
+        case 0xA9F742524C61746ELLU: // xpk_Latn_BR
+        case 0xADF741554C61746ELLU: // xpl_Latn_AU
+        case 0xB1F752554379726CLLU: // xpm_Cyrl_RU
+        case 0xB5F742524C61746ELLU: // xpn_Latn_BR
+        case 0xB9F74D584C61746ELLU: // xpo_Latn_MX
+        case 0xC1F755534C61746ELLU: // xpq_Latn_US
+        case 0xC5F7495250727469LLU: // xpr_Prti_IR
+        case 0xCDF741554C61746ELLU: // xpt_Latn_AU
+        case 0xD5F741554C61746ELLU: // xpv_Latn_AU
+        case 0xD9F741554C61746ELLU: // xpw_Latn_AU
+        case 0xDDF741554C61746ELLU: // xpx_Latn_AU
+        case 0xE5F741554C61746ELLU: // xpz_Latn_AU
+        case 0x823742524C61746ELLU: // xra_Latn_BR
+        case 0x863742464C61746ELLU: // xrb_Latn_BF
+        case 0x8E3741554C61746ELLU: // xrd_Latn_AU
+        case 0x923742524C61746ELLU: // xre_Latn_BR
+        case 0x9A3741554C61746ELLU: // xrg_Latn_AU
+        case 0xA23742524C61746ELLU: // xri_Latn_BR
+        case 0xB23752554379726CLLU: // xrm_Cyrl_RU
+        case 0xB63752554379726CLLU: // xrn_Cyrl_RU
+        case 0xC63749544C61746ELLU: // xrr_Latn_IT
+        case 0xD23741554C61746ELLU: // xru_Latn_AU
+        case 0xDA3750474C61746ELLU: // xrw_Latn_PG
+        case 0x8257594553617262LLU: // xsa_Sarb_YE
+        case 0x865750484C61746ELLU: // xsb_Latn_PH
+        case 0x925749444C61746ELLU: // xse_Latn_ID
+        case 0x9E574E474C61746ELLU: // xsh_Latn_NG
+        case 0xA25750474C61746ELLU: // xsi_Latn_PG
+        case 0xB25747484C61746ELLU: // xsm_Latn_GH
+        case 0xB6574E474C61746ELLU: // xsn_Latn_NG
+        case 0xBE5750474C61746ELLU: // xsp_Latn_PG
+        case 0xC2574D5A4C61746ELLU: // xsq_Latn_MZ
+        case 0xC6574E5044657661LLU: // xsr_Deva_NP
+        case 0xD25756454C61746ELLU: // xsu_Latn_VE
+        case 0xE25754574C61746ELLU: // xsy_Latn_TW
+        case 0x82774D584C61746ELLU: // xta_Latn_MX
+        case 0x86774D584C61746ELLU: // xtb_Latn_MX
+        case 0x8A7753444C61746ELLU: // xtc_Latn_SD
+        case 0x8E774D584C61746ELLU: // xtd_Latn_MX
+        case 0x927749444C61746ELLU: // xte_Latn_ID
+        case 0x9E7741554C61746ELLU: // xth_Latn_AU
+        case 0xA2774D584C61746ELLU: // xti_Latn_MX
+        case 0xA6774D584C61746ELLU: // xtj_Latn_MX
+        case 0xAE774D584C61746ELLU: // xtl_Latn_MX
+        case 0xB2774D584C61746ELLU: // xtm_Latn_MX
+        case 0xB6774D584C61746ELLU: // xtn_Latn_MX
+        case 0xBE774D584C61746ELLU: // xtp_Latn_MX
+        case 0xC277495242726168LLU: // xtq_Brah_IR
+        case 0xCA774D584C61746ELLU: // xts_Latn_MX
+        case 0xCE774D584C61746ELLU: // xtt_Latn_MX
+        case 0xD2774D584C61746ELLU: // xtu_Latn_MX
+        case 0xD67741554C61746ELLU: // xtv_Latn_AU
+        case 0xDA7742524C61746ELLU: // xtw_Latn_BR
+        case 0xE2774D584C61746ELLU: // xty_Latn_MX
+        case 0x8697494E54616D6CLLU: // xub_Taml_IN
+        case 0x8E9741554C61746ELLU: // xud_Latn_AU
+        case 0xA697494E54616D6CLLU: // xuj_Taml_IN
+        case 0xAE9741554C61746ELLU: // xul_Latn_AU
+        case 0xB29749544C61746ELLU: // xum_Latn_IT
+        case 0xB69741554C61746ELLU: // xun_Latn_AU
+        case 0xBA9754444C61746ELLU: // xuo_Latn_TD
+        case 0xCE9741554C61746ELLU: // xut_Latn_AU
+        case 0xD2974E414C61746ELLU: // xuu_Latn_NA
+        case 0x92B749544974616CLLU: // xve_Ital_IT
+        case 0xA2B7414641726162LLU: // xvi_Arab_AF
+        case 0xB6B745534C61746ELLU: // xvn_Latn_ES
+        case 0xBAB749544C61746ELLU: // xvo_Latn_IT
+        case 0xCAB749544C61746ELLU: // xvs_Latn_IT
+        case 0x82D742524C61746ELLU: // xwa_Latn_BR
+        case 0x8ED741554C61746ELLU: // xwd_Latn_AU
+        case 0x92D7424A4C61746ELLU: // xwe_Latn_BJ
+        case 0xA6D741554C61746ELLU: // xwj_Latn_AU
+        case 0xAAD741554C61746ELLU: // xwk_Latn_AU
+        case 0xAED7424A4C61746ELLU: // xwl_Latn_BJ
+        case 0xBAD752554379726CLLU: // xwo_Cyrl_RU
+        case 0xC6D749444C61746ELLU: // xwr_Latn_ID
+        case 0xCED741554C61746ELLU: // xwt_Latn_AU
+        case 0xDAD741554C61746ELLU: // xww_Latn_AU
+        case 0x86F747484C61746ELLU: // xxb_Latn_GH
+        case 0xAAF749444C61746ELLU: // xxk_Latn_ID
+        case 0xB2F741554C61746ELLU: // xxm_Latn_AU
+        case 0xC6F742524C61746ELLU: // xxr_Latn_BR
+        case 0xCEF749444C61746ELLU: // xxt_Latn_ID
+        case 0x831741554C61746ELLU: // xya_Latn_AU
+        case 0x871741554C61746ELLU: // xyb_Latn_AU
+        case 0xA71741554C61746ELLU: // xyj_Latn_AU
+        case 0xAB1741554C61746ELLU: // xyk_Latn_AU
+        case 0xAF1742524C61746ELLU: // xyl_Latn_BR
+        case 0xCF1741554C61746ELLU: // xyt_Latn_AU
+        case 0xE31741554C61746ELLU: // xyy_Latn_AU
+        case 0x9F37434E4D617263LLU: // xzh_Marc_CN
+        case 0xBF374D584C61746ELLU: // xzp_Latn_MX
+        case 0x801850454C61746ELLU: // yaa_Latn_PE
+        case 0x841842524C61746ELLU: // yab_Latn_BR
+        case 0x881849444C61746ELLU: // yac_Latn_ID
+        case 0x8C1850454C61746ELLU: // yad_Latn_PE
+        case 0x901856454C61746ELLU: // yae_Latn_VE
+        case 0x941843444C61746ELLU: // yaf_Latn_CD
+        case 0x9818434C4C61746ELLU: // yag_Latn_CL
+        case 0x9C18544A4C61746ELLU: // yah_Latn_TJ
+        case 0xA018544A4379726CLLU: // yai_Cyrl_TJ
+        case 0xA41843464C61746ELLU: // yaj_Latn_CF
+        case 0xA81855534C61746ELLU: // yak_Latn_US
+        case 0xAC18474E4C61746ELLU: // yal_Latn_GN
+        case 0xB018434D4C61746ELLU: // yam_Latn_CM
+        case 0xB4184E494C61746ELLU: // yan_Latn_NI
+        case 0xB8184D5A4C61746ELLU: // yao_Latn_MZ
+        case 0xBC18464D4C61746ELLU: // yap_Latn_FM
+        case 0xC0184D584C61746ELLU: // yaq_Latn_MX
+        case 0xC41856454C61746ELLU: // yar_Latn_VE
+        case 0xC818434D4C61746ELLU: // yas_Latn_CM
+        case 0xCC18434D4C61746ELLU: // yat_Latn_CM
+        case 0xD01856454C61746ELLU: // yau_Latn_VE
+        case 0xD418434D4C61746ELLU: // yav_Latn_CM
+        case 0xD81842524C61746ELLU: // yaw_Latn_BR
+        case 0xDC18414F4C61746ELLU: // yax_Latn_AO
+        case 0xE0184E474C61746ELLU: // yay_Latn_NG
+        case 0xE4184E474C61746ELLU: // yaz_Latn_NG
+        case 0x80384E474C61746ELLU: // yba_Latn_NG
+        case 0x8438434D4C61746ELLU: // ybb_Latn_CM
+        case 0x9038434E4C61746ELLU: // ybe_Latn_CN
+        case 0x9C384E5044657661LLU: // ybh_Deva_NP
+        case 0xA0384E5044657661LLU: // ybi_Deva_NP
+        case 0xA4384E474C61746ELLU: // ybj_Latn_NG
+        case 0xAC384E474C61746ELLU: // ybl_Latn_NG
+        case 0xB03850474C61746ELLU: // ybm_Latn_PG
+        case 0xB43842524C61746ELLU: // ybn_Latn_BR
+        case 0xB83850474C61746ELLU: // ybo_Latn_PG
+        case 0xDC3850474C61746ELLU: // ybx_Latn_PG
+        case 0xE03850474C61746ELLU: // yby_Latn_PG
+        case 0xAC58434E4C61746ELLU: // ycl_Latn_CN
+        case 0xB458434F4C61746ELLU: // ycn_Latn_CO
+        case 0xC45854574C61746ELLU: // ycr_Latn_TW
+        case 0x807841554C61746ELLU: // yda_Latn_AU
+        case 0x907850474C61746ELLU: // yde_Latn_PG
+        case 0x9878504B41726162LLU: // ydg_Arab_PK
+        case 0xA87850474C61746ELLU: // ydk_Latn_PG
+        case 0x8098494E4D6C796DLLU: // yea_Mlym_IN
+        case 0x889844454C61746ELLU: // yec_Latn_DE
+        case 0x909850474C61746ELLU: // yee_Latn_PG
+        case 0xA098434D4C61746ELLU: // yei_Latn_CM
+        case 0xA49847524772656BLLU: // yej_Grek_GR
+        case 0xAC9843444C61746ELLU: // yel_Latn_CD
+        case 0xC4984E474C61746ELLU: // yer_Latn_NG
+        case 0xC8984E474C61746ELLU: // yes_Latn_NG
+        case 0xCC9849444C61746ELLU: // yet_Latn_ID
+        case 0xD098494E54656C75LLU: // yeu_Telu_IN
+        case 0xD49850474C61746ELLU: // yev_Latn_PG
+        case 0xE09842574C61746ELLU: // yey_Latn_BW
+        case 0x80D841554C61746ELLU: // yga_Latn_AU
+        case 0xA0D841554C61746ELLU: // ygi_Latn_AU
+        case 0xACD850474C61746ELLU: // ygl_Latn_PG
+        case 0xB0D850474C61746ELLU: // ygm_Latn_PG
+        case 0xBCD8434E506C7264LLU: // ygp_Plrd_CN
+        case 0xC4D850474C61746ELLU: // ygr_Latn_PG
+        case 0xD0D841554C61746ELLU: // ygu_Latn_AU
+        case 0xD8D850474C61746ELLU: // ygw_Latn_PG
+        case 0x8CF8494C48656272LLU: // yhd_Hebr_IL
+        case 0x7969554148656272LLU: // yi_Hebr_UA
+        case 0x811841554C61746ELLU: // yia_Latn_AU
+        case 0x9918434E59696969LLU: // yig_Yiii_CN
+        case 0x9D18444548656272LLU: // yih_Hebr_DE
+        case 0xA11841554C61746ELLU: // yii_Latn_AU
+        case 0xA51841554C61746ELLU: // yij_Latn_AU
+        case 0xAD1841554C61746ELLU: // yil_Latn_AU
+        case 0xB118494E4C61746ELLU: // yim_Latn_IN
+        case 0xC51849444C61746ELLU: // yir_Latn_ID
+        case 0xC91850474C61746ELLU: // yis_Latn_PG
+        case 0xD518434E59696969LLU: // yiv_Yiii_CN
+        case 0x815850484C61746ELLU: // yka_Latn_PH
+        case 0x995852554379726CLLU: // ykg_Cyrl_RU
+        case 0x9D584D4E4379726CLLU: // ykh_Cyrl_MN
+        case 0xA15849444C61746ELLU: // yki_Latn_ID
+        case 0xA95850474C61746ELLU: // ykk_Latn_PG
+        case 0xB15850474C61746ELLU: // ykm_Latn_PG
+        case 0xB958434D4C61746ELLU: // yko_Latn_CM
+        case 0xC55850474C61746ELLU: // ykr_Latn_PG
+        case 0xE15843464C61746ELLU: // yky_Latn_CF
+        case 0x817850474C61746ELLU: // yla_Latn_PG
+        case 0x857850474C61746ELLU: // ylb_Latn_PG
+        case 0x917850474C61746ELLU: // yle_Latn_PG
+        case 0x997850474C61746ELLU: // ylg_Latn_PG
+        case 0xA17849444C61746ELLU: // yli_Latn_ID
+        case 0xAD7850474C61746ELLU: // yll_Latn_PG
+        case 0xC57841554C61746ELLU: // ylr_Latn_AU
+        case 0xD17850474C61746ELLU: // ylu_Latn_PG
+        case 0xE1784E434C61746ELLU: // yly_Latn_NC
+        case 0x859850474C61746ELLU: // ymb_Latn_PG
+        case 0x919850454C61746ELLU: // yme_Latn_PE
+        case 0x999843444C61746ELLU: // ymg_Latn_CD
+        case 0xA9984D5A4C61746ELLU: // ymk_Latn_MZ
+        case 0xAD9850474C61746ELLU: // yml_Latn_PG
+        case 0xB198534F4C61746ELLU: // ymm_Latn_SO
+        case 0xB59849444C61746ELLU: // ymn_Latn_ID
+        case 0xB99850474C61746ELLU: // ymo_Latn_PG
+        case 0xBD9850474C61746ELLU: // ymp_Latn_PG
+        case 0x81B8434E506C7264LLU: // yna_Plrd_CN
+        case 0x8DB841554C61746ELLU: // ynd_Latn_AU
+        case 0x99B843444C61746ELLU: // yng_Latn_CD
+        case 0xA9B852554379726CLLU: // ynk_Cyrl_RU
+        case 0xADB850474C61746ELLU: // ynl_Latn_PG
+        case 0xC1B84E474C61746ELLU: // ynq_Latn_NG
+        case 0xC9B843444C61746ELLU: // yns_Latn_CD
+        case 0xD1B8434F4C61746ELLU: // ynu_Latn_CO
+        case 0x796F4E474C61746ELLU: // yo_Latn_NG
+        case 0x85D850474C61746ELLU: // yob_Latn_PG
+        case 0x99D850484C61746ELLU: // yog_Latn_PH
+        case 0xA1D84A504A70616ELLU: // yoi_Jpan_JP
+        case 0xA9D855534C61746ELLU: // yok_Latn_US
+        case 0xADD849454C61746ELLU: // yol_Latn_IE
+        case 0xB1D843444C61746ELLU: // yom_Latn_CD
+        case 0xB5D850474C61746ELLU: // yon_Latn_PG
+        case 0xCDD84E474C61746ELLU: // yot_Latn_NG
+        case 0xE1D8544854686169LLU: // yoy_Thai_TH
+        case 0x823850474C61746ELLU: // yra_Latn_PG
+        case 0x863850474C61746ELLU: // yrb_Latn_PG
+        case 0x923843494C61746ELLU: // yre_Latn_CI
+        case 0xAA3852554379726CLLU: // yrk_Cyrl_RU
+        case 0xAE3842524C61746ELLU: // yrl_Latn_BR
+        case 0xB23841554C61746ELLU: // yrm_Latn_AU
+        case 0xBA3842524C61746ELLU: // yro_Latn_BR
+        case 0xCA3849444C61746ELLU: // yrs_Latn_ID
+        case 0xDA3850474C61746ELLU: // yrw_Latn_PG
+        case 0xE23841554C61746ELLU: // yry_Latn_AU
+        case 0x8E58434E59696969LLU: // ysd_Yiii_CN
+        case 0xB658434E59696969LLU: // ysn_Yiii_CN
+        case 0xBE58434E59696969LLU: // ysp_Yiii_CN
+        case 0xC65852554379726CLLU: // ysr_Cyrl_RU
+        case 0xCA5850474C61746ELLU: // yss_Latn_PG
+        case 0xE258434E506C7264LLU: // ysy_Plrd_CN
+        case 0xDA7850474C61746ELLU: // ytw_Latn_PG
+        case 0xE27841554C61746ELLU: // yty_Latn_AU
+        case 0x82984D584C61746ELLU: // yua_Latn_MX
+        case 0x869841554C61746ELLU: // yub_Latn_AU
+        case 0x8A9855534C61746ELLU: // yuc_Latn_US
+        case 0x8E98494C48656272LLU: // yud_Hebr_IL
+        case 0x9298434E48616E73LLU: // yue_Hans_CN
+        case 0x9298484B48616E74LLU: // yue_Hant_HK
+        case 0x969855534C61746ELLU: // yuf_Latn_US
+        case 0x9A9852554379726CLLU: // yug_Cyrl_RU
+        case 0xA298434F4C61746ELLU: // yui_Latn_CO
+        case 0xA69850474C61746ELLU: // yuj_Latn_PG
+        case 0xAE9843464C61746ELLU: // yul_Latn_CF
+        case 0xB29855534C61746ELLU: // yum_Latn_US
+        case 0xB6984E474C61746ELLU: // yun_Latn_NG
+        case 0xBE98434F4C61746ELLU: // yup_Latn_CO
+        case 0xC298424F4C61746ELLU: // yuq_Latn_BO
+        case 0xC69855534C61746ELLU: // yur_Latn_US
+        case 0xCE9850474C61746ELLU: // yut_Latn_PG
+        case 0xDA9850474C61746ELLU: // yuw_Latn_PG
+        case 0xDE9852554379726CLLU: // yux_Cyrl_RU
+        case 0xE698424F4C61746ELLU: // yuz_Latn_BO
+        case 0x82B849444C61746ELLU: // yva_Latn_ID
+        case 0xCEB856454C61746ELLU: // yvt_Latn_VE
+        case 0x82D850474C61746ELLU: // ywa_Latn_PG
+        case 0x9AD841554C61746ELLU: // ywg_Latn_AU
+        case 0xB6D842524C61746ELLU: // ywn_Latn_BR
+        case 0xC2D8434E506C7264LLU: // ywq_Plrd_CN
+        case 0xC6D841554C61746ELLU: // ywr_Latn_AU
+        case 0xD2D8434E506C7264LLU: // ywu_Plrd_CN
+        case 0xDAD841554C61746ELLU: // yww_Latn_AU
+        case 0x82F841554C61746ELLU: // yxa_Latn_AU
+        case 0x9AF841554C61746ELLU: // yxg_Latn_AU
+        case 0xAEF841554C61746ELLU: // yxl_Latn_AU
+        case 0xB2F841554C61746ELLU: // yxm_Latn_AU
+        case 0xD2F841554C61746ELLU: // yxu_Latn_AU
+        case 0xE2F841554C61746ELLU: // yxy_Latn_AU
+        case 0xC71841554C61746ELLU: // yyr_Latn_AU
+        case 0xD31850474C61746ELLU: // yyu_Latn_PG
+        case 0x7A61434E4C61746ELLU: // za_Latn_CN
+        case 0x80194D584C61746ELLU: // zaa_Latn_MX
+        case 0x84194D584C61746ELLU: // zab_Latn_MX
+        case 0x88194D584C61746ELLU: // zac_Latn_MX
+        case 0x8C194D584C61746ELLU: // zad_Latn_MX
+        case 0x90194D584C61746ELLU: // zae_Latn_MX
+        case 0x94194D584C61746ELLU: // zaf_Latn_MX
+        case 0x981953444C61746ELLU: // zag_Latn_SD
+        case 0x9C194E474C61746ELLU: // zah_Latn_NG
+        case 0xA419545A4C61746ELLU: // zaj_Latn_TZ
+        case 0xA819545A4C61746ELLU: // zak_Latn_TZ
+        case 0xB0194D584C61746ELLU: // zam_Latn_MX
+        case 0xB8194D584C61746ELLU: // zao_Latn_MX
+        case 0xBC194D584C61746ELLU: // zap_Latn_MX
+        case 0xC0194D584C61746ELLU: // zaq_Latn_MX
+        case 0xC4194D584C61746ELLU: // zar_Latn_MX
+        case 0xC8194D584C61746ELLU: // zas_Latn_MX
+        case 0xCC194D584C61746ELLU: // zat_Latn_MX
+        case 0xD019494E54696274LLU: // zau_Tibt_IN
+        case 0xD4194D584C61746ELLU: // zav_Latn_MX
+        case 0xD8194D584C61746ELLU: // zaw_Latn_MX
+        case 0xDC194D584C61746ELLU: // zax_Latn_MX
+        case 0xE01945544C61746ELLU: // zay_Latn_ET
+        case 0xE4194E474C61746ELLU: // zaz_Latn_NG
+        case 0x88394D594C61746ELLU: // zbc_Latn_MY
+        case 0x90394D594C61746ELLU: // zbe_Latn_MY
+        case 0xCC3949444C61746ELLU: // zbt_Latn_ID
+        case 0xD0394E474C61746ELLU: // zbu_Latn_NG
+        case 0xD8394D594C61746ELLU: // zbw_Latn_MY
+        case 0x80594D584C61746ELLU: // zca_Latn_MX
+        case 0x9C59434E48616E69LLU: // zch_Hani_CN
+        case 0xA4794B4D41726162LLU: // zdj_Arab_KM
+        case 0x80994E4C4C61746ELLU: // zea_Latn_NL
+        case 0x989950474C61746ELLU: // zeg_Latn_PG
+        case 0x9C99434E48616E69LLU: // zeh_Hani_CN
+        case 0xB0994E474C61746ELLU: // zem_Latn_NG
+        case 0xB4994D5254666E67LLU: // zen_Tfng_MR
+        case 0x80D9545A4C61746ELLU: // zga_Latn_TZ
+        case 0x84D9434E48616E69LLU: // zgb_Hani_CN
+        case 0x9CD94D4154666E67LLU: // zgh_Tfng_MA
+        case 0xB0D9434E48616E69LLU: // zgm_Hani_CN
+        case 0xB4D9434E48616E69LLU: // zgn_Hani_CN
+        case 0xC4D950474C61746ELLU: // zgr_Latn_PG
+        case 0x7A685457426F706FLLU: // zh_Bopo_TW
+        case 0x7A68545748616E62LLU: // zh_Hanb_TW
+        case 0x7A68434E48616E73LLU: // zh_Hans_CN
+        case 0x7A68545748616E74LLU: // zh_Hant_TW
+        case 0x8CF9434E48616E69LLU: // zhd_Hani_CN
+        case 0xA0F94E474C61746ELLU: // zhi_Latn_NG
+        case 0xB4F9434E4C61746ELLU: // zhn_Latn_CN
+        case 0xD8F9434D4C61746ELLU: // zhw_Latn_CM
+        case 0xDCF9434E4E736875LLU: // zhx_Nshu_CN
+        case 0x811950474C61746ELLU: // zia_Latn_PG
+        case 0xA91950474C61746ELLU: // zik_Latn_PG
+        case 0xAD19474E4C61746ELLU: // zil_Latn_GN
+        case 0xB11954444C61746ELLU: // zim_Latn_TD
+        case 0xB519545A4C61746ELLU: // zin_Latn_TZ
+        case 0xD919545A4C61746ELLU: // ziw_Latn_TZ
+        case 0xE5194E474C61746ELLU: // ziz_Latn_NG
+        case 0x815949444C61746ELLU: // zka_Latn_ID
+        case 0x8D594D4D4C61746ELLU: // zkd_Latn_MM
+        case 0xB95952554379726CLLU: // zko_Cyrl_RU
+        case 0xBD5942524C61746ELLU: // zkp_Latn_BR
+        case 0xCD59434E4B697473LLU: // zkt_Kits_CN
+        case 0xD15941554C61746ELLU: // zku_Latn_AU
+        case 0xE55952554379726CLLU: // zkz_Cyrl_RU
+        case 0x817943444C61746ELLU: // zla_Latn_CD
+        case 0xA579434E48616E69LLU: // zlj_Hani_CN
+        case 0xB17954474C61746ELLU: // zlm_Latn_TG
+        case 0xB579434E48616E69LLU: // zln_Hani_CN
+        case 0xC179434E48616E69LLU: // zlq_Hani_CN
+        case 0xD1794E474C61746ELLU: // zlu_Latn_NG
+        case 0x819941554C61746ELLU: // zma_Latn_AU
+        case 0x859943444C61746ELLU: // zmb_Latn_CD
+        case 0x899941554C61746ELLU: // zmc_Latn_AU
+        case 0x8D9941554C61746ELLU: // zmd_Latn_AU
+        case 0x919941554C61746ELLU: // zme_Latn_AU
+        case 0x959943444C61746ELLU: // zmf_Latn_CD
+        case 0x999941554C61746ELLU: // zmg_Latn_AU
+        case 0x9D9950474C61746ELLU: // zmh_Latn_PG
+        case 0xA1994D594C61746ELLU: // zmi_Latn_MY
+        case 0xA59941554C61746ELLU: // zmj_Latn_AU
+        case 0xA99941554C61746ELLU: // zmk_Latn_AU
+        case 0xAD9941554C61746ELLU: // zml_Latn_AU
+        case 0xB19941554C61746ELLU: // zmm_Latn_AU
+        case 0xB59947414C61746ELLU: // zmn_Latn_GA
+        case 0xB99953444C61746ELLU: // zmo_Latn_SD
+        case 0xBD9943444C61746ELLU: // zmp_Latn_CD
+        case 0xC19943444C61746ELLU: // zmq_Latn_CD
+        case 0xC59941554C61746ELLU: // zmr_Latn_AU
+        case 0xC99943444C61746ELLU: // zms_Latn_CD
+        case 0xCD9941554C61746ELLU: // zmt_Latn_AU
+        case 0xD19941554C61746ELLU: // zmu_Latn_AU
+        case 0xD59941554C61746ELLU: // zmv_Latn_AU
+        case 0xD99943444C61746ELLU: // zmw_Latn_CD
+        case 0xDD9943474C61746ELLU: // zmx_Latn_CG
+        case 0xE19941554C61746ELLU: // zmy_Latn_AU
+        case 0xE59943444C61746ELLU: // zmz_Latn_CD
+        case 0x81B954444C61746ELLU: // zna_Latn_TD
+        case 0x91B943444C61746ELLU: // zne_Latn_CD
+        case 0x99B9564E4C61746ELLU: // zng_Latn_VN
+        case 0xA9B941554C61746ELLU: // znk_Latn_AU
+        case 0xC9B94E474C61746ELLU: // zns_Latn_NG
+        case 0x89D94D584C61746ELLU: // zoc_Latn_MX
+        case 0x9DD94D584C61746ELLU: // zoh_Latn_MX
+        case 0xB1D9494E4C61746ELLU: // zom_Latn_IN
+        case 0xB9D94D584C61746ELLU: // zoo_Latn_MX
+        case 0xC1D94D584C61746ELLU: // zoq_Latn_MX
+        case 0xC5D94D584C61746ELLU: // zor_Latn_MX
+        case 0xC9D94D584C61746ELLU: // zos_Latn_MX
+        case 0x81F94D584C61746ELLU: // zpa_Latn_MX
+        case 0x85F94D584C61746ELLU: // zpb_Latn_MX
+        case 0x89F94D584C61746ELLU: // zpc_Latn_MX
+        case 0x8DF94D584C61746ELLU: // zpd_Latn_MX
+        case 0x91F94D584C61746ELLU: // zpe_Latn_MX
+        case 0x95F94D584C61746ELLU: // zpf_Latn_MX
+        case 0x99F94D584C61746ELLU: // zpg_Latn_MX
+        case 0x9DF94D584C61746ELLU: // zph_Latn_MX
+        case 0xA1F94D584C61746ELLU: // zpi_Latn_MX
+        case 0xA5F94D584C61746ELLU: // zpj_Latn_MX
+        case 0xA9F94D584C61746ELLU: // zpk_Latn_MX
+        case 0xADF94D584C61746ELLU: // zpl_Latn_MX
+        case 0xB1F94D584C61746ELLU: // zpm_Latn_MX
+        case 0xB5F94D584C61746ELLU: // zpn_Latn_MX
+        case 0xB9F94D584C61746ELLU: // zpo_Latn_MX
+        case 0xBDF94D584C61746ELLU: // zpp_Latn_MX
+        case 0xC1F94D584C61746ELLU: // zpq_Latn_MX
+        case 0xC5F94D584C61746ELLU: // zpr_Latn_MX
+        case 0xC9F94D584C61746ELLU: // zps_Latn_MX
+        case 0xCDF94D584C61746ELLU: // zpt_Latn_MX
+        case 0xD1F94D584C61746ELLU: // zpu_Latn_MX
+        case 0xD5F94D584C61746ELLU: // zpv_Latn_MX
+        case 0xD9F94D584C61746ELLU: // zpw_Latn_MX
+        case 0xDDF94D584C61746ELLU: // zpx_Latn_MX
+        case 0xE1F94D584C61746ELLU: // zpy_Latn_MX
+        case 0xE5F94D584C61746ELLU: // zpz_Latn_MX
+        case 0x9219434E48616E69LLU: // zqe_Hani_CN
+        case 0x9A39494E4F727961LLU: // zrg_Orya_IN
+        case 0xB63954444C61746ELLU: // zrn_Latn_TD
+        case 0xBA3945434C61746ELLU: // zro_Latn_EC
+        case 0xBE39465248656272LLU: // zrp_Hebr_FR
+        case 0xCA3949444C61746ELLU: // zrs_Latn_ID
+        case 0x825950474C61746ELLU: // zsa_Latn_PG
+        case 0xC6594D584C61746ELLU: // zsr_Latn_MX
+        case 0xD25950474C61746ELLU: // zsu_Latn_PG
+        case 0x92794D584C61746ELLU: // zte_Latn_MX
+        case 0x9A794D584C61746ELLU: // ztg_Latn_MX
+        case 0xAE794D584C61746ELLU: // ztl_Latn_MX
+        case 0xB2794D584C61746ELLU: // ztm_Latn_MX
+        case 0xB6794D584C61746ELLU: // ztn_Latn_MX
+        case 0xBE794D584C61746ELLU: // ztp_Latn_MX
+        case 0xC2794D584C61746ELLU: // ztq_Latn_MX
+        case 0xCA794D584C61746ELLU: // zts_Latn_MX
+        case 0xCE794D584C61746ELLU: // ztt_Latn_MX
+        case 0xD2794D584C61746ELLU: // ztu_Latn_MX
+        case 0xDE794D584C61746ELLU: // ztx_Latn_MX
+        case 0xE2794D584C61746ELLU: // zty_Latn_MX
+        case 0x7A755A414C61746ELLU: // zu_Latn_ZA
+        case 0x9E9950474C61746ELLU: // zuh_Latn_PG
+        case 0xB2994F4D41726162LLU: // zum_Arab_OM
+        case 0xB69955534C61746ELLU: // zun_Latn_US
+        case 0xE299434D4C61746ELLU: // zuy_Latn_CM
+        case 0x82D9455445746869LLU: // zwa_Ethi_ET
+        case 0x9B19434E48616E69LLU: // zyg_Hani_CN
+        case 0xA719434E4C61746ELLU: // zyj_Latn_CN
+        case 0xB719434E48616E69LLU: // zyn_Hani_CN
+        case 0xBF194D4D4C61746ELLU: // zyp_Latn_MM
+        case 0x833954524C61746ELLU: // zza_Latn_TR
+        case 0xA739434E48616E69LLU: // zzj_Hani_CN
+            return true;
+        default:
+            return false;
+    }
+}
+
+static uint32_t findArabParent(uint32_t packed_lang_region) {
+    switch(packed_lang_region) {
+        case 0x61724145u: // ar-AE -> ar-015
+        case 0x6172445Au: // ar-DZ -> ar-015
+        case 0x61724548u: // ar-EH -> ar-015
+        case 0x61724C59u: // ar-LY -> ar-015
+        case 0x61724D41u: // ar-MA -> ar-015
+        case 0x6172544Eu: // ar-TN -> ar-015
+            return 0x61729420u;
+        default:
+            return 0;
+    }
+}
+
+static uint32_t findDevaParent(uint32_t packed_lang_region) {
+    switch(packed_lang_region) {
+        case 0x68690000u: // hi-Latn -> en-IN
+            return 0x656E494Eu;
+        default:
+            return 0;
+    }
+}
+
+static uint32_t findHantParent(uint32_t packed_lang_region) {
+    switch(packed_lang_region) {
+        case 0x7A684D4Fu: // zh-Hant-MO -> zh-Hant-HK
+            return 0x7A68484Bu;
+        default:
+            return 0;
+    }
+}
+
+static uint32_t findLatnParent(uint32_t packed_lang_region) {
+    switch(packed_lang_region) {
+        case 0x656E80A1u: // en-150 -> en-001
+        case 0x656E4147u: // en-AG -> en-001
+        case 0x656E4149u: // en-AI -> en-001
+        case 0x656E4155u: // en-AU -> en-001
+        case 0x656E4242u: // en-BB -> en-001
+        case 0x656E424Du: // en-BM -> en-001
+        case 0x656E4253u: // en-BS -> en-001
+        case 0x656E4257u: // en-BW -> en-001
+        case 0x656E425Au: // en-BZ -> en-001
+        case 0x656E4343u: // en-CC -> en-001
+        case 0x656E434Bu: // en-CK -> en-001
+        case 0x656E434Du: // en-CM -> en-001
+        case 0x656E4358u: // en-CX -> en-001
+        case 0x656E4359u: // en-CY -> en-001
+        case 0x656E4447u: // en-DG -> en-001
+        case 0x656E444Du: // en-DM -> en-001
+        case 0x656E4552u: // en-ER -> en-001
+        case 0x656E464Au: // en-FJ -> en-001
+        case 0x656E464Bu: // en-FK -> en-001
+        case 0x656E464Du: // en-FM -> en-001
+        case 0x656E4742u: // en-GB -> en-001
+        case 0x656E4744u: // en-GD -> en-001
+        case 0x656E4747u: // en-GG -> en-001
+        case 0x656E4748u: // en-GH -> en-001
+        case 0x656E4749u: // en-GI -> en-001
+        case 0x656E474Du: // en-GM -> en-001
+        case 0x656E4759u: // en-GY -> en-001
+        case 0x656E484Bu: // en-HK -> en-001
+        case 0x656E4944u: // en-ID -> en-001
+        case 0x656E4945u: // en-IE -> en-001
+        case 0x656E494Cu: // en-IL -> en-001
+        case 0x656E494Du: // en-IM -> en-001
+        case 0x656E494Eu: // en-IN -> en-001
+        case 0x656E494Fu: // en-IO -> en-001
+        case 0x656E4A45u: // en-JE -> en-001
+        case 0x656E4A4Du: // en-JM -> en-001
+        case 0x656E4B45u: // en-KE -> en-001
+        case 0x656E4B49u: // en-KI -> en-001
+        case 0x656E4B4Eu: // en-KN -> en-001
+        case 0x656E4B59u: // en-KY -> en-001
+        case 0x656E4C43u: // en-LC -> en-001
+        case 0x656E4C52u: // en-LR -> en-001
+        case 0x656E4C53u: // en-LS -> en-001
+        case 0x656E4D47u: // en-MG -> en-001
+        case 0x656E4D4Fu: // en-MO -> en-001
+        case 0x656E4D53u: // en-MS -> en-001
+        case 0x656E4D54u: // en-MT -> en-001
+        case 0x656E4D55u: // en-MU -> en-001
+        case 0x656E4D56u: // en-MV -> en-001
+        case 0x656E4D57u: // en-MW -> en-001
+        case 0x656E4D59u: // en-MY -> en-001
+        case 0x656E4E41u: // en-NA -> en-001
+        case 0x656E4E46u: // en-NF -> en-001
+        case 0x656E4E47u: // en-NG -> en-001
+        case 0x656E4E52u: // en-NR -> en-001
+        case 0x656E4E55u: // en-NU -> en-001
+        case 0x656E4E5Au: // en-NZ -> en-001
+        case 0x656E5047u: // en-PG -> en-001
+        case 0x656E504Bu: // en-PK -> en-001
+        case 0x656E504Eu: // en-PN -> en-001
+        case 0x656E5057u: // en-PW -> en-001
+        case 0x656E5257u: // en-RW -> en-001
+        case 0x656E5342u: // en-SB -> en-001
+        case 0x656E5343u: // en-SC -> en-001
+        case 0x656E5344u: // en-SD -> en-001
+        case 0x656E5347u: // en-SG -> en-001
+        case 0x656E5348u: // en-SH -> en-001
+        case 0x656E534Cu: // en-SL -> en-001
+        case 0x656E5353u: // en-SS -> en-001
+        case 0x656E5358u: // en-SX -> en-001
+        case 0x656E535Au: // en-SZ -> en-001
+        case 0x656E5443u: // en-TC -> en-001
+        case 0x656E544Bu: // en-TK -> en-001
+        case 0x656E544Fu: // en-TO -> en-001
+        case 0x656E5454u: // en-TT -> en-001
+        case 0x656E5456u: // en-TV -> en-001
+        case 0x656E545Au: // en-TZ -> en-001
+        case 0x656E5547u: // en-UG -> en-001
+        case 0x656E5643u: // en-VC -> en-001
+        case 0x656E5647u: // en-VG -> en-001
+        case 0x656E5655u: // en-VU -> en-001
+        case 0x656E5753u: // en-WS -> en-001
+        case 0x656E5A41u: // en-ZA -> en-001
+        case 0x656E5A4Du: // en-ZM -> en-001
+        case 0x656E5A57u: // en-ZW -> en-001
+            return 0x656E8400u;
+        case 0x656E4154u: // en-AT -> en-150
+        case 0x656E4245u: // en-BE -> en-150
+        case 0x656E4348u: // en-CH -> en-150
+        case 0x656E4445u: // en-DE -> en-150
+        case 0x656E444Bu: // en-DK -> en-150
+        case 0x656E4649u: // en-FI -> en-150
+        case 0x656E4E4Cu: // en-NL -> en-150
+        case 0x656E5345u: // en-SE -> en-150
+        case 0x656E5349u: // en-SI -> en-150
+            return 0x656E80A1u;
+        case 0x65734152u: // es-AR -> es-419
+        case 0x6573424Fu: // es-BO -> es-419
+        case 0x65734252u: // es-BR -> es-419
+        case 0x6573425Au: // es-BZ -> es-419
+        case 0x6573434Cu: // es-CL -> es-419
+        case 0x6573434Fu: // es-CO -> es-419
+        case 0x65734352u: // es-CR -> es-419
+        case 0x65734355u: // es-CU -> es-419
+        case 0x6573444Fu: // es-DO -> es-419
+        case 0x65734543u: // es-EC -> es-419
+        case 0x65734754u: // es-GT -> es-419
+        case 0x6573484Eu: // es-HN -> es-419
+        case 0x65734D58u: // es-MX -> es-419
+        case 0x65734E49u: // es-NI -> es-419
+        case 0x65735041u: // es-PA -> es-419
+        case 0x65735045u: // es-PE -> es-419
+        case 0x65735052u: // es-PR -> es-419
+        case 0x65735059u: // es-PY -> es-419
+        case 0x65735356u: // es-SV -> es-419
+        case 0x65735553u: // es-US -> es-419
+        case 0x65735559u: // es-UY -> es-419
+        case 0x65735645u: // es-VE -> es-419
+            return 0x6573A424u;
+        case 0x6E620000u: // nb -> no
+        case 0x6E6E0000u: // nn -> no
+            return 0x6E6F0000u;
+        case 0x7074414Fu: // pt-AO -> pt-PT
+        case 0x70744348u: // pt-CH -> pt-PT
+        case 0x70744356u: // pt-CV -> pt-PT
+        case 0x70744751u: // pt-GQ -> pt-PT
+        case 0x70744757u: // pt-GW -> pt-PT
+        case 0x70744C55u: // pt-LU -> pt-PT
+        case 0x70744D4Fu: // pt-MO -> pt-PT
+        case 0x70744D5Au: // pt-MZ -> pt-PT
+        case 0x70745354u: // pt-ST -> pt-PT
+        case 0x7074544Cu: // pt-TL -> pt-PT
+            return 0x70745054u;
+        default:
+            return 0;
+    }
+}
+
+static uint32_t find000BParent(uint32_t packed_lang_region) {
+    switch(packed_lang_region) {
+        case 0x61725842u: // ar-XB -> ar-015
+            return 0x61729420u;
+        default:
+            return 0;
+    }
+}
+
+uint32_t findParentLocalePackedKey(const char* script, uint32_t packed_lang_region) {
+    uint32_t packedScript = packScript(script);
+    switch (packedScript) {
+        case 0x41726162u: // Arab
+            return findArabParent(packed_lang_region);
+        case 0x44657661u: // Deva
+            return findDevaParent(packed_lang_region);
+        case 0x48616E74u: // Hant
+            return findHantParent(packed_lang_region);
+        case 0x4C61746Eu: // Latn
+            return findLatnParent(packed_lang_region);
+        case 0x7E7E7E42u: // ~~~B
+            return find000BParent(packed_lang_region);
+        default:
+            return 0;
+    }
+}
+
+uint32_t getMaxAncestorTreeDepth() {
+    return 3;
+}
+
+} // namespace android
diff --git a/libs/androidfw/LocaleDataTables.cpp b/libs/androidfw/LocaleDataTables.cpp
deleted file mode 100644
index 9435118..0000000
--- a/libs/androidfw/LocaleDataTables.cpp
+++ /dev/null
@@ -1,2461 +0,0 @@
-// Auto-generated by ./tools/localedata/extract_icu_data.py
-
-const char SCRIPT_CODES[][4] = {
-    /* 0  */ {'A', 'g', 'h', 'b'},
-    /* 1  */ {'A', 'h', 'o', 'm'},
-    /* 2  */ {'A', 'r', 'a', 'b'},
-    /* 3  */ {'A', 'r', 'm', 'i'},
-    /* 4  */ {'A', 'r', 'm', 'n'},
-    /* 5  */ {'A', 'v', 's', 't'},
-    /* 6  */ {'B', 'a', 'm', 'u'},
-    /* 7  */ {'B', 'a', 's', 's'},
-    /* 8  */ {'B', 'e', 'n', 'g'},
-    /* 9  */ {'B', 'r', 'a', 'h'},
-    /* 10 */ {'C', 'a', 'k', 'm'},
-    /* 11 */ {'C', 'a', 'n', 's'},
-    /* 12 */ {'C', 'a', 'r', 'i'},
-    /* 13 */ {'C', 'h', 'a', 'm'},
-    /* 14 */ {'C', 'h', 'e', 'r'},
-    /* 15 */ {'C', 'h', 'r', 's'},
-    /* 16 */ {'C', 'o', 'p', 't'},
-    /* 17 */ {'C', 'p', 'r', 't'},
-    /* 18 */ {'C', 'y', 'r', 'l'},
-    /* 19 */ {'D', 'e', 'v', 'a'},
-    /* 20 */ {'E', 'g', 'y', 'p'},
-    /* 21 */ {'E', 't', 'h', 'i'},
-    /* 22 */ {'G', 'e', 'o', 'r'},
-    /* 23 */ {'G', 'o', 'n', 'g'},
-    /* 24 */ {'G', 'o', 'n', 'm'},
-    /* 25 */ {'G', 'o', 't', 'h'},
-    /* 26 */ {'G', 'r', 'e', 'k'},
-    /* 27 */ {'G', 'u', 'j', 'r'},
-    /* 28 */ {'G', 'u', 'r', 'u'},
-    /* 29 */ {'H', 'a', 'n', 's'},
-    /* 30 */ {'H', 'a', 'n', 't'},
-    /* 31 */ {'H', 'e', 'b', 'r'},
-    /* 32 */ {'H', 'l', 'u', 'w'},
-    /* 33 */ {'H', 'm', 'n', 'p'},
-    /* 34 */ {'I', 't', 'a', 'l'},
-    /* 35 */ {'J', 'p', 'a', 'n'},
-    /* 36 */ {'K', 'a', 'l', 'i'},
-    /* 37 */ {'K', 'a', 'n', 'a'},
-    /* 38 */ {'K', 'a', 'w', 'i'},
-    /* 39 */ {'K', 'h', 'a', 'r'},
-    /* 40 */ {'K', 'h', 'm', 'r'},
-    /* 41 */ {'K', 'i', 't', 's'},
-    /* 42 */ {'K', 'n', 'd', 'a'},
-    /* 43 */ {'K', 'o', 'r', 'e'},
-    /* 44 */ {'L', 'a', 'n', 'a'},
-    /* 45 */ {'L', 'a', 'o', 'o'},
-    /* 46 */ {'L', 'a', 't', 'n'},
-    /* 47 */ {'L', 'e', 'p', 'c'},
-    /* 48 */ {'L', 'i', 'n', 'a'},
-    /* 49 */ {'L', 'i', 's', 'u'},
-    /* 50 */ {'L', 'y', 'c', 'i'},
-    /* 51 */ {'L', 'y', 'd', 'i'},
-    /* 52 */ {'M', 'a', 'n', 'd'},
-    /* 53 */ {'M', 'a', 'n', 'i'},
-    /* 54 */ {'M', 'e', 'd', 'f'},
-    /* 55 */ {'M', 'e', 'r', 'c'},
-    /* 56 */ {'M', 'l', 'y', 'm'},
-    /* 57 */ {'M', 'o', 'n', 'g'},
-    /* 58 */ {'M', 'r', 'o', 'o'},
-    /* 59 */ {'M', 'y', 'm', 'r'},
-    /* 60 */ {'N', 'a', 'r', 'b'},
-    /* 61 */ {'N', 'k', 'o', 'o'},
-    /* 62 */ {'N', 's', 'h', 'u'},
-    /* 63 */ {'O', 'g', 'a', 'm'},
-    /* 64 */ {'O', 'l', 'c', 'k'},
-    /* 65 */ {'O', 'r', 'k', 'h'},
-    /* 66 */ {'O', 'r', 'y', 'a'},
-    /* 67 */ {'O', 's', 'g', 'e'},
-    /* 68 */ {'O', 'u', 'g', 'r'},
-    /* 69 */ {'P', 'a', 'u', 'c'},
-    /* 70 */ {'P', 'h', 'l', 'i'},
-    /* 71 */ {'P', 'h', 'n', 'x'},
-    /* 72 */ {'P', 'l', 'r', 'd'},
-    /* 73 */ {'P', 'r', 't', 'i'},
-    /* 74 */ {'R', 'o', 'h', 'g'},
-    /* 75 */ {'R', 'u', 'n', 'r'},
-    /* 76 */ {'S', 'a', 'm', 'r'},
-    /* 77 */ {'S', 'a', 'r', 'b'},
-    /* 78 */ {'S', 'a', 'u', 'r'},
-    /* 79 */ {'S', 'g', 'n', 'w'},
-    /* 80 */ {'S', 'i', 'n', 'h'},
-    /* 81 */ {'S', 'o', 'g', 'd'},
-    /* 82 */ {'S', 'o', 'r', 'a'},
-    /* 83 */ {'S', 'o', 'y', 'o'},
-    /* 84 */ {'S', 'y', 'r', 'c'},
-    /* 85 */ {'T', 'a', 'l', 'e'},
-    /* 86 */ {'T', 'a', 'l', 'u'},
-    /* 87 */ {'T', 'a', 'm', 'l'},
-    /* 88 */ {'T', 'a', 'n', 'g'},
-    /* 89 */ {'T', 'a', 'v', 't'},
-    /* 90 */ {'T', 'e', 'l', 'u'},
-    /* 91 */ {'T', 'f', 'n', 'g'},
-    /* 92 */ {'T', 'h', 'a', 'a'},
-    /* 93 */ {'T', 'h', 'a', 'i'},
-    /* 94 */ {'T', 'i', 'b', 't'},
-    /* 95 */ {'T', 'n', 's', 'a'},
-    /* 96 */ {'T', 'o', 't', 'o'},
-    /* 97 */ {'U', 'g', 'a', 'r'},
-    /* 98 */ {'V', 'a', 'i', 'i'},
-    /* 99 */ {'W', 'c', 'h', 'o'},
-    /* 100 */ {'X', 'p', 'e', 'o'},
-    /* 101 */ {'X', 's', 'u', 'x'},
-    /* 102 */ {'Y', 'i', 'i', 'i'},
-    /* 103 */ {'~', '~', '~', 'A'},
-    /* 104 */ {'~', '~', '~', 'B'},
-};
-
-
-const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({
-    {0x61610000u, 46u}, // aa -> Latn
-    {0xA0000000u, 46u}, // aai -> Latn
-    {0xA8000000u, 46u}, // aak -> Latn
-    {0xD0000000u, 46u}, // aau -> Latn
-    {0x61620000u, 18u}, // ab -> Cyrl
-    {0xA0200000u, 46u}, // abi -> Latn
-    {0xC0200000u, 18u}, // abq -> Cyrl
-    {0xC4200000u, 46u}, // abr -> Latn
-    {0xCC200000u, 46u}, // abt -> Latn
-    {0xE0200000u, 46u}, // aby -> Latn
-    {0x8C400000u, 46u}, // acd -> Latn
-    {0x90400000u, 46u}, // ace -> Latn
-    {0x9C400000u, 46u}, // ach -> Latn
-    {0x80600000u, 46u}, // ada -> Latn
-    {0x90600000u, 46u}, // ade -> Latn
-    {0xA4600000u, 46u}, // adj -> Latn
-    {0xBC600000u, 94u}, // adp -> Tibt
-    {0xE0600000u, 18u}, // ady -> Cyrl
-    {0xE4600000u, 46u}, // adz -> Latn
-    {0x61650000u,  5u}, // ae -> Avst
-    {0x84800000u,  2u}, // aeb -> Arab
-    {0xE0800000u, 46u}, // aey -> Latn
-    {0x61660000u, 46u}, // af -> Latn
-    {0x88C00000u, 46u}, // agc -> Latn
-    {0x8CC00000u, 46u}, // agd -> Latn
-    {0x98C00000u, 46u}, // agg -> Latn
-    {0xB0C00000u, 46u}, // agm -> Latn
-    {0xB8C00000u, 46u}, // ago -> Latn
-    {0xC0C00000u, 46u}, // agq -> Latn
-    {0x80E00000u, 46u}, // aha -> Latn
-    {0xACE00000u, 46u}, // ahl -> Latn
-    {0xB8E00000u,  1u}, // aho -> Ahom
-    {0x99200000u, 46u}, // ajg -> Latn
-    {0xCD200000u,  2u}, // ajt -> Arab
-    {0x616B0000u, 46u}, // ak -> Latn
-    {0xA9400000u, 101u}, // akk -> Xsux
-    {0x81600000u, 46u}, // ala -> Latn
-    {0xA1600000u, 46u}, // ali -> Latn
-    {0xB5600000u, 46u}, // aln -> Latn
-    {0xCD600000u, 18u}, // alt -> Cyrl
-    {0x616D0000u, 21u}, // am -> Ethi
-    {0xB1800000u, 46u}, // amm -> Latn
-    {0xB5800000u, 46u}, // amn -> Latn
-    {0xB9800000u, 46u}, // amo -> Latn
-    {0xBD800000u, 46u}, // amp -> Latn
-    {0x616E0000u, 46u}, // an -> Latn
-    {0x89A00000u, 46u}, // anc -> Latn
-    {0xA9A00000u, 46u}, // ank -> Latn
-    {0xB5A00000u, 46u}, // ann -> Latn
-    {0xE1A00000u, 46u}, // any -> Latn
-    {0xA5C00000u, 46u}, // aoj -> Latn
-    {0xB1C00000u, 46u}, // aom -> Latn
-    {0xE5C00000u, 46u}, // aoz -> Latn
-    {0x89E00000u,  2u}, // apc -> Arab
-    {0x8DE00000u,  2u}, // apd -> Arab
-    {0x91E00000u, 46u}, // ape -> Latn
-    {0xC5E00000u, 46u}, // apr -> Latn
-    {0xC9E00000u, 46u}, // aps -> Latn
-    {0xE5E00000u, 46u}, // apz -> Latn
-    {0x61720000u,  2u}, // ar -> Arab
-    {0x61725842u, 104u}, // ar-XB -> ~~~B
-    {0x8A200000u,  3u}, // arc -> Armi
-    {0x9E200000u, 46u}, // arh -> Latn
-    {0xB6200000u, 46u}, // arn -> Latn
-    {0xBA200000u, 46u}, // aro -> Latn
-    {0xC2200000u,  2u}, // arq -> Arab
-    {0xCA200000u,  2u}, // ars -> Arab
-    {0xE2200000u,  2u}, // ary -> Arab
-    {0xE6200000u,  2u}, // arz -> Arab
-    {0x61730000u,  8u}, // as -> Beng
-    {0x82400000u, 46u}, // asa -> Latn
-    {0x92400000u, 79u}, // ase -> Sgnw
-    {0x9A400000u, 46u}, // asg -> Latn
-    {0xBA400000u, 46u}, // aso -> Latn
-    {0xCE400000u, 46u}, // ast -> Latn
-    {0x82600000u, 46u}, // ata -> Latn
-    {0x9A600000u, 46u}, // atg -> Latn
-    {0xA6600000u, 46u}, // atj -> Latn
-    {0xE2800000u, 46u}, // auy -> Latn
-    {0x61760000u, 18u}, // av -> Cyrl
-    {0xAEA00000u,  2u}, // avl -> Arab
-    {0xB6A00000u, 46u}, // avn -> Latn
-    {0xCEA00000u, 46u}, // avt -> Latn
-    {0xD2A00000u, 46u}, // avu -> Latn
-    {0x82C00000u, 19u}, // awa -> Deva
-    {0x86C00000u, 46u}, // awb -> Latn
-    {0xBAC00000u, 46u}, // awo -> Latn
-    {0xDEC00000u, 46u}, // awx -> Latn
-    {0x61790000u, 46u}, // ay -> Latn
-    {0x87000000u, 46u}, // ayb -> Latn
-    {0x617A0000u, 46u}, // az -> Latn
-    {0x617A4951u,  2u}, // az-IQ -> Arab
-    {0x617A4952u,  2u}, // az-IR -> Arab
-    {0x617A5255u, 18u}, // az-RU -> Cyrl
-    {0x62610000u, 18u}, // ba -> Cyrl
-    {0xAC010000u,  2u}, // bal -> Arab
-    {0xB4010000u, 46u}, // ban -> Latn
-    {0xBC010000u, 19u}, // bap -> Deva
-    {0xC4010000u, 46u}, // bar -> Latn
-    {0xC8010000u, 46u}, // bas -> Latn
-    {0xD4010000u, 46u}, // bav -> Latn
-    {0xDC010000u,  6u}, // bax -> Bamu
-    {0x80210000u, 46u}, // bba -> Latn
-    {0x84210000u, 46u}, // bbb -> Latn
-    {0x88210000u, 46u}, // bbc -> Latn
-    {0x8C210000u, 46u}, // bbd -> Latn
-    {0xA4210000u, 46u}, // bbj -> Latn
-    {0xBC210000u, 46u}, // bbp -> Latn
-    {0xC4210000u, 46u}, // bbr -> Latn
-    {0x94410000u, 46u}, // bcf -> Latn
-    {0x9C410000u, 46u}, // bch -> Latn
-    {0xA0410000u, 46u}, // bci -> Latn
-    {0xB0410000u, 46u}, // bcm -> Latn
-    {0xB4410000u, 46u}, // bcn -> Latn
-    {0xB8410000u, 46u}, // bco -> Latn
-    {0xC0410000u, 21u}, // bcq -> Ethi
-    {0xD0410000u, 46u}, // bcu -> Latn
-    {0x8C610000u, 46u}, // bdd -> Latn
-    {0x62650000u, 18u}, // be -> Cyrl
-    {0x94810000u, 46u}, // bef -> Latn
-    {0x9C810000u, 46u}, // beh -> Latn
-    {0xA4810000u,  2u}, // bej -> Arab
-    {0xB0810000u, 46u}, // bem -> Latn
-    {0xCC810000u, 46u}, // bet -> Latn
-    {0xD8810000u, 46u}, // bew -> Latn
-    {0xDC810000u, 46u}, // bex -> Latn
-    {0xE4810000u, 46u}, // bez -> Latn
-    {0x8CA10000u, 46u}, // bfd -> Latn
-    {0xC0A10000u, 87u}, // bfq -> Taml
-    {0xCCA10000u,  2u}, // bft -> Arab
-    {0xE0A10000u, 19u}, // bfy -> Deva
-    {0x62670000u, 18u}, // bg -> Cyrl
-    {0x88C10000u, 19u}, // bgc -> Deva
-    {0xB4C10000u,  2u}, // bgn -> Arab
-    {0xDCC10000u, 26u}, // bgx -> Grek
-    {0x84E10000u, 19u}, // bhb -> Deva
-    {0x98E10000u, 46u}, // bhg -> Latn
-    {0xA0E10000u, 19u}, // bhi -> Deva
-    {0xACE10000u, 46u}, // bhl -> Latn
-    {0xB8E10000u, 19u}, // bho -> Deva
-    {0xE0E10000u, 46u}, // bhy -> Latn
-    {0x62690000u, 46u}, // bi -> Latn
-    {0x85010000u, 46u}, // bib -> Latn
-    {0x99010000u, 46u}, // big -> Latn
-    {0xA9010000u, 46u}, // bik -> Latn
-    {0xB1010000u, 46u}, // bim -> Latn
-    {0xB5010000u, 46u}, // bin -> Latn
-    {0xB9010000u, 46u}, // bio -> Latn
-    {0xC1010000u, 46u}, // biq -> Latn
-    {0x9D210000u, 46u}, // bjh -> Latn
-    {0xA1210000u, 21u}, // bji -> Ethi
-    {0xA5210000u, 19u}, // bjj -> Deva
-    {0xB5210000u, 46u}, // bjn -> Latn
-    {0xB9210000u, 46u}, // bjo -> Latn
-    {0xC5210000u, 46u}, // bjr -> Latn
-    {0xCD210000u, 46u}, // bjt -> Latn
-    {0xE5210000u, 46u}, // bjz -> Latn
-    {0x89410000u, 46u}, // bkc -> Latn
-    {0xB1410000u, 46u}, // bkm -> Latn
-    {0xC1410000u, 46u}, // bkq -> Latn
-    {0xD1410000u, 46u}, // bku -> Latn
-    {0xD5410000u, 46u}, // bkv -> Latn
-    {0x81610000u, 46u}, // bla -> Latn
-    {0x99610000u, 46u}, // blg -> Latn
-    {0xCD610000u, 89u}, // blt -> Tavt
-    {0x626D0000u, 46u}, // bm -> Latn
-    {0x9D810000u, 46u}, // bmh -> Latn
-    {0xA9810000u, 46u}, // bmk -> Latn
-    {0xC1810000u, 46u}, // bmq -> Latn
-    {0xD1810000u, 46u}, // bmu -> Latn
-    {0x626E0000u,  8u}, // bn -> Beng
-    {0x99A10000u, 46u}, // bng -> Latn
-    {0xB1A10000u, 46u}, // bnm -> Latn
-    {0xBDA10000u, 46u}, // bnp -> Latn
-    {0x626F0000u, 94u}, // bo -> Tibt
-    {0xA5C10000u, 46u}, // boj -> Latn
-    {0xB1C10000u, 46u}, // bom -> Latn
-    {0xB5C10000u, 46u}, // bon -> Latn
-    {0xE1E10000u,  8u}, // bpy -> Beng
-    {0x8A010000u, 46u}, // bqc -> Latn
-    {0xA2010000u,  2u}, // bqi -> Arab
-    {0xBE010000u, 46u}, // bqp -> Latn
-    {0xD6010000u, 46u}, // bqv -> Latn
-    {0x62720000u, 46u}, // br -> Latn
-    {0x82210000u, 19u}, // bra -> Deva
-    {0x9E210000u,  2u}, // brh -> Arab
-    {0xDE210000u, 19u}, // brx -> Deva
-    {0xE6210000u, 46u}, // brz -> Latn
-    {0x62730000u, 46u}, // bs -> Latn
-    {0xA6410000u, 46u}, // bsj -> Latn
-    {0xC2410000u,  7u}, // bsq -> Bass
-    {0xCA410000u, 46u}, // bss -> Latn
-    {0xCE410000u, 21u}, // bst -> Ethi
-    {0xBA610000u, 46u}, // bto -> Latn
-    {0xCE610000u, 46u}, // btt -> Latn
-    {0xD6610000u, 19u}, // btv -> Deva
-    {0x82810000u, 18u}, // bua -> Cyrl
-    {0x8A810000u, 46u}, // buc -> Latn
-    {0x8E810000u, 46u}, // bud -> Latn
-    {0x9A810000u, 46u}, // bug -> Latn
-    {0xAA810000u, 46u}, // buk -> Latn
-    {0xB2810000u, 46u}, // bum -> Latn
-    {0xBA810000u, 46u}, // buo -> Latn
-    {0xCA810000u, 46u}, // bus -> Latn
-    {0xD2810000u, 46u}, // buu -> Latn
-    {0x86A10000u, 46u}, // bvb -> Latn
-    {0x8EC10000u, 46u}, // bwd -> Latn
-    {0xC6C10000u, 46u}, // bwr -> Latn
-    {0x9EE10000u, 46u}, // bxh -> Latn
-    {0x93010000u, 46u}, // bye -> Latn
-    {0xB7010000u, 21u}, // byn -> Ethi
-    {0xC7010000u, 46u}, // byr -> Latn
-    {0xCB010000u, 46u}, // bys -> Latn
-    {0xD7010000u, 46u}, // byv -> Latn
-    {0xDF010000u, 46u}, // byx -> Latn
-    {0x83210000u, 46u}, // bza -> Latn
-    {0x93210000u, 46u}, // bze -> Latn
-    {0x97210000u, 46u}, // bzf -> Latn
-    {0x9F210000u, 46u}, // bzh -> Latn
-    {0xDB210000u, 46u}, // bzw -> Latn
-    {0x63610000u, 46u}, // ca -> Latn
-    {0x8C020000u, 46u}, // cad -> Latn
-    {0xB4020000u, 46u}, // can -> Latn
-    {0xA4220000u, 46u}, // cbj -> Latn
-    {0x9C420000u, 46u}, // cch -> Latn
-    {0xBC420000u, 10u}, // ccp -> Cakm
-    {0x63650000u, 18u}, // ce -> Cyrl
-    {0x84820000u, 46u}, // ceb -> Latn
-    {0x80A20000u, 46u}, // cfa -> Latn
-    {0x98C20000u, 46u}, // cgg -> Latn
-    {0x63680000u, 46u}, // ch -> Latn
-    {0xA8E20000u, 46u}, // chk -> Latn
-    {0xB0E20000u, 18u}, // chm -> Cyrl
-    {0xB8E20000u, 46u}, // cho -> Latn
-    {0xBCE20000u, 46u}, // chp -> Latn
-    {0xC4E20000u, 14u}, // chr -> Cher
-    {0x89020000u, 46u}, // cic -> Latn
-    {0x81220000u,  2u}, // cja -> Arab
-    {0xB1220000u, 13u}, // cjm -> Cham
-    {0xD5220000u, 46u}, // cjv -> Latn
-    {0x85420000u,  2u}, // ckb -> Arab
-    {0xAD420000u, 46u}, // ckl -> Latn
-    {0xB9420000u, 46u}, // cko -> Latn
-    {0xE1420000u, 46u}, // cky -> Latn
-    {0x81620000u, 46u}, // cla -> Latn
-    {0x89620000u, 46u}, // clc -> Latn
-    {0x91820000u, 46u}, // cme -> Latn
-    {0x99820000u, 83u}, // cmg -> Soyo
-    {0x636F0000u, 46u}, // co -> Latn
-    {0xBDC20000u, 16u}, // cop -> Copt
-    {0xC9E20000u, 46u}, // cps -> Latn
-    {0x63720000u, 11u}, // cr -> Cans
-    {0x9A220000u, 46u}, // crg -> Latn
-    {0x9E220000u, 18u}, // crh -> Cyrl
-    {0xAA220000u, 11u}, // crk -> Cans
-    {0xAE220000u, 11u}, // crl -> Cans
-    {0xCA220000u, 46u}, // crs -> Latn
-    {0x63730000u, 46u}, // cs -> Latn
-    {0x86420000u, 46u}, // csb -> Latn
-    {0xDA420000u, 11u}, // csw -> Cans
-    {0x8E620000u, 69u}, // ctd -> Pauc
-    {0x63750000u, 18u}, // cu -> Cyrl
-    {0x63760000u, 18u}, // cv -> Cyrl
-    {0x63790000u, 46u}, // cy -> Latn
-    {0x64610000u, 46u}, // da -> Latn
-    {0x8C030000u, 46u}, // dad -> Latn
-    {0x94030000u, 46u}, // daf -> Latn
-    {0x98030000u, 46u}, // dag -> Latn
-    {0x9C030000u, 46u}, // dah -> Latn
-    {0xA8030000u, 46u}, // dak -> Latn
-    {0xC4030000u, 18u}, // dar -> Cyrl
-    {0xD4030000u, 46u}, // dav -> Latn
-    {0x8C230000u, 46u}, // dbd -> Latn
-    {0xC0230000u, 46u}, // dbq -> Latn
-    {0x88430000u,  2u}, // dcc -> Arab
-    {0xB4630000u, 46u}, // ddn -> Latn
-    {0x64650000u, 46u}, // de -> Latn
-    {0x8C830000u, 46u}, // ded -> Latn
-    {0xB4830000u, 46u}, // den -> Latn
-    {0x80C30000u, 46u}, // dga -> Latn
-    {0x9CC30000u, 46u}, // dgh -> Latn
-    {0xA0C30000u, 46u}, // dgi -> Latn
-    {0xACC30000u,  2u}, // dgl -> Arab
-    {0xC4C30000u, 46u}, // dgr -> Latn
-    {0xE4C30000u, 46u}, // dgz -> Latn
-    {0x81030000u, 46u}, // dia -> Latn
-    {0x91230000u, 46u}, // dje -> Latn
-    {0x95830000u, 54u}, // dmf -> Medf
-    {0xA5A30000u, 46u}, // dnj -> Latn
-    {0x85C30000u, 46u}, // dob -> Latn
-    {0xA1C30000u, 19u}, // doi -> Deva
-    {0xBDC30000u, 46u}, // dop -> Latn
-    {0xD9C30000u, 46u}, // dow -> Latn
-    {0x9E230000u, 57u}, // drh -> Mong
-    {0xA2230000u, 46u}, // dri -> Latn
-    {0xCA230000u, 21u}, // drs -> Ethi
-    {0x86430000u, 46u}, // dsb -> Latn
-    {0xB2630000u, 46u}, // dtm -> Latn
-    {0xBE630000u, 46u}, // dtp -> Latn
-    {0xCA630000u, 46u}, // dts -> Latn
-    {0xE2630000u, 19u}, // dty -> Deva
-    {0x82830000u, 46u}, // dua -> Latn
-    {0x8A830000u, 46u}, // duc -> Latn
-    {0x8E830000u, 46u}, // dud -> Latn
-    {0x9A830000u, 46u}, // dug -> Latn
-    {0x64760000u, 92u}, // dv -> Thaa
-    {0x82A30000u, 46u}, // dva -> Latn
-    {0xDAC30000u, 46u}, // dww -> Latn
-    {0xBB030000u, 46u}, // dyo -> Latn
-    {0xD3030000u, 46u}, // dyu -> Latn
-    {0x647A0000u, 94u}, // dz -> Tibt
-    {0x9B230000u, 46u}, // dzg -> Latn
-    {0xD0240000u, 46u}, // ebu -> Latn
-    {0x65650000u, 46u}, // ee -> Latn
-    {0xA0A40000u, 46u}, // efi -> Latn
-    {0xACC40000u, 46u}, // egl -> Latn
-    {0xE0C40000u, 20u}, // egy -> Egyp
-    {0x81440000u, 46u}, // eka -> Latn
-    {0xE1440000u, 36u}, // eky -> Kali
-    {0x656C0000u, 26u}, // el -> Grek
-    {0x81840000u, 46u}, // ema -> Latn
-    {0xA1840000u, 46u}, // emi -> Latn
-    {0x656E0000u, 46u}, // en -> Latn
-    {0x656E5841u, 103u}, // en-XA -> ~~~A
-    {0xB5A40000u, 46u}, // enn -> Latn
-    {0xC1A40000u, 46u}, // enq -> Latn
-    {0x656F0000u, 46u}, // eo -> Latn
-    {0xA2240000u, 46u}, // eri -> Latn
-    {0x65730000u, 46u}, // es -> Latn
-    {0x9A440000u, 24u}, // esg -> Gonm
-    {0xD2440000u, 46u}, // esu -> Latn
-    {0x65740000u, 46u}, // et -> Latn
-    {0xC6640000u, 46u}, // etr -> Latn
-    {0xCE640000u, 34u}, // ett -> Ital
-    {0xD2640000u, 46u}, // etu -> Latn
-    {0xDE640000u, 46u}, // etx -> Latn
-    {0x65750000u, 46u}, // eu -> Latn
-    {0xBAC40000u, 46u}, // ewo -> Latn
-    {0xCEE40000u, 46u}, // ext -> Latn
-    {0x83240000u, 46u}, // eza -> Latn
-    {0x66610000u,  2u}, // fa -> Arab
-    {0x80050000u, 46u}, // faa -> Latn
-    {0x84050000u, 46u}, // fab -> Latn
-    {0x98050000u, 46u}, // fag -> Latn
-    {0xA0050000u, 46u}, // fai -> Latn
-    {0xB4050000u, 46u}, // fan -> Latn
-    {0x66660000u, 46u}, // ff -> Latn
-    {0xA0A50000u, 46u}, // ffi -> Latn
-    {0xB0A50000u, 46u}, // ffm -> Latn
-    {0x66690000u, 46u}, // fi -> Latn
-    {0x81050000u,  2u}, // fia -> Arab
-    {0xAD050000u, 46u}, // fil -> Latn
-    {0xCD050000u, 46u}, // fit -> Latn
-    {0x666A0000u, 46u}, // fj -> Latn
-    {0xC5650000u, 46u}, // flr -> Latn
-    {0xBD850000u, 46u}, // fmp -> Latn
-    {0x666F0000u, 46u}, // fo -> Latn
-    {0x8DC50000u, 46u}, // fod -> Latn
-    {0xB5C50000u, 46u}, // fon -> Latn
-    {0xC5C50000u, 46u}, // for -> Latn
-    {0x91E50000u, 46u}, // fpe -> Latn
-    {0xCA050000u, 46u}, // fqs -> Latn
-    {0x66720000u, 46u}, // fr -> Latn
-    {0x8A250000u, 46u}, // frc -> Latn
-    {0xBE250000u, 46u}, // frp -> Latn
-    {0xC6250000u, 46u}, // frr -> Latn
-    {0xCA250000u, 46u}, // frs -> Latn
-    {0x86850000u,  2u}, // fub -> Arab
-    {0x8E850000u, 46u}, // fud -> Latn
-    {0x92850000u, 46u}, // fue -> Latn
-    {0x96850000u, 46u}, // fuf -> Latn
-    {0x9E850000u, 46u}, // fuh -> Latn
-    {0xC2850000u, 46u}, // fuq -> Latn
-    {0xC6850000u, 46u}, // fur -> Latn
-    {0xD6850000u, 46u}, // fuv -> Latn
-    {0xE2850000u, 46u}, // fuy -> Latn
-    {0xC6A50000u, 46u}, // fvr -> Latn
-    {0x66790000u, 46u}, // fy -> Latn
-    {0x67610000u, 46u}, // ga -> Latn
-    {0x80060000u, 46u}, // gaa -> Latn
-    {0x94060000u, 46u}, // gaf -> Latn
-    {0x98060000u, 46u}, // gag -> Latn
-    {0x9C060000u, 46u}, // gah -> Latn
-    {0xA4060000u, 46u}, // gaj -> Latn
-    {0xB0060000u, 46u}, // gam -> Latn
-    {0xB4060000u, 29u}, // gan -> Hans
-    {0xD8060000u, 46u}, // gaw -> Latn
-    {0xE0060000u, 46u}, // gay -> Latn
-    {0x80260000u, 46u}, // gba -> Latn
-    {0x94260000u, 46u}, // gbf -> Latn
-    {0xB0260000u, 19u}, // gbm -> Deva
-    {0xE0260000u, 46u}, // gby -> Latn
-    {0xE4260000u,  2u}, // gbz -> Arab
-    {0xC4460000u, 46u}, // gcr -> Latn
-    {0x67640000u, 46u}, // gd -> Latn
-    {0x90660000u, 46u}, // gde -> Latn
-    {0xB4660000u, 46u}, // gdn -> Latn
-    {0xC4660000u, 46u}, // gdr -> Latn
-    {0x84860000u, 46u}, // geb -> Latn
-    {0xA4860000u, 46u}, // gej -> Latn
-    {0xAC860000u, 46u}, // gel -> Latn
-    {0xE4860000u, 21u}, // gez -> Ethi
-    {0xA8A60000u, 46u}, // gfk -> Latn
-    {0xB4C60000u, 19u}, // ggn -> Deva
-    {0xC8E60000u, 46u}, // ghs -> Latn
-    {0xAD060000u, 46u}, // gil -> Latn
-    {0xB1060000u, 46u}, // gim -> Latn
-    {0xA9260000u,  2u}, // gjk -> Arab
-    {0xB5260000u, 46u}, // gjn -> Latn
-    {0xD1260000u,  2u}, // gju -> Arab
-    {0xB5460000u, 46u}, // gkn -> Latn
-    {0xBD460000u, 46u}, // gkp -> Latn
-    {0x676C0000u, 46u}, // gl -> Latn
-    {0xA9660000u,  2u}, // glk -> Arab
-    {0xB1860000u, 46u}, // gmm -> Latn
-    {0xD5860000u, 21u}, // gmv -> Ethi
-    {0x676E0000u, 46u}, // gn -> Latn
-    {0x8DA60000u, 46u}, // gnd -> Latn
-    {0x99A60000u, 46u}, // gng -> Latn
-    {0x8DC60000u, 46u}, // god -> Latn
-    {0x95C60000u, 21u}, // gof -> Ethi
-    {0xA1C60000u, 46u}, // goi -> Latn
-    {0xB1C60000u, 19u}, // gom -> Deva
-    {0xB5C60000u, 90u}, // gon -> Telu
-    {0xC5C60000u, 46u}, // gor -> Latn
-    {0xC9C60000u, 46u}, // gos -> Latn
-    {0xCDC60000u, 25u}, // got -> Goth
-    {0x86260000u, 46u}, // grb -> Latn
-    {0x8A260000u, 17u}, // grc -> Cprt
-    {0xCE260000u,  8u}, // grt -> Beng
-    {0xDA260000u, 46u}, // grw -> Latn
-    {0xDA460000u, 46u}, // gsw -> Latn
-    {0x67750000u, 27u}, // gu -> Gujr
-    {0x86860000u, 46u}, // gub -> Latn
-    {0x8A860000u, 46u}, // guc -> Latn
-    {0x8E860000u, 46u}, // gud -> Latn
-    {0xC6860000u, 46u}, // gur -> Latn
-    {0xDA860000u, 46u}, // guw -> Latn
-    {0xDE860000u, 46u}, // gux -> Latn
-    {0xE6860000u, 46u}, // guz -> Latn
-    {0x67760000u, 46u}, // gv -> Latn
-    {0x96A60000u, 46u}, // gvf -> Latn
-    {0xC6A60000u, 19u}, // gvr -> Deva
-    {0xCAA60000u, 46u}, // gvs -> Latn
-    {0x8AC60000u,  2u}, // gwc -> Arab
-    {0xA2C60000u, 46u}, // gwi -> Latn
-    {0xCEC60000u,  2u}, // gwt -> Arab
-    {0xA3060000u, 46u}, // gyi -> Latn
-    {0x68610000u, 46u}, // ha -> Latn
-    {0x6861434Du,  2u}, // ha-CM -> Arab
-    {0x68615344u,  2u}, // ha-SD -> Arab
-    {0x98070000u, 46u}, // hag -> Latn
-    {0xA8070000u, 29u}, // hak -> Hans
-    {0xB0070000u, 46u}, // ham -> Latn
-    {0xD8070000u, 46u}, // haw -> Latn
-    {0xE4070000u,  2u}, // haz -> Arab
-    {0x84270000u, 46u}, // hbb -> Latn
-    {0xE0670000u, 21u}, // hdy -> Ethi
-    {0x68650000u, 31u}, // he -> Hebr
-    {0xE0E70000u, 46u}, // hhy -> Latn
-    {0x68690000u, 19u}, // hi -> Deva
-    {0x81070000u, 46u}, // hia -> Latn
-    {0x95070000u, 46u}, // hif -> Latn
-    {0x99070000u, 46u}, // hig -> Latn
-    {0x9D070000u, 46u}, // hih -> Latn
-    {0xAD070000u, 46u}, // hil -> Latn
-    {0x81670000u, 46u}, // hla -> Latn
-    {0xD1670000u, 32u}, // hlu -> Hluw
-    {0x8D870000u, 72u}, // hmd -> Plrd
-    {0xCD870000u, 46u}, // hmt -> Latn
-    {0x8DA70000u,  2u}, // hnd -> Arab
-    {0x91A70000u, 19u}, // hne -> Deva
-    {0xA5A70000u, 33u}, // hnj -> Hmnp
-    {0xB5A70000u, 46u}, // hnn -> Latn
-    {0xB9A70000u,  2u}, // hno -> Arab
-    {0x686F0000u, 46u}, // ho -> Latn
-    {0x89C70000u, 19u}, // hoc -> Deva
-    {0xA5C70000u, 19u}, // hoj -> Deva
-    {0xCDC70000u, 46u}, // hot -> Latn
-    {0x68720000u, 46u}, // hr -> Latn
-    {0x86470000u, 46u}, // hsb -> Latn
-    {0xB6470000u, 29u}, // hsn -> Hans
-    {0x68740000u, 46u}, // ht -> Latn
-    {0x68750000u, 46u}, // hu -> Latn
-    {0xA2870000u, 46u}, // hui -> Latn
-    {0xC6870000u, 46u}, // hur -> Latn
-    {0x68790000u,  4u}, // hy -> Armn
-    {0x687A0000u, 46u}, // hz -> Latn
-    {0x69610000u, 46u}, // ia -> Latn
-    {0xB4080000u, 46u}, // ian -> Latn
-    {0xC4080000u, 46u}, // iar -> Latn
-    {0x80280000u, 46u}, // iba -> Latn
-    {0x84280000u, 46u}, // ibb -> Latn
-    {0xE0280000u, 46u}, // iby -> Latn
-    {0x80480000u, 46u}, // ica -> Latn
-    {0x9C480000u, 46u}, // ich -> Latn
-    {0x69640000u, 46u}, // id -> Latn
-    {0x8C680000u, 46u}, // idd -> Latn
-    {0xA0680000u, 46u}, // idi -> Latn
-    {0xD0680000u, 46u}, // idu -> Latn
-    {0x90A80000u, 46u}, // ife -> Latn
-    {0x69670000u, 46u}, // ig -> Latn
-    {0x84C80000u, 46u}, // igb -> Latn
-    {0x90C80000u, 46u}, // ige -> Latn
-    {0x69690000u, 102u}, // ii -> Yiii
-    {0xA5280000u, 46u}, // ijj -> Latn
-    {0x696B0000u, 46u}, // ik -> Latn
-    {0xA9480000u, 46u}, // ikk -> Latn
-    {0xD9480000u, 46u}, // ikw -> Latn
-    {0xDD480000u, 46u}, // ikx -> Latn
-    {0xB9680000u, 46u}, // ilo -> Latn
-    {0xB9880000u, 46u}, // imo -> Latn
-    {0x696E0000u, 46u}, // in -> Latn
-    {0x9DA80000u, 18u}, // inh -> Cyrl
-    {0x696F0000u, 46u}, // io -> Latn
-    {0xD1C80000u, 46u}, // iou -> Latn
-    {0xA2280000u, 46u}, // iri -> Latn
-    {0x69730000u, 46u}, // is -> Latn
-    {0x69740000u, 46u}, // it -> Latn
-    {0x69750000u, 11u}, // iu -> Cans
-    {0x69770000u, 31u}, // iw -> Hebr
-    {0xB2C80000u, 46u}, // iwm -> Latn
-    {0xCAC80000u, 46u}, // iws -> Latn
-    {0x9F280000u, 46u}, // izh -> Latn
-    {0xA3280000u, 46u}, // izi -> Latn
-    {0x6A610000u, 35u}, // ja -> Jpan
-    {0x84090000u, 46u}, // jab -> Latn
-    {0xB0090000u, 46u}, // jam -> Latn
-    {0xC4090000u, 46u}, // jar -> Latn
-    {0xB8290000u, 46u}, // jbo -> Latn
-    {0xD0290000u, 46u}, // jbu -> Latn
-    {0xB4890000u, 46u}, // jen -> Latn
-    {0xA8C90000u, 46u}, // jgk -> Latn
-    {0xB8C90000u, 46u}, // jgo -> Latn
-    {0x6A690000u, 31u}, // ji -> Hebr
-    {0x85090000u, 46u}, // jib -> Latn
-    {0x89890000u, 46u}, // jmc -> Latn
-    {0xAD890000u, 19u}, // jml -> Deva
-    {0x82290000u, 46u}, // jra -> Latn
-    {0xCE890000u, 46u}, // jut -> Latn
-    {0x6A760000u, 46u}, // jv -> Latn
-    {0x6A770000u, 46u}, // jw -> Latn
-    {0x6B610000u, 22u}, // ka -> Geor
-    {0x800A0000u, 18u}, // kaa -> Cyrl
-    {0x840A0000u, 46u}, // kab -> Latn
-    {0x880A0000u, 46u}, // kac -> Latn
-    {0x8C0A0000u, 46u}, // kad -> Latn
-    {0xA00A0000u, 46u}, // kai -> Latn
-    {0xA40A0000u, 46u}, // kaj -> Latn
-    {0xB00A0000u, 46u}, // kam -> Latn
-    {0xB80A0000u, 46u}, // kao -> Latn
-    {0xD80A0000u, 38u}, // kaw -> Kawi
-    {0x8C2A0000u, 18u}, // kbd -> Cyrl
-    {0xB02A0000u, 46u}, // kbm -> Latn
-    {0xBC2A0000u, 46u}, // kbp -> Latn
-    {0xC02A0000u, 46u}, // kbq -> Latn
-    {0xDC2A0000u, 46u}, // kbx -> Latn
-    {0xE02A0000u,  2u}, // kby -> Arab
-    {0x984A0000u, 46u}, // kcg -> Latn
-    {0xA84A0000u, 46u}, // kck -> Latn
-    {0xAC4A0000u, 46u}, // kcl -> Latn
-    {0xCC4A0000u, 46u}, // kct -> Latn
-    {0x906A0000u, 46u}, // kde -> Latn
-    {0x9C6A0000u, 46u}, // kdh -> Latn
-    {0xAC6A0000u, 46u}, // kdl -> Latn
-    {0xCC6A0000u, 93u}, // kdt -> Thai
-    {0x808A0000u, 46u}, // kea -> Latn
-    {0xB48A0000u, 46u}, // ken -> Latn
-    {0xE48A0000u, 46u}, // kez -> Latn
-    {0xB8AA0000u, 46u}, // kfo -> Latn
-    {0xC4AA0000u, 19u}, // kfr -> Deva
-    {0xE0AA0000u, 19u}, // kfy -> Deva
-    {0x6B670000u, 46u}, // kg -> Latn
-    {0x90CA0000u, 46u}, // kge -> Latn
-    {0x94CA0000u, 46u}, // kgf -> Latn
-    {0xBCCA0000u, 46u}, // kgp -> Latn
-    {0x80EA0000u, 46u}, // kha -> Latn
-    {0x84EA0000u, 86u}, // khb -> Talu
-    {0xB4EA0000u, 19u}, // khn -> Deva
-    {0xC0EA0000u, 46u}, // khq -> Latn
-    {0xC8EA0000u, 46u}, // khs -> Latn
-    {0xCCEA0000u, 59u}, // kht -> Mymr
-    {0xD8EA0000u,  2u}, // khw -> Arab
-    {0xE4EA0000u, 46u}, // khz -> Latn
-    {0x6B690000u, 46u}, // ki -> Latn
-    {0xA50A0000u, 46u}, // kij -> Latn
-    {0xD10A0000u, 46u}, // kiu -> Latn
-    {0xD90A0000u, 46u}, // kiw -> Latn
-    {0x6B6A0000u, 46u}, // kj -> Latn
-    {0x8D2A0000u, 46u}, // kjd -> Latn
-    {0x992A0000u, 45u}, // kjg -> Laoo
-    {0xC92A0000u, 46u}, // kjs -> Latn
-    {0xE12A0000u, 46u}, // kjy -> Latn
-    {0x6B6B0000u, 18u}, // kk -> Cyrl
-    {0x6B6B4146u,  2u}, // kk-AF -> Arab
-    {0x6B6B434Eu,  2u}, // kk-CN -> Arab
-    {0x6B6B4952u,  2u}, // kk-IR -> Arab
-    {0x6B6B4D4Eu,  2u}, // kk-MN -> Arab
-    {0x894A0000u, 46u}, // kkc -> Latn
-    {0xA54A0000u, 46u}, // kkj -> Latn
-    {0x6B6C0000u, 46u}, // kl -> Latn
-    {0xB56A0000u, 46u}, // kln -> Latn
-    {0xC16A0000u, 46u}, // klq -> Latn
-    {0xCD6A0000u, 46u}, // klt -> Latn
-    {0xDD6A0000u, 46u}, // klx -> Latn
-    {0x6B6D0000u, 40u}, // km -> Khmr
-    {0x858A0000u, 46u}, // kmb -> Latn
-    {0x9D8A0000u, 46u}, // kmh -> Latn
-    {0xB98A0000u, 46u}, // kmo -> Latn
-    {0xC98A0000u, 46u}, // kms -> Latn
-    {0xD18A0000u, 46u}, // kmu -> Latn
-    {0xD98A0000u, 46u}, // kmw -> Latn
-    {0x6B6E0000u, 42u}, // kn -> Knda
-    {0x95AA0000u, 46u}, // knf -> Latn
-    {0xBDAA0000u, 46u}, // knp -> Latn
-    {0x6B6F0000u, 43u}, // ko -> Kore
-    {0xA1CA0000u, 18u}, // koi -> Cyrl
-    {0xA9CA0000u, 19u}, // kok -> Deva
-    {0xADCA0000u, 46u}, // kol -> Latn
-    {0xC9CA0000u, 46u}, // kos -> Latn
-    {0xE5CA0000u, 46u}, // koz -> Latn
-    {0x91EA0000u, 46u}, // kpe -> Latn
-    {0x95EA0000u, 46u}, // kpf -> Latn
-    {0xB9EA0000u, 46u}, // kpo -> Latn
-    {0xC5EA0000u, 46u}, // kpr -> Latn
-    {0xDDEA0000u, 46u}, // kpx -> Latn
-    {0x860A0000u, 46u}, // kqb -> Latn
-    {0x960A0000u, 46u}, // kqf -> Latn
-    {0xCA0A0000u, 46u}, // kqs -> Latn
-    {0xE20A0000u, 21u}, // kqy -> Ethi
-    {0x6B720000u, 46u}, // kr -> Latn
-    {0x8A2A0000u, 18u}, // krc -> Cyrl
-    {0xA22A0000u, 46u}, // kri -> Latn
-    {0xA62A0000u, 46u}, // krj -> Latn
-    {0xAE2A0000u, 46u}, // krl -> Latn
-    {0xCA2A0000u, 46u}, // krs -> Latn
-    {0xD22A0000u, 19u}, // kru -> Deva
-    {0x6B730000u,  2u}, // ks -> Arab
-    {0x864A0000u, 46u}, // ksb -> Latn
-    {0x8E4A0000u, 46u}, // ksd -> Latn
-    {0x964A0000u, 46u}, // ksf -> Latn
-    {0x9E4A0000u, 46u}, // ksh -> Latn
-    {0xA64A0000u, 46u}, // ksj -> Latn
-    {0xC64A0000u, 46u}, // ksr -> Latn
-    {0x866A0000u, 21u}, // ktb -> Ethi
-    {0xB26A0000u, 46u}, // ktm -> Latn
-    {0xBA6A0000u, 46u}, // kto -> Latn
-    {0xC66A0000u, 46u}, // ktr -> Latn
-    {0x6B750000u, 46u}, // ku -> Latn
-    {0x6B754952u,  2u}, // ku-IR -> Arab
-    {0x6B754C42u,  2u}, // ku-LB -> Arab
-    {0x868A0000u, 46u}, // kub -> Latn
-    {0x8E8A0000u, 46u}, // kud -> Latn
-    {0x928A0000u, 46u}, // kue -> Latn
-    {0xA68A0000u, 46u}, // kuj -> Latn
-    {0xB28A0000u, 18u}, // kum -> Cyrl
-    {0xB68A0000u, 46u}, // kun -> Latn
-    {0xBE8A0000u, 46u}, // kup -> Latn
-    {0xCA8A0000u, 46u}, // kus -> Latn
-    {0x6B760000u, 18u}, // kv -> Cyrl
-    {0x9AAA0000u, 46u}, // kvg -> Latn
-    {0xC6AA0000u, 46u}, // kvr -> Latn
-    {0xDEAA0000u,  2u}, // kvx -> Arab
-    {0x6B770000u, 46u}, // kw -> Latn
-    {0xA6CA0000u, 46u}, // kwj -> Latn
-    {0xAACA0000u, 46u}, // kwk -> Latn
-    {0xBACA0000u, 46u}, // kwo -> Latn
-    {0xC2CA0000u, 46u}, // kwq -> Latn
-    {0x82EA0000u, 46u}, // kxa -> Latn
-    {0x8AEA0000u, 21u}, // kxc -> Ethi
-    {0x92EA0000u, 46u}, // kxe -> Latn
-    {0xAEEA0000u, 19u}, // kxl -> Deva
-    {0xB2EA0000u, 93u}, // kxm -> Thai
-    {0xBEEA0000u,  2u}, // kxp -> Arab
-    {0xDAEA0000u, 46u}, // kxw -> Latn
-    {0xE6EA0000u, 46u}, // kxz -> Latn
-    {0x6B790000u, 18u}, // ky -> Cyrl
-    {0x6B79434Eu,  2u}, // ky-CN -> Arab
-    {0x6B795452u, 46u}, // ky-TR -> Latn
-    {0x930A0000u, 46u}, // kye -> Latn
-    {0xDF0A0000u, 46u}, // kyx -> Latn
-    {0x9F2A0000u,  2u}, // kzh -> Arab
-    {0xA72A0000u, 46u}, // kzj -> Latn
-    {0xC72A0000u, 46u}, // kzr -> Latn
-    {0xCF2A0000u, 46u}, // kzt -> Latn
-    {0x6C610000u, 46u}, // la -> Latn
-    {0x840B0000u, 48u}, // lab -> Lina
-    {0x8C0B0000u, 31u}, // lad -> Hebr
-    {0x980B0000u, 46u}, // lag -> Latn
-    {0x9C0B0000u,  2u}, // lah -> Arab
-    {0xA40B0000u, 46u}, // laj -> Latn
-    {0xC80B0000u, 46u}, // las -> Latn
-    {0x6C620000u, 46u}, // lb -> Latn
-    {0x902B0000u, 18u}, // lbe -> Cyrl
-    {0xD02B0000u, 46u}, // lbu -> Latn
-    {0xD82B0000u, 46u}, // lbw -> Latn
-    {0xB04B0000u, 46u}, // lcm -> Latn
-    {0xBC4B0000u, 93u}, // lcp -> Thai
-    {0x846B0000u, 46u}, // ldb -> Latn
-    {0x8C8B0000u, 46u}, // led -> Latn
-    {0x908B0000u, 46u}, // lee -> Latn
-    {0xB08B0000u, 46u}, // lem -> Latn
-    {0xBC8B0000u, 47u}, // lep -> Lepc
-    {0xC08B0000u, 46u}, // leq -> Latn
-    {0xD08B0000u, 46u}, // leu -> Latn
-    {0xE48B0000u, 18u}, // lez -> Cyrl
-    {0x6C670000u, 46u}, // lg -> Latn
-    {0x98CB0000u, 46u}, // lgg -> Latn
-    {0x6C690000u, 46u}, // li -> Latn
-    {0x810B0000u, 46u}, // lia -> Latn
-    {0x8D0B0000u, 46u}, // lid -> Latn
-    {0x950B0000u, 19u}, // lif -> Deva
-    {0x990B0000u, 46u}, // lig -> Latn
-    {0x9D0B0000u, 46u}, // lih -> Latn
-    {0xA50B0000u, 46u}, // lij -> Latn
-    {0xAD0B0000u, 46u}, // lil -> Latn
-    {0xC90B0000u, 49u}, // lis -> Lisu
-    {0xBD2B0000u, 46u}, // ljp -> Latn
-    {0xA14B0000u,  2u}, // lki -> Arab
-    {0xCD4B0000u, 46u}, // lkt -> Latn
-    {0x916B0000u, 46u}, // lle -> Latn
-    {0xB56B0000u, 46u}, // lln -> Latn
-    {0xB58B0000u, 90u}, // lmn -> Telu
-    {0xB98B0000u, 46u}, // lmo -> Latn
-    {0xBD8B0000u, 46u}, // lmp -> Latn
-    {0x6C6E0000u, 46u}, // ln -> Latn
-    {0xC9AB0000u, 46u}, // lns -> Latn
-    {0xD1AB0000u, 46u}, // lnu -> Latn
-    {0x6C6F0000u, 45u}, // lo -> Laoo
-    {0xA5CB0000u, 46u}, // loj -> Latn
-    {0xA9CB0000u, 46u}, // lok -> Latn
-    {0xADCB0000u, 46u}, // lol -> Latn
-    {0xC5CB0000u, 46u}, // lor -> Latn
-    {0xC9CB0000u, 46u}, // los -> Latn
-    {0xE5CB0000u, 46u}, // loz -> Latn
-    {0x8A2B0000u,  2u}, // lrc -> Arab
-    {0x6C740000u, 46u}, // lt -> Latn
-    {0x9A6B0000u, 46u}, // ltg -> Latn
-    {0x6C750000u, 46u}, // lu -> Latn
-    {0x828B0000u, 46u}, // lua -> Latn
-    {0xBA8B0000u, 46u}, // luo -> Latn
-    {0xE28B0000u, 46u}, // luy -> Latn
-    {0xE68B0000u,  2u}, // luz -> Arab
-    {0x6C760000u, 46u}, // lv -> Latn
-    {0xAECB0000u, 93u}, // lwl -> Thai
-    {0x9F2B0000u, 29u}, // lzh -> Hans
-    {0xE72B0000u, 46u}, // lzz -> Latn
-    {0x8C0C0000u, 46u}, // mad -> Latn
-    {0x940C0000u, 46u}, // maf -> Latn
-    {0x980C0000u, 19u}, // mag -> Deva
-    {0xA00C0000u, 19u}, // mai -> Deva
-    {0xA80C0000u, 46u}, // mak -> Latn
-    {0xB40C0000u, 46u}, // man -> Latn
-    {0xB40C474Eu, 61u}, // man-GN -> Nkoo
-    {0xC80C0000u, 46u}, // mas -> Latn
-    {0xD80C0000u, 46u}, // maw -> Latn
-    {0xE40C0000u, 46u}, // maz -> Latn
-    {0x9C2C0000u, 46u}, // mbh -> Latn
-    {0xB82C0000u, 46u}, // mbo -> Latn
-    {0xC02C0000u, 46u}, // mbq -> Latn
-    {0xD02C0000u, 46u}, // mbu -> Latn
-    {0xD82C0000u, 46u}, // mbw -> Latn
-    {0xA04C0000u, 46u}, // mci -> Latn
-    {0xBC4C0000u, 46u}, // mcp -> Latn
-    {0xC04C0000u, 46u}, // mcq -> Latn
-    {0xC44C0000u, 46u}, // mcr -> Latn
-    {0xD04C0000u, 46u}, // mcu -> Latn
-    {0x806C0000u, 46u}, // mda -> Latn
-    {0x906C0000u,  2u}, // mde -> Arab
-    {0x946C0000u, 18u}, // mdf -> Cyrl
-    {0x9C6C0000u, 46u}, // mdh -> Latn
-    {0xA46C0000u, 46u}, // mdj -> Latn
-    {0xC46C0000u, 46u}, // mdr -> Latn
-    {0xDC6C0000u, 21u}, // mdx -> Ethi
-    {0x8C8C0000u, 46u}, // med -> Latn
-    {0x908C0000u, 46u}, // mee -> Latn
-    {0xA88C0000u, 46u}, // mek -> Latn
-    {0xB48C0000u, 46u}, // men -> Latn
-    {0xC48C0000u, 46u}, // mer -> Latn
-    {0xCC8C0000u, 46u}, // met -> Latn
-    {0xD08C0000u, 46u}, // meu -> Latn
-    {0x80AC0000u,  2u}, // mfa -> Arab
-    {0x90AC0000u, 46u}, // mfe -> Latn
-    {0xB4AC0000u, 46u}, // mfn -> Latn
-    {0xB8AC0000u, 46u}, // mfo -> Latn
-    {0xC0AC0000u, 46u}, // mfq -> Latn
-    {0x6D670000u, 46u}, // mg -> Latn
-    {0x9CCC0000u, 46u}, // mgh -> Latn
-    {0xACCC0000u, 46u}, // mgl -> Latn
-    {0xB8CC0000u, 46u}, // mgo -> Latn
-    {0xBCCC0000u, 19u}, // mgp -> Deva
-    {0xE0CC0000u, 46u}, // mgy -> Latn
-    {0x6D680000u, 46u}, // mh -> Latn
-    {0xA0EC0000u, 46u}, // mhi -> Latn
-    {0xACEC0000u, 46u}, // mhl -> Latn
-    {0x6D690000u, 46u}, // mi -> Latn
-    {0x890C0000u, 46u}, // mic -> Latn
-    {0x950C0000u, 46u}, // mif -> Latn
-    {0xB50C0000u, 46u}, // min -> Latn
-    {0xD90C0000u, 46u}, // miw -> Latn
-    {0x6D6B0000u, 18u}, // mk -> Cyrl
-    {0xA14C0000u,  2u}, // mki -> Arab
-    {0xAD4C0000u, 46u}, // mkl -> Latn
-    {0xBD4C0000u, 46u}, // mkp -> Latn
-    {0xD94C0000u, 46u}, // mkw -> Latn
-    {0x6D6C0000u, 56u}, // ml -> Mlym
-    {0x916C0000u, 46u}, // mle -> Latn
-    {0xBD6C0000u, 46u}, // mlp -> Latn
-    {0xC96C0000u, 46u}, // mls -> Latn
-    {0xB98C0000u, 46u}, // mmo -> Latn
-    {0xD18C0000u, 46u}, // mmu -> Latn
-    {0xDD8C0000u, 46u}, // mmx -> Latn
-    {0x6D6E0000u, 18u}, // mn -> Cyrl
-    {0x6D6E434Eu, 57u}, // mn-CN -> Mong
-    {0x81AC0000u, 46u}, // mna -> Latn
-    {0x95AC0000u, 46u}, // mnf -> Latn
-    {0xA1AC0000u,  8u}, // mni -> Beng
-    {0xD9AC0000u, 59u}, // mnw -> Mymr
-    {0x6D6F0000u, 46u}, // mo -> Latn
-    {0x81CC0000u, 46u}, // moa -> Latn
-    {0x91CC0000u, 46u}, // moe -> Latn
-    {0x9DCC0000u, 46u}, // moh -> Latn
-    {0xC9CC0000u, 46u}, // mos -> Latn
-    {0xDDCC0000u, 46u}, // mox -> Latn
-    {0xBDEC0000u, 46u}, // mpp -> Latn
-    {0xC9EC0000u, 46u}, // mps -> Latn
-    {0xCDEC0000u, 46u}, // mpt -> Latn
-    {0xDDEC0000u, 46u}, // mpx -> Latn
-    {0xAE0C0000u, 46u}, // mql -> Latn
-    {0x6D720000u, 19u}, // mr -> Deva
-    {0x8E2C0000u, 19u}, // mrd -> Deva
-    {0xA62C0000u, 18u}, // mrj -> Cyrl
-    {0xBA2C0000u, 58u}, // mro -> Mroo
-    {0x6D730000u, 46u}, // ms -> Latn
-    {0x6D734343u,  2u}, // ms-CC -> Arab
-    {0x6D740000u, 46u}, // mt -> Latn
-    {0x8A6C0000u, 46u}, // mtc -> Latn
-    {0x966C0000u, 46u}, // mtf -> Latn
-    {0xA26C0000u, 46u}, // mti -> Latn
-    {0xC66C0000u, 19u}, // mtr -> Deva
-    {0x828C0000u, 46u}, // mua -> Latn
-    {0xC68C0000u, 46u}, // mur -> Latn
-    {0xCA8C0000u, 46u}, // mus -> Latn
-    {0x82AC0000u, 46u}, // mva -> Latn
-    {0xB6AC0000u, 46u}, // mvn -> Latn
-    {0xE2AC0000u,  2u}, // mvy -> Arab
-    {0xAACC0000u, 46u}, // mwk -> Latn
-    {0xC6CC0000u, 19u}, // mwr -> Deva
-    {0xD6CC0000u, 46u}, // mwv -> Latn
-    {0xDACC0000u, 33u}, // mww -> Hmnp
-    {0x8AEC0000u, 46u}, // mxc -> Latn
-    {0xB2EC0000u, 46u}, // mxm -> Latn
-    {0x6D790000u, 59u}, // my -> Mymr
-    {0xAB0C0000u, 46u}, // myk -> Latn
-    {0xB30C0000u, 21u}, // mym -> Ethi
-    {0xD70C0000u, 18u}, // myv -> Cyrl
-    {0xDB0C0000u, 46u}, // myw -> Latn
-    {0xDF0C0000u, 46u}, // myx -> Latn
-    {0xE70C0000u, 52u}, // myz -> Mand
-    {0xAB2C0000u, 46u}, // mzk -> Latn
-    {0xB32C0000u, 46u}, // mzm -> Latn
-    {0xB72C0000u,  2u}, // mzn -> Arab
-    {0xBF2C0000u, 46u}, // mzp -> Latn
-    {0xDB2C0000u, 46u}, // mzw -> Latn
-    {0xE72C0000u, 46u}, // mzz -> Latn
-    {0x6E610000u, 46u}, // na -> Latn
-    {0x880D0000u, 46u}, // nac -> Latn
-    {0x940D0000u, 46u}, // naf -> Latn
-    {0xA80D0000u, 46u}, // nak -> Latn
-    {0xB40D0000u, 29u}, // nan -> Hans
-    {0xBC0D0000u, 46u}, // nap -> Latn
-    {0xC00D0000u, 46u}, // naq -> Latn
-    {0xC80D0000u, 46u}, // nas -> Latn
-    {0x6E620000u, 46u}, // nb -> Latn
-    {0x804D0000u, 46u}, // nca -> Latn
-    {0x904D0000u, 46u}, // nce -> Latn
-    {0x944D0000u, 46u}, // ncf -> Latn
-    {0x9C4D0000u, 46u}, // nch -> Latn
-    {0xB84D0000u, 46u}, // nco -> Latn
-    {0xD04D0000u, 46u}, // ncu -> Latn
-    {0x6E640000u, 46u}, // nd -> Latn
-    {0x886D0000u, 46u}, // ndc -> Latn
-    {0xC86D0000u, 46u}, // nds -> Latn
-    {0x6E650000u, 19u}, // ne -> Deva
-    {0x848D0000u, 46u}, // neb -> Latn
-    {0xD88D0000u, 19u}, // new -> Deva
-    {0xDC8D0000u, 46u}, // nex -> Latn
-    {0xC4AD0000u, 46u}, // nfr -> Latn
-    {0x6E670000u, 46u}, // ng -> Latn
-    {0x80CD0000u, 46u}, // nga -> Latn
-    {0x84CD0000u, 46u}, // ngb -> Latn
-    {0xACCD0000u, 46u}, // ngl -> Latn
-    {0x84ED0000u, 46u}, // nhb -> Latn
-    {0x90ED0000u, 46u}, // nhe -> Latn
-    {0xD8ED0000u, 46u}, // nhw -> Latn
-    {0x950D0000u, 46u}, // nif -> Latn
-    {0xA10D0000u, 46u}, // nii -> Latn
-    {0xA50D0000u, 46u}, // nij -> Latn
-    {0xB50D0000u, 46u}, // nin -> Latn
-    {0xD10D0000u, 46u}, // niu -> Latn
-    {0xE10D0000u, 46u}, // niy -> Latn
-    {0xE50D0000u, 46u}, // niz -> Latn
-    {0xB92D0000u, 46u}, // njo -> Latn
-    {0x994D0000u, 46u}, // nkg -> Latn
-    {0xB94D0000u, 46u}, // nko -> Latn
-    {0x6E6C0000u, 46u}, // nl -> Latn
-    {0x998D0000u, 46u}, // nmg -> Latn
-    {0xE58D0000u, 46u}, // nmz -> Latn
-    {0x6E6E0000u, 46u}, // nn -> Latn
-    {0x95AD0000u, 46u}, // nnf -> Latn
-    {0x9DAD0000u, 46u}, // nnh -> Latn
-    {0xA9AD0000u, 46u}, // nnk -> Latn
-    {0xB1AD0000u, 46u}, // nnm -> Latn
-    {0xBDAD0000u, 99u}, // nnp -> Wcho
-    {0x6E6F0000u, 46u}, // no -> Latn
-    {0x8DCD0000u, 44u}, // nod -> Lana
-    {0x91CD0000u, 19u}, // noe -> Deva
-    {0xB5CD0000u, 75u}, // non -> Runr
-    {0xBDCD0000u, 46u}, // nop -> Latn
-    {0xD1CD0000u, 46u}, // nou -> Latn
-    {0xBA0D0000u, 61u}, // nqo -> Nkoo
-    {0x6E720000u, 46u}, // nr -> Latn
-    {0x862D0000u, 46u}, // nrb -> Latn
-    {0xAA4D0000u, 11u}, // nsk -> Cans
-    {0xB64D0000u, 46u}, // nsn -> Latn
-    {0xBA4D0000u, 46u}, // nso -> Latn
-    {0xCA4D0000u, 46u}, // nss -> Latn
-    {0xCE4D0000u, 95u}, // nst -> Tnsa
-    {0xB26D0000u, 46u}, // ntm -> Latn
-    {0xC66D0000u, 46u}, // ntr -> Latn
-    {0xA28D0000u, 46u}, // nui -> Latn
-    {0xBE8D0000u, 46u}, // nup -> Latn
-    {0xCA8D0000u, 46u}, // nus -> Latn
-    {0xD68D0000u, 46u}, // nuv -> Latn
-    {0xDE8D0000u, 46u}, // nux -> Latn
-    {0x6E760000u, 46u}, // nv -> Latn
-    {0x86CD0000u, 46u}, // nwb -> Latn
-    {0xC2ED0000u, 46u}, // nxq -> Latn
-    {0xC6ED0000u, 46u}, // nxr -> Latn
-    {0x6E790000u, 46u}, // ny -> Latn
-    {0xB30D0000u, 46u}, // nym -> Latn
-    {0xB70D0000u, 46u}, // nyn -> Latn
-    {0xA32D0000u, 46u}, // nzi -> Latn
-    {0x6F630000u, 46u}, // oc -> Latn
-    {0x6F634553u, 46u}, // oc-ES -> Latn
-    {0x88CE0000u, 46u}, // ogc -> Latn
-    {0x6F6A0000u, 11u}, // oj -> Cans
-    {0xC92E0000u, 11u}, // ojs -> Cans
-    {0x814E0000u, 46u}, // oka -> Latn
-    {0xC54E0000u, 46u}, // okr -> Latn
-    {0xD54E0000u, 46u}, // okv -> Latn
-    {0x6F6D0000u, 46u}, // om -> Latn
-    {0x99AE0000u, 46u}, // ong -> Latn
-    {0xB5AE0000u, 46u}, // onn -> Latn
-    {0xC9AE0000u, 46u}, // ons -> Latn
-    {0xB1EE0000u, 46u}, // opm -> Latn
-    {0x6F720000u, 66u}, // or -> Orya
-    {0xBA2E0000u, 46u}, // oro -> Latn
-    {0xD22E0000u,  2u}, // oru -> Arab
-    {0x6F730000u, 18u}, // os -> Cyrl
-    {0x824E0000u, 67u}, // osa -> Osge
-    {0x826E0000u,  2u}, // ota -> Arab
-    {0xAA6E0000u, 65u}, // otk -> Orkh
-    {0xA28E0000u, 68u}, // oui -> Ougr
-    {0xB32E0000u, 46u}, // ozm -> Latn
-    {0x70610000u, 28u}, // pa -> Guru
-    {0x7061504Bu,  2u}, // pa-PK -> Arab
-    {0x980F0000u, 46u}, // pag -> Latn
-    {0xAC0F0000u, 70u}, // pal -> Phli
-    {0xB00F0000u, 46u}, // pam -> Latn
-    {0xBC0F0000u, 46u}, // pap -> Latn
-    {0xD00F0000u, 46u}, // pau -> Latn
-    {0xA02F0000u, 46u}, // pbi -> Latn
-    {0x8C4F0000u, 46u}, // pcd -> Latn
-    {0xB04F0000u, 46u}, // pcm -> Latn
-    {0x886F0000u, 46u}, // pdc -> Latn
-    {0xCC6F0000u, 46u}, // pdt -> Latn
-    {0x8C8F0000u, 46u}, // ped -> Latn
-    {0xB88F0000u, 100u}, // peo -> Xpeo
-    {0xDC8F0000u, 46u}, // pex -> Latn
-    {0xACAF0000u, 46u}, // pfl -> Latn
-    {0xACEF0000u,  2u}, // phl -> Arab
-    {0xB4EF0000u, 71u}, // phn -> Phnx
-    {0xAD0F0000u, 46u}, // pil -> Latn
-    {0xBD0F0000u, 46u}, // pip -> Latn
-    {0xC90F0000u, 46u}, // pis -> Latn
-    {0x814F0000u,  9u}, // pka -> Brah
-    {0xB94F0000u, 46u}, // pko -> Latn
-    {0x706C0000u, 46u}, // pl -> Latn
-    {0x816F0000u, 46u}, // pla -> Latn
-    {0xC98F0000u, 46u}, // pms -> Latn
-    {0x99AF0000u, 46u}, // png -> Latn
-    {0xB5AF0000u, 46u}, // pnn -> Latn
-    {0xCDAF0000u, 26u}, // pnt -> Grek
-    {0xB5CF0000u, 46u}, // pon -> Latn
-    {0x81EF0000u, 19u}, // ppa -> Deva
-    {0xB9EF0000u, 46u}, // ppo -> Latn
-    {0xB20F0000u, 46u}, // pqm -> Latn
-    {0x822F0000u, 39u}, // pra -> Khar
-    {0x8E2F0000u,  2u}, // prd -> Arab
-    {0x9A2F0000u, 46u}, // prg -> Latn
-    {0x70730000u,  2u}, // ps -> Arab
-    {0xCA4F0000u, 46u}, // pss -> Latn
-    {0x70740000u, 46u}, // pt -> Latn
-    {0xBE6F0000u, 46u}, // ptp -> Latn
-    {0xD28F0000u, 46u}, // puu -> Latn
-    {0x82CF0000u, 46u}, // pwa -> Latn
-    {0x71750000u, 46u}, // qu -> Latn
-    {0x8A900000u, 46u}, // quc -> Latn
-    {0x9A900000u, 46u}, // qug -> Latn
-    {0xA0110000u, 46u}, // rai -> Latn
-    {0xA4110000u, 19u}, // raj -> Deva
-    {0xB8110000u, 46u}, // rao -> Latn
-    {0x94510000u, 46u}, // rcf -> Latn
-    {0xA4910000u, 46u}, // rej -> Latn
-    {0xAC910000u, 46u}, // rel -> Latn
-    {0xC8910000u, 46u}, // res -> Latn
-    {0xB4D10000u, 46u}, // rgn -> Latn
-    {0x98F10000u, 74u}, // rhg -> Rohg
-    {0x81110000u, 46u}, // ria -> Latn
-    {0x95110000u, 91u}, // rif -> Tfng
-    {0x95114E4Cu, 46u}, // rif-NL -> Latn
-    {0xC9310000u, 19u}, // rjs -> Deva
-    {0xCD510000u,  8u}, // rkt -> Beng
-    {0x726D0000u, 46u}, // rm -> Latn
-    {0x95910000u, 46u}, // rmf -> Latn
-    {0xB9910000u, 46u}, // rmo -> Latn
-    {0xCD910000u,  2u}, // rmt -> Arab
-    {0xD1910000u, 46u}, // rmu -> Latn
-    {0x726E0000u, 46u}, // rn -> Latn
-    {0x81B10000u, 46u}, // rna -> Latn
-    {0x99B10000u, 46u}, // rng -> Latn
-    {0x726F0000u, 46u}, // ro -> Latn
-    {0x85D10000u, 46u}, // rob -> Latn
-    {0x95D10000u, 46u}, // rof -> Latn
-    {0xB9D10000u, 46u}, // roo -> Latn
-    {0xBA310000u, 46u}, // rro -> Latn
-    {0xB2710000u, 46u}, // rtm -> Latn
-    {0x72750000u, 18u}, // ru -> Cyrl
-    {0x92910000u, 18u}, // rue -> Cyrl
-    {0x9A910000u, 46u}, // rug -> Latn
-    {0x72770000u, 46u}, // rw -> Latn
-    {0xAAD10000u, 46u}, // rwk -> Latn
-    {0xBAD10000u, 46u}, // rwo -> Latn
-    {0xD3110000u, 37u}, // ryu -> Kana
-    {0x73610000u, 19u}, // sa -> Deva
-    {0x94120000u, 46u}, // saf -> Latn
-    {0x9C120000u, 18u}, // sah -> Cyrl
-    {0xC0120000u, 46u}, // saq -> Latn
-    {0xC8120000u, 46u}, // sas -> Latn
-    {0xCC120000u, 64u}, // sat -> Olck
-    {0xD4120000u, 46u}, // sav -> Latn
-    {0xE4120000u, 78u}, // saz -> Saur
-    {0x80320000u, 46u}, // sba -> Latn
-    {0x90320000u, 46u}, // sbe -> Latn
-    {0xBC320000u, 46u}, // sbp -> Latn
-    {0x73630000u, 46u}, // sc -> Latn
-    {0xA8520000u, 19u}, // sck -> Deva
-    {0xAC520000u,  2u}, // scl -> Arab
-    {0xB4520000u, 46u}, // scn -> Latn
-    {0xB8520000u, 46u}, // sco -> Latn
-    {0x73640000u,  2u}, // sd -> Arab
-    {0x7364494Eu, 19u}, // sd-IN -> Deva
-    {0x88720000u, 46u}, // sdc -> Latn
-    {0x9C720000u,  2u}, // sdh -> Arab
-    {0x73650000u, 46u}, // se -> Latn
-    {0x94920000u, 46u}, // sef -> Latn
-    {0x9C920000u, 46u}, // seh -> Latn
-    {0xA0920000u, 46u}, // sei -> Latn
-    {0xC8920000u, 46u}, // ses -> Latn
-    {0x73670000u, 46u}, // sg -> Latn
-    {0x80D20000u, 63u}, // sga -> Ogam
-    {0xC8D20000u, 46u}, // sgs -> Latn
-    {0xD8D20000u, 21u}, // sgw -> Ethi
-    {0xE4D20000u, 46u}, // sgz -> Latn
-    {0x73680000u, 46u}, // sh -> Latn
-    {0xA0F20000u, 91u}, // shi -> Tfng
-    {0xA8F20000u, 46u}, // shk -> Latn
-    {0xB4F20000u, 59u}, // shn -> Mymr
-    {0xD0F20000u,  2u}, // shu -> Arab
-    {0x73690000u, 80u}, // si -> Sinh
-    {0x8D120000u, 46u}, // sid -> Latn
-    {0x99120000u, 46u}, // sig -> Latn
-    {0xAD120000u, 46u}, // sil -> Latn
-    {0xB1120000u, 46u}, // sim -> Latn
-    {0xC5320000u, 46u}, // sjr -> Latn
-    {0x736B0000u, 46u}, // sk -> Latn
-    {0x89520000u, 46u}, // skc -> Latn
-    {0xC5520000u,  2u}, // skr -> Arab
-    {0xC9520000u, 46u}, // sks -> Latn
-    {0x736C0000u, 46u}, // sl -> Latn
-    {0x8D720000u, 46u}, // sld -> Latn
-    {0xA1720000u, 46u}, // sli -> Latn
-    {0xAD720000u, 46u}, // sll -> Latn
-    {0xE1720000u, 46u}, // sly -> Latn
-    {0x736D0000u, 46u}, // sm -> Latn
-    {0x81920000u, 46u}, // sma -> Latn
-    {0x8D920000u, 46u}, // smd -> Latn
-    {0xA5920000u, 46u}, // smj -> Latn
-    {0xB5920000u, 46u}, // smn -> Latn
-    {0xBD920000u, 76u}, // smp -> Samr
-    {0xC1920000u, 46u}, // smq -> Latn
-    {0xC9920000u, 46u}, // sms -> Latn
-    {0x736E0000u, 46u}, // sn -> Latn
-    {0x85B20000u, 46u}, // snb -> Latn
-    {0x89B20000u, 46u}, // snc -> Latn
-    {0xA9B20000u, 46u}, // snk -> Latn
-    {0xBDB20000u, 46u}, // snp -> Latn
-    {0xDDB20000u, 46u}, // snx -> Latn
-    {0xE1B20000u, 46u}, // sny -> Latn
-    {0x736F0000u, 46u}, // so -> Latn
-    {0x99D20000u, 81u}, // sog -> Sogd
-    {0xA9D20000u, 46u}, // sok -> Latn
-    {0xC1D20000u, 46u}, // soq -> Latn
-    {0xD1D20000u, 93u}, // sou -> Thai
-    {0xE1D20000u, 46u}, // soy -> Latn
-    {0x8DF20000u, 46u}, // spd -> Latn
-    {0xADF20000u, 46u}, // spl -> Latn
-    {0xC9F20000u, 46u}, // sps -> Latn
-    {0x73710000u, 46u}, // sq -> Latn
-    {0x73720000u, 18u}, // sr -> Cyrl
-    {0x73724D45u, 46u}, // sr-ME -> Latn
-    {0x7372524Fu, 46u}, // sr-RO -> Latn
-    {0x73725255u, 46u}, // sr-RU -> Latn
-    {0x73725452u, 46u}, // sr-TR -> Latn
-    {0x86320000u, 82u}, // srb -> Sora
-    {0xB6320000u, 46u}, // srn -> Latn
-    {0xC6320000u, 46u}, // srr -> Latn
-    {0xDE320000u, 19u}, // srx -> Deva
-    {0x73730000u, 46u}, // ss -> Latn
-    {0x8E520000u, 46u}, // ssd -> Latn
-    {0x9A520000u, 46u}, // ssg -> Latn
-    {0xE2520000u, 46u}, // ssy -> Latn
-    {0x73740000u, 46u}, // st -> Latn
-    {0xAA720000u, 46u}, // stk -> Latn
-    {0xC2720000u, 46u}, // stq -> Latn
-    {0x73750000u, 46u}, // su -> Latn
-    {0x82920000u, 46u}, // sua -> Latn
-    {0x92920000u, 46u}, // sue -> Latn
-    {0xAA920000u, 46u}, // suk -> Latn
-    {0xC6920000u, 46u}, // sur -> Latn
-    {0xCA920000u, 46u}, // sus -> Latn
-    {0x73760000u, 46u}, // sv -> Latn
-    {0x73770000u, 46u}, // sw -> Latn
-    {0x86D20000u,  2u}, // swb -> Arab
-    {0x8AD20000u, 46u}, // swc -> Latn
-    {0x9AD20000u, 46u}, // swg -> Latn
-    {0xBED20000u, 46u}, // swp -> Latn
-    {0xD6D20000u, 19u}, // swv -> Deva
-    {0xB6F20000u, 46u}, // sxn -> Latn
-    {0xDAF20000u, 46u}, // sxw -> Latn
-    {0xAF120000u,  8u}, // syl -> Beng
-    {0xC7120000u, 84u}, // syr -> Syrc
-    {0xAF320000u, 46u}, // szl -> Latn
-    {0x74610000u, 87u}, // ta -> Taml
-    {0xA4130000u, 19u}, // taj -> Deva
-    {0xAC130000u, 46u}, // tal -> Latn
-    {0xB4130000u, 46u}, // tan -> Latn
-    {0xC0130000u, 46u}, // taq -> Latn
-    {0x88330000u, 46u}, // tbc -> Latn
-    {0x8C330000u, 46u}, // tbd -> Latn
-    {0x94330000u, 46u}, // tbf -> Latn
-    {0x98330000u, 46u}, // tbg -> Latn
-    {0xB8330000u, 46u}, // tbo -> Latn
-    {0xD8330000u, 46u}, // tbw -> Latn
-    {0xE4330000u, 46u}, // tbz -> Latn
-    {0xA0530000u, 46u}, // tci -> Latn
-    {0xE0530000u, 42u}, // tcy -> Knda
-    {0x8C730000u, 85u}, // tdd -> Tale
-    {0x98730000u, 19u}, // tdg -> Deva
-    {0x9C730000u, 19u}, // tdh -> Deva
-    {0xD0730000u, 46u}, // tdu -> Latn
-    {0x74650000u, 90u}, // te -> Telu
-    {0x8C930000u, 46u}, // ted -> Latn
-    {0xB0930000u, 46u}, // tem -> Latn
-    {0xB8930000u, 46u}, // teo -> Latn
-    {0xCC930000u, 46u}, // tet -> Latn
-    {0xA0B30000u, 46u}, // tfi -> Latn
-    {0x74670000u, 18u}, // tg -> Cyrl
-    {0x7467504Bu,  2u}, // tg-PK -> Arab
-    {0x88D30000u, 46u}, // tgc -> Latn
-    {0xB8D30000u, 46u}, // tgo -> Latn
-    {0xD0D30000u, 46u}, // tgu -> Latn
-    {0x74680000u, 93u}, // th -> Thai
-    {0xACF30000u, 19u}, // thl -> Deva
-    {0xC0F30000u, 19u}, // thq -> Deva
-    {0xC4F30000u, 19u}, // thr -> Deva
-    {0x74690000u, 21u}, // ti -> Ethi
-    {0x95130000u, 46u}, // tif -> Latn
-    {0x99130000u, 21u}, // tig -> Ethi
-    {0xA9130000u, 46u}, // tik -> Latn
-    {0xB1130000u, 46u}, // tim -> Latn
-    {0xB9130000u, 46u}, // tio -> Latn
-    {0xD5130000u, 46u}, // tiv -> Latn
-    {0x746B0000u, 46u}, // tk -> Latn
-    {0xAD530000u, 46u}, // tkl -> Latn
-    {0xC5530000u, 46u}, // tkr -> Latn
-    {0xCD530000u, 19u}, // tkt -> Deva
-    {0x746C0000u, 46u}, // tl -> Latn
-    {0x95730000u, 46u}, // tlf -> Latn
-    {0xDD730000u, 46u}, // tlx -> Latn
-    {0xE1730000u, 46u}, // tly -> Latn
-    {0x9D930000u, 46u}, // tmh -> Latn
-    {0xE1930000u, 46u}, // tmy -> Latn
-    {0x746E0000u, 46u}, // tn -> Latn
-    {0x9DB30000u, 46u}, // tnh -> Latn
-    {0x746F0000u, 46u}, // to -> Latn
-    {0x95D30000u, 46u}, // tof -> Latn
-    {0x99D30000u, 46u}, // tog -> Latn
-    {0xA9D30000u, 46u}, // tok -> Latn
-    {0xC1D30000u, 46u}, // toq -> Latn
-    {0xA1F30000u, 46u}, // tpi -> Latn
-    {0xB1F30000u, 46u}, // tpm -> Latn
-    {0xE5F30000u, 46u}, // tpz -> Latn
-    {0xBA130000u, 46u}, // tqo -> Latn
-    {0x74720000u, 46u}, // tr -> Latn
-    {0xD2330000u, 46u}, // tru -> Latn
-    {0xD6330000u, 46u}, // trv -> Latn
-    {0xDA330000u,  2u}, // trw -> Arab
-    {0x74730000u, 46u}, // ts -> Latn
-    {0x8E530000u, 26u}, // tsd -> Grek
-    {0x96530000u, 19u}, // tsf -> Deva
-    {0x9A530000u, 46u}, // tsg -> Latn
-    {0xA6530000u, 94u}, // tsj -> Tibt
-    {0xDA530000u, 46u}, // tsw -> Latn
-    {0x74740000u, 18u}, // tt -> Cyrl
-    {0x8E730000u, 46u}, // ttd -> Latn
-    {0x92730000u, 46u}, // tte -> Latn
-    {0xA6730000u, 46u}, // ttj -> Latn
-    {0xC6730000u, 46u}, // ttr -> Latn
-    {0xCA730000u, 93u}, // tts -> Thai
-    {0xCE730000u, 46u}, // ttt -> Latn
-    {0x9E930000u, 46u}, // tuh -> Latn
-    {0xAE930000u, 46u}, // tul -> Latn
-    {0xB2930000u, 46u}, // tum -> Latn
-    {0xC2930000u, 46u}, // tuq -> Latn
-    {0x8EB30000u, 46u}, // tvd -> Latn
-    {0xAEB30000u, 46u}, // tvl -> Latn
-    {0xD2B30000u, 46u}, // tvu -> Latn
-    {0x9ED30000u, 46u}, // twh -> Latn
-    {0xC2D30000u, 46u}, // twq -> Latn
-    {0x9AF30000u, 88u}, // txg -> Tang
-    {0xBAF30000u, 96u}, // txo -> Toto
-    {0x74790000u, 46u}, // ty -> Latn
-    {0x83130000u, 46u}, // tya -> Latn
-    {0xD7130000u, 18u}, // tyv -> Cyrl
-    {0xB3330000u, 46u}, // tzm -> Latn
-    {0xD0340000u, 46u}, // ubu -> Latn
-    {0xA0740000u,  0u}, // udi -> Aghb
-    {0xB0740000u, 18u}, // udm -> Cyrl
-    {0x75670000u,  2u}, // ug -> Arab
-    {0x75674B5Au, 18u}, // ug-KZ -> Cyrl
-    {0x75674D4Eu, 18u}, // ug-MN -> Cyrl
-    {0x80D40000u, 97u}, // uga -> Ugar
-    {0x756B0000u, 18u}, // uk -> Cyrl
-    {0xA1740000u, 46u}, // uli -> Latn
-    {0x85940000u, 46u}, // umb -> Latn
-    {0xC5B40000u,  8u}, // unr -> Beng
-    {0xC5B44E50u, 19u}, // unr-NP -> Deva
-    {0xDDB40000u,  8u}, // unx -> Beng
-    {0xA9D40000u, 46u}, // uok -> Latn
-    {0x75720000u,  2u}, // ur -> Arab
-    {0xA2340000u, 46u}, // uri -> Latn
-    {0xCE340000u, 46u}, // urt -> Latn
-    {0xDA340000u, 46u}, // urw -> Latn
-    {0x82540000u, 46u}, // usa -> Latn
-    {0x9E740000u, 46u}, // uth -> Latn
-    {0xC6740000u, 46u}, // utr -> Latn
-    {0x9EB40000u, 46u}, // uvh -> Latn
-    {0xAEB40000u, 46u}, // uvl -> Latn
-    {0x757A0000u, 46u}, // uz -> Latn
-    {0x757A4146u,  2u}, // uz-AF -> Arab
-    {0x757A434Eu, 18u}, // uz-CN -> Cyrl
-    {0x98150000u, 46u}, // vag -> Latn
-    {0xA0150000u, 98u}, // vai -> Vaii
-    {0xB4150000u, 46u}, // van -> Latn
-    {0x76650000u, 46u}, // ve -> Latn
-    {0x88950000u, 46u}, // vec -> Latn
-    {0xBC950000u, 46u}, // vep -> Latn
-    {0x76690000u, 46u}, // vi -> Latn
-    {0x89150000u, 46u}, // vic -> Latn
-    {0xD5150000u, 46u}, // viv -> Latn
-    {0xC9750000u, 46u}, // vls -> Latn
-    {0x95950000u, 46u}, // vmf -> Latn
-    {0xD9950000u, 46u}, // vmw -> Latn
-    {0x766F0000u, 46u}, // vo -> Latn
-    {0xCDD50000u, 46u}, // vot -> Latn
-    {0xBA350000u, 46u}, // vro -> Latn
-    {0xB6950000u, 46u}, // vun -> Latn
-    {0xCE950000u, 46u}, // vut -> Latn
-    {0x77610000u, 46u}, // wa -> Latn
-    {0x90160000u, 46u}, // wae -> Latn
-    {0xA4160000u, 46u}, // waj -> Latn
-    {0xAC160000u, 21u}, // wal -> Ethi
-    {0xB4160000u, 46u}, // wan -> Latn
-    {0xC4160000u, 46u}, // war -> Latn
-    {0xBC360000u, 46u}, // wbp -> Latn
-    {0xC0360000u, 90u}, // wbq -> Telu
-    {0xC4360000u, 19u}, // wbr -> Deva
-    {0xA0560000u, 46u}, // wci -> Latn
-    {0xC4960000u, 46u}, // wer -> Latn
-    {0xA0D60000u, 46u}, // wgi -> Latn
-    {0x98F60000u, 46u}, // whg -> Latn
-    {0x85160000u, 46u}, // wib -> Latn
-    {0xD1160000u, 46u}, // wiu -> Latn
-    {0xD5160000u, 46u}, // wiv -> Latn
-    {0x81360000u, 46u}, // wja -> Latn
-    {0xA1360000u, 46u}, // wji -> Latn
-    {0xC9760000u, 46u}, // wls -> Latn
-    {0xB9960000u, 46u}, // wmo -> Latn
-    {0x89B60000u, 46u}, // wnc -> Latn
-    {0xA1B60000u,  2u}, // wni -> Arab
-    {0xD1B60000u, 46u}, // wnu -> Latn
-    {0x776F0000u, 46u}, // wo -> Latn
-    {0x85D60000u, 46u}, // wob -> Latn
-    {0xC9D60000u, 46u}, // wos -> Latn
-    {0xCA360000u, 46u}, // wrs -> Latn
-    {0x9A560000u, 23u}, // wsg -> Gong
-    {0xAA560000u, 46u}, // wsk -> Latn
-    {0xB2760000u, 19u}, // wtm -> Deva
-    {0xD2960000u, 29u}, // wuu -> Hans
-    {0xD6960000u, 46u}, // wuv -> Latn
-    {0x82D60000u, 46u}, // wwa -> Latn
-    {0xD4170000u, 46u}, // xav -> Latn
-    {0xA0370000u, 46u}, // xbi -> Latn
-    {0xB8570000u, 15u}, // xco -> Chrs
-    {0xC4570000u, 12u}, // xcr -> Cari
-    {0xC8970000u, 46u}, // xes -> Latn
-    {0x78680000u, 46u}, // xh -> Latn
-    {0x81770000u, 46u}, // xla -> Latn
-    {0x89770000u, 50u}, // xlc -> Lyci
-    {0x8D770000u, 51u}, // xld -> Lydi
-    {0x95970000u, 22u}, // xmf -> Geor
-    {0xB5970000u, 53u}, // xmn -> Mani
-    {0xC5970000u, 55u}, // xmr -> Merc
-    {0x81B70000u, 60u}, // xna -> Narb
-    {0xC5B70000u, 19u}, // xnr -> Deva
-    {0x99D70000u, 46u}, // xog -> Latn
-    {0xB5D70000u, 46u}, // xon -> Latn
-    {0xC5F70000u, 73u}, // xpr -> Prti
-    {0x86370000u, 46u}, // xrb -> Latn
-    {0x82570000u, 77u}, // xsa -> Sarb
-    {0xA2570000u, 46u}, // xsi -> Latn
-    {0xB2570000u, 46u}, // xsm -> Latn
-    {0xC6570000u, 19u}, // xsr -> Deva
-    {0x92D70000u, 46u}, // xwe -> Latn
-    {0xB0180000u, 46u}, // yam -> Latn
-    {0xB8180000u, 46u}, // yao -> Latn
-    {0xBC180000u, 46u}, // yap -> Latn
-    {0xC8180000u, 46u}, // yas -> Latn
-    {0xCC180000u, 46u}, // yat -> Latn
-    {0xD4180000u, 46u}, // yav -> Latn
-    {0xE0180000u, 46u}, // yay -> Latn
-    {0xE4180000u, 46u}, // yaz -> Latn
-    {0x80380000u, 46u}, // yba -> Latn
-    {0x84380000u, 46u}, // ybb -> Latn
-    {0xE0380000u, 46u}, // yby -> Latn
-    {0xC4980000u, 46u}, // yer -> Latn
-    {0xC4D80000u, 46u}, // ygr -> Latn
-    {0xD8D80000u, 46u}, // ygw -> Latn
-    {0x79690000u, 31u}, // yi -> Hebr
-    {0xB9580000u, 46u}, // yko -> Latn
-    {0x91780000u, 46u}, // yle -> Latn
-    {0x99780000u, 46u}, // ylg -> Latn
-    {0xAD780000u, 46u}, // yll -> Latn
-    {0xAD980000u, 46u}, // yml -> Latn
-    {0x796F0000u, 46u}, // yo -> Latn
-    {0xB5D80000u, 46u}, // yon -> Latn
-    {0x86380000u, 46u}, // yrb -> Latn
-    {0x92380000u, 46u}, // yre -> Latn
-    {0xAE380000u, 46u}, // yrl -> Latn
-    {0xCA580000u, 46u}, // yss -> Latn
-    {0x82980000u, 46u}, // yua -> Latn
-    {0x92980000u, 30u}, // yue -> Hant
-    {0x9298434Eu, 29u}, // yue-CN -> Hans
-    {0xA6980000u, 46u}, // yuj -> Latn
-    {0xCE980000u, 46u}, // yut -> Latn
-    {0xDA980000u, 46u}, // yuw -> Latn
-    {0x7A610000u, 46u}, // za -> Latn
-    {0x98190000u, 46u}, // zag -> Latn
-    {0xA4790000u,  2u}, // zdj -> Arab
-    {0x80990000u, 46u}, // zea -> Latn
-    {0x9CD90000u, 91u}, // zgh -> Tfng
-    {0x7A680000u, 29u}, // zh -> Hans
-    {0x7A684155u, 30u}, // zh-AU -> Hant
-    {0x7A68424Eu, 30u}, // zh-BN -> Hant
-    {0x7A684742u, 30u}, // zh-GB -> Hant
-    {0x7A684746u, 30u}, // zh-GF -> Hant
-    {0x7A68484Bu, 30u}, // zh-HK -> Hant
-    {0x7A684944u, 30u}, // zh-ID -> Hant
-    {0x7A684D4Fu, 30u}, // zh-MO -> Hant
-    {0x7A685041u, 30u}, // zh-PA -> Hant
-    {0x7A685046u, 30u}, // zh-PF -> Hant
-    {0x7A685048u, 30u}, // zh-PH -> Hant
-    {0x7A685352u, 30u}, // zh-SR -> Hant
-    {0x7A685448u, 30u}, // zh-TH -> Hant
-    {0x7A685457u, 30u}, // zh-TW -> Hant
-    {0x7A685553u, 30u}, // zh-US -> Hant
-    {0x7A68564Eu, 30u}, // zh-VN -> Hant
-    {0xDCF90000u, 62u}, // zhx -> Nshu
-    {0x81190000u, 46u}, // zia -> Latn
-    {0xCD590000u, 41u}, // zkt -> Kits
-    {0xB1790000u, 46u}, // zlm -> Latn
-    {0xA1990000u, 46u}, // zmi -> Latn
-    {0x91B90000u, 46u}, // zne -> Latn
-    {0x7A750000u, 46u}, // zu -> Latn
-    {0x83390000u, 46u}, // zza -> Latn
-});
-
-std::unordered_set<uint64_t> REPRESENTATIVE_LOCALES({
-    0x616145544C61746ELLU, // aa_Latn_ET
-    0x616247454379726CLLU, // ab_Cyrl_GE
-    0xC42047484C61746ELLU, // abr_Latn_GH
-    0x904049444C61746ELLU, // ace_Latn_ID
-    0x9C4055474C61746ELLU, // ach_Latn_UG
-    0x806047484C61746ELLU, // ada_Latn_GH
-    0xBC60425454696274LLU, // adp_Tibt_BT
-    0xE06052554379726CLLU, // ady_Cyrl_RU
-    0x6165495241767374LLU, // ae_Avst_IR
-    0x8480544E41726162LLU, // aeb_Arab_TN
-    0x61665A414C61746ELLU, // af_Latn_ZA
-    0xC0C0434D4C61746ELLU, // agq_Latn_CM
-    0xB8E0494E41686F6DLLU, // aho_Ahom_IN
-    0xCD20544E41726162LLU, // ajt_Arab_TN
-    0x616B47484C61746ELLU, // ak_Latn_GH
-    0xA940495158737578LLU, // akk_Xsux_IQ
-    0xB560584B4C61746ELLU, // aln_Latn_XK
-    0xCD6052554379726CLLU, // alt_Cyrl_RU
-    0x616D455445746869LLU, // am_Ethi_ET
-    0xB9804E474C61746ELLU, // amo_Latn_NG
-    0x616E45534C61746ELLU, // an_Latn_ES
-    0xB5A04E474C61746ELLU, // ann_Latn_NG
-    0xE5C049444C61746ELLU, // aoz_Latn_ID
-    0x8DE0544741726162LLU, // apd_Arab_TG
-    0x6172454741726162LLU, // ar_Arab_EG
-    0x8A20495241726D69LLU, // arc_Armi_IR
-    0x8A204A4F4E626174LLU, // arc_Nbat_JO
-    0x8A20535950616C6DLLU, // arc_Palm_SY
-    0xB620434C4C61746ELLU, // arn_Latn_CL
-    0xBA20424F4C61746ELLU, // aro_Latn_BO
-    0xC220445A41726162LLU, // arq_Arab_DZ
-    0xCA20534141726162LLU, // ars_Arab_SA
-    0xE2204D4141726162LLU, // ary_Arab_MA
-    0xE620454741726162LLU, // arz_Arab_EG
-    0x6173494E42656E67LLU, // as_Beng_IN
-    0x8240545A4C61746ELLU, // asa_Latn_TZ
-    0x9240555353676E77LLU, // ase_Sgnw_US
-    0xCE4045534C61746ELLU, // ast_Latn_ES
-    0xA66043414C61746ELLU, // atj_Latn_CA
-    0x617652554379726CLLU, // av_Cyrl_RU
-    0x82C0494E44657661LLU, // awa_Deva_IN
-    0x6179424F4C61746ELLU, // ay_Latn_BO
-    0x617A495241726162LLU, // az_Arab_IR
-    0x617A415A4C61746ELLU, // az_Latn_AZ
-    0x626152554379726CLLU, // ba_Cyrl_RU
-    0xAC01504B41726162LLU, // bal_Arab_PK
-    0xB40149444C61746ELLU, // ban_Latn_ID
-    0xBC014E5044657661LLU, // bap_Deva_NP
-    0xC40141544C61746ELLU, // bar_Latn_AT
-    0xC801434D4C61746ELLU, // bas_Latn_CM
-    0xDC01434D42616D75LLU, // bax_Bamu_CM
-    0x882149444C61746ELLU, // bbc_Latn_ID
-    0xA421434D4C61746ELLU, // bbj_Latn_CM
-    0xA04143494C61746ELLU, // bci_Latn_CI
-    0x626542594379726CLLU, // be_Cyrl_BY
-    0xA481534441726162LLU, // bej_Arab_SD
-    0xB0815A4D4C61746ELLU, // bem_Latn_ZM
-    0xD88149444C61746ELLU, // bew_Latn_ID
-    0xE481545A4C61746ELLU, // bez_Latn_TZ
-    0x8CA1434D4C61746ELLU, // bfd_Latn_CM
-    0xC0A1494E54616D6CLLU, // bfq_Taml_IN
-    0xCCA1504B41726162LLU, // bft_Arab_PK
-    0xE0A1494E44657661LLU, // bfy_Deva_IN
-    0x626742474379726CLLU, // bg_Cyrl_BG
-    0x88C1494E44657661LLU, // bgc_Deva_IN
-    0xB4C1504B41726162LLU, // bgn_Arab_PK
-    0xDCC154524772656BLLU, // bgx_Grek_TR
-    0x84E1494E44657661LLU, // bhb_Deva_IN
-    0xA0E1494E44657661LLU, // bhi_Deva_IN
-    0xB8E1494E44657661LLU, // bho_Deva_IN
-    0x626956554C61746ELLU, // bi_Latn_VU
-    0xA90150484C61746ELLU, // bik_Latn_PH
-    0xB5014E474C61746ELLU, // bin_Latn_NG
-    0xA521494E44657661LLU, // bjj_Deva_IN
-    0xB52149444C61746ELLU, // bjn_Latn_ID
-    0xCD21534E4C61746ELLU, // bjt_Latn_SN
-    0xB141434D4C61746ELLU, // bkm_Latn_CM
-    0xD14150484C61746ELLU, // bku_Latn_PH
-    0x816143414C61746ELLU, // bla_Latn_CA
-    0x99614D594C61746ELLU, // blg_Latn_MY
-    0xCD61564E54617674LLU, // blt_Tavt_VN
-    0x626D4D4C4C61746ELLU, // bm_Latn_ML
-    0xC1814D4C4C61746ELLU, // bmq_Latn_ML
-    0x626E424442656E67LLU, // bn_Beng_BD
-    0x626F434E54696274LLU, // bo_Tibt_CN
-    0xE1E1494E42656E67LLU, // bpy_Beng_IN
-    0xA201495241726162LLU, // bqi_Arab_IR
-    0xD60143494C61746ELLU, // bqv_Latn_CI
-    0x627246524C61746ELLU, // br_Latn_FR
-    0x8221494E44657661LLU, // bra_Deva_IN
-    0x9E21504B41726162LLU, // brh_Arab_PK
-    0xDE21494E44657661LLU, // brx_Deva_IN
-    0x627342414C61746ELLU, // bs_Latn_BA
-    0xC2414C5242617373LLU, // bsq_Bass_LR
-    0xCA41434D4C61746ELLU, // bss_Latn_CM
-    0xBA6150484C61746ELLU, // bto_Latn_PH
-    0xD661504B44657661LLU, // btv_Deva_PK
-    0x828152554379726CLLU, // bua_Cyrl_RU
-    0x8A8159544C61746ELLU, // buc_Latn_YT
-    0x9A8149444C61746ELLU, // bug_Latn_ID
-    0xB281434D4C61746ELLU, // bum_Latn_CM
-    0x86A147514C61746ELLU, // bvb_Latn_GQ
-    0xB701455245746869LLU, // byn_Ethi_ER
-    0xD701434D4C61746ELLU, // byv_Latn_CM
-    0x93214D4C4C61746ELLU, // bze_Latn_ML
-    0x636145534C61746ELLU, // ca_Latn_ES
-    0x8C0255534C61746ELLU, // cad_Latn_US
-    0x9C424E474C61746ELLU, // cch_Latn_NG
-    0xBC42424443616B6DLLU, // ccp_Cakm_BD
-    0x636552554379726CLLU, // ce_Cyrl_RU
-    0x848250484C61746ELLU, // ceb_Latn_PH
-    0x98C255474C61746ELLU, // cgg_Latn_UG
-    0x636847554C61746ELLU, // ch_Latn_GU
-    0xA8E2464D4C61746ELLU, // chk_Latn_FM
-    0xB0E252554379726CLLU, // chm_Cyrl_RU
-    0xB8E255534C61746ELLU, // cho_Latn_US
-    0xBCE243414C61746ELLU, // chp_Latn_CA
-    0xC4E2555343686572LLU, // chr_Cher_US
-    0x890255534C61746ELLU, // cic_Latn_US
-    0x81224B4841726162LLU, // cja_Arab_KH
-    0xB122564E4368616DLLU, // cjm_Cham_VN
-    0x8542495141726162LLU, // ckb_Arab_IQ
-    0x896243414C61746ELLU, // clc_Latn_CA
-    0x99824D4E536F796FLLU, // cmg_Soyo_MN
-    0x636F46524C61746ELLU, // co_Latn_FR
-    0xBDC24547436F7074LLU, // cop_Copt_EG
-    0xC9E250484C61746ELLU, // cps_Latn_PH
-    0x6372434143616E73LLU, // cr_Cans_CA
-    0x9A2243414C61746ELLU, // crg_Latn_CA
-    0x9E2255414379726CLLU, // crh_Cyrl_UA
-    0xAA22434143616E73LLU, // crk_Cans_CA
-    0xAE22434143616E73LLU, // crl_Cans_CA
-    0xCA2253434C61746ELLU, // crs_Latn_SC
-    0x6373435A4C61746ELLU, // cs_Latn_CZ
-    0x8642504C4C61746ELLU, // csb_Latn_PL
-    0xDA42434143616E73LLU, // csw_Cans_CA
-    0x8E624D4D50617563LLU, // ctd_Pauc_MM
-    0x637552554379726CLLU, // cu_Cyrl_RU
-    0x63754247476C6167LLU, // cu_Glag_BG
-    0x637652554379726CLLU, // cv_Cyrl_RU
-    0x637947424C61746ELLU, // cy_Latn_GB
-    0x6461444B4C61746ELLU, // da_Latn_DK
-    0x940343494C61746ELLU, // daf_Latn_CI
-    0xA80355534C61746ELLU, // dak_Latn_US
-    0xC40352554379726CLLU, // dar_Cyrl_RU
-    0xD4034B454C61746ELLU, // dav_Latn_KE
-    0x8843494E41726162LLU, // dcc_Arab_IN
-    0x646544454C61746ELLU, // de_Latn_DE
-    0xB48343414C61746ELLU, // den_Latn_CA
-    0xC4C343414C61746ELLU, // dgr_Latn_CA
-    0x91234E454C61746ELLU, // dje_Latn_NE
-    0x95834E474D656466LLU, // dmf_Medf_NG
-    0xA5A343494C61746ELLU, // dnj_Latn_CI
-    0xA1C3494E44657661LLU, // doi_Deva_IN
-    0x9E23434E4D6F6E67LLU, // drh_Mong_CN
-    0x864344454C61746ELLU, // dsb_Latn_DE
-    0xB2634D4C4C61746ELLU, // dtm_Latn_ML
-    0xBE634D594C61746ELLU, // dtp_Latn_MY
-    0xE2634E5044657661LLU, // dty_Deva_NP
-    0x8283434D4C61746ELLU, // dua_Latn_CM
-    0x64764D5654686161LLU, // dv_Thaa_MV
-    0xBB03534E4C61746ELLU, // dyo_Latn_SN
-    0xD30342464C61746ELLU, // dyu_Latn_BF
-    0x647A425454696274LLU, // dz_Tibt_BT
-    0xD0244B454C61746ELLU, // ebu_Latn_KE
-    0x656547484C61746ELLU, // ee_Latn_GH
-    0xA0A44E474C61746ELLU, // efi_Latn_NG
-    0xACC449544C61746ELLU, // egl_Latn_IT
-    0xE0C4454745677970LLU, // egy_Egyp_EG
-    0xE1444D4D4B616C69LLU, // eky_Kali_MM
-    0x656C47524772656BLLU, // el_Grek_GR
-    0x656E47424C61746ELLU, // en_Latn_GB
-    0x656E55534C61746ELLU, // en_Latn_US
-    0x656E474253686177LLU, // en_Shaw_GB
-    0x657345534C61746ELLU, // es_Latn_ES
-    0x65734D584C61746ELLU, // es_Latn_MX
-    0x657355534C61746ELLU, // es_Latn_US
-    0x9A44494E476F6E6DLLU, // esg_Gonm_IN
-    0xD24455534C61746ELLU, // esu_Latn_US
-    0x657445454C61746ELLU, // et_Latn_EE
-    0xCE6449544974616CLLU, // ett_Ital_IT
-    0x657545534C61746ELLU, // eu_Latn_ES
-    0xBAC4434D4C61746ELLU, // ewo_Latn_CM
-    0xCEE445534C61746ELLU, // ext_Latn_ES
-    0x6661495241726162LLU, // fa_Arab_IR
-    0xB40547514C61746ELLU, // fan_Latn_GQ
-    0x6666474E41646C6DLLU, // ff_Adlm_GN
-    0x6666534E4C61746ELLU, // ff_Latn_SN
-    0xB0A54D4C4C61746ELLU, // ffm_Latn_ML
-    0x666946494C61746ELLU, // fi_Latn_FI
-    0x8105534441726162LLU, // fia_Arab_SD
-    0xAD0550484C61746ELLU, // fil_Latn_PH
-    0xCD0553454C61746ELLU, // fit_Latn_SE
-    0x666A464A4C61746ELLU, // fj_Latn_FJ
-    0x666F464F4C61746ELLU, // fo_Latn_FO
-    0xB5C5424A4C61746ELLU, // fon_Latn_BJ
-    0x667246524C61746ELLU, // fr_Latn_FR
-    0x8A2555534C61746ELLU, // frc_Latn_US
-    0xBE2546524C61746ELLU, // frp_Latn_FR
-    0xC62544454C61746ELLU, // frr_Latn_DE
-    0xCA2544454C61746ELLU, // frs_Latn_DE
-    0x8685434D41726162LLU, // fub_Arab_CM
-    0x8E8557464C61746ELLU, // fud_Latn_WF
-    0x9685474E4C61746ELLU, // fuf_Latn_GN
-    0xC2854E454C61746ELLU, // fuq_Latn_NE
-    0xC68549544C61746ELLU, // fur_Latn_IT
-    0xD6854E474C61746ELLU, // fuv_Latn_NG
-    0xC6A553444C61746ELLU, // fvr_Latn_SD
-    0x66794E4C4C61746ELLU, // fy_Latn_NL
-    0x676149454C61746ELLU, // ga_Latn_IE
-    0x800647484C61746ELLU, // gaa_Latn_GH
-    0x98064D444C61746ELLU, // gag_Latn_MD
-    0xB406434E48616E73LLU, // gan_Hans_CN
-    0xE00649444C61746ELLU, // gay_Latn_ID
-    0xB026494E44657661LLU, // gbm_Deva_IN
-    0xE426495241726162LLU, // gbz_Arab_IR
-    0xC44647464C61746ELLU, // gcr_Latn_GF
-    0x676447424C61746ELLU, // gd_Latn_GB
-    0xE486455445746869LLU, // gez_Ethi_ET
-    0xB4C64E5044657661LLU, // ggn_Deva_NP
-    0xAD064B494C61746ELLU, // gil_Latn_KI
-    0xA926504B41726162LLU, // gjk_Arab_PK
-    0xD126504B41726162LLU, // gju_Arab_PK
-    0x676C45534C61746ELLU, // gl_Latn_ES
-    0xA966495241726162LLU, // glk_Arab_IR
-    0x676E50594C61746ELLU, // gn_Latn_PY
-    0xB1C6494E44657661LLU, // gom_Deva_IN
-    0xB5C6494E54656C75LLU, // gon_Telu_IN
-    0xC5C649444C61746ELLU, // gor_Latn_ID
-    0xC9C64E4C4C61746ELLU, // gos_Latn_NL
-    0xCDC65541476F7468LLU, // got_Goth_UA
-    0x8A26435943707274LLU, // grc_Cprt_CY
-    0x8A2647524C696E62LLU, // grc_Linb_GR
-    0xCE26494E42656E67LLU, // grt_Beng_IN
-    0xDA4643484C61746ELLU, // gsw_Latn_CH
-    0x6775494E47756A72LLU, // gu_Gujr_IN
-    0x868642524C61746ELLU, // gub_Latn_BR
-    0x8A86434F4C61746ELLU, // guc_Latn_CO
-    0xC68647484C61746ELLU, // gur_Latn_GH
-    0xE6864B454C61746ELLU, // guz_Latn_KE
-    0x6776494D4C61746ELLU, // gv_Latn_IM
-    0xC6A64E5044657661LLU, // gvr_Deva_NP
-    0xA2C643414C61746ELLU, // gwi_Latn_CA
-    0x68614E474C61746ELLU, // ha_Latn_NG
-    0xA807434E48616E73LLU, // hak_Hans_CN
-    0xD80755534C61746ELLU, // haw_Latn_US
-    0xE407414641726162LLU, // haz_Arab_AF
-    0x6865494C48656272LLU, // he_Hebr_IL
-    0x6869494E44657661LLU, // hi_Deva_IN
-    0x6869494E4C61746ELLU, // hi_Latn_IN
-    0x9507464A4C61746ELLU, // hif_Latn_FJ
-    0xAD0750484C61746ELLU, // hil_Latn_PH
-    0xD1675452486C7577LLU, // hlu_Hluw_TR
-    0x8D87434E506C7264LLU, // hmd_Plrd_CN
-    0x8DA7504B41726162LLU, // hnd_Arab_PK
-    0x91A7494E44657661LLU, // hne_Deva_IN
-    0xA5A75553486D6E70LLU, // hnj_Hmnp_US
-    0xB5A750484C61746ELLU, // hnn_Latn_PH
-    0xB9A7504B41726162LLU, // hno_Arab_PK
-    0x686F50474C61746ELLU, // ho_Latn_PG
-    0x89C7494E44657661LLU, // hoc_Deva_IN
-    0xA5C7494E44657661LLU, // hoj_Deva_IN
-    0x687248524C61746ELLU, // hr_Latn_HR
-    0x864744454C61746ELLU, // hsb_Latn_DE
-    0xB647434E48616E73LLU, // hsn_Hans_CN
-    0x687448544C61746ELLU, // ht_Latn_HT
-    0x687548554C61746ELLU, // hu_Latn_HU
-    0xC68743414C61746ELLU, // hur_Latn_CA
-    0x6879414D41726D6ELLU, // hy_Armn_AM
-    0x687A4E414C61746ELLU, // hz_Latn_NA
-    0x80284D594C61746ELLU, // iba_Latn_MY
-    0x84284E474C61746ELLU, // ibb_Latn_NG
-    0x696449444C61746ELLU, // id_Latn_ID
-    0x90A854474C61746ELLU, // ife_Latn_TG
-    0x69674E474C61746ELLU, // ig_Latn_NG
-    0x6969434E59696969LLU, // ii_Yiii_CN
-    0x696B55534C61746ELLU, // ik_Latn_US
-    0xB96850484C61746ELLU, // ilo_Latn_PH
-    0x696E49444C61746ELLU, // in_Latn_ID
-    0x9DA852554379726CLLU, // inh_Cyrl_RU
-    0x697349534C61746ELLU, // is_Latn_IS
-    0x697449544C61746ELLU, // it_Latn_IT
-    0x6975434143616E73LLU, // iu_Cans_CA
-    0x6977494C48656272LLU, // iw_Hebr_IL
-    0x9F2852554C61746ELLU, // izh_Latn_RU
-    0x6A614A504A70616ELLU, // ja_Jpan_JP
-    0xB0094A4D4C61746ELLU, // jam_Latn_JM
-    0xB8C9434D4C61746ELLU, // jgo_Latn_CM
-    0x8989545A4C61746ELLU, // jmc_Latn_TZ
-    0xAD894E5044657661LLU, // jml_Deva_NP
-    0xCE89444B4C61746ELLU, // jut_Latn_DK
-    0x6A7649444C61746ELLU, // jv_Latn_ID
-    0x6A7749444C61746ELLU, // jw_Latn_ID
-    0x6B61474547656F72LLU, // ka_Geor_GE
-    0x800A555A4379726CLLU, // kaa_Cyrl_UZ
-    0x840A445A4C61746ELLU, // kab_Latn_DZ
-    0x880A4D4D4C61746ELLU, // kac_Latn_MM
-    0xA40A4E474C61746ELLU, // kaj_Latn_NG
-    0xB00A4B454C61746ELLU, // kam_Latn_KE
-    0xB80A4D4C4C61746ELLU, // kao_Latn_ML
-    0xD80A49444B617769LLU, // kaw_Kawi_ID
-    0x8C2A52554379726CLLU, // kbd_Cyrl_RU
-    0xE02A4E4541726162LLU, // kby_Arab_NE
-    0x984A4E474C61746ELLU, // kcg_Latn_NG
-    0xA84A5A574C61746ELLU, // kck_Latn_ZW
-    0x906A545A4C61746ELLU, // kde_Latn_TZ
-    0x9C6A54474C61746ELLU, // kdh_Latn_TG
-    0xCC6A544854686169LLU, // kdt_Thai_TH
-    0x808A43564C61746ELLU, // kea_Latn_CV
-    0xB48A434D4C61746ELLU, // ken_Latn_CM
-    0xB8AA43494C61746ELLU, // kfo_Latn_CI
-    0xC4AA494E44657661LLU, // kfr_Deva_IN
-    0xE0AA494E44657661LLU, // kfy_Deva_IN
-    0x6B6743444C61746ELLU, // kg_Latn_CD
-    0x90CA49444C61746ELLU, // kge_Latn_ID
-    0xBCCA42524C61746ELLU, // kgp_Latn_BR
-    0x80EA494E4C61746ELLU, // kha_Latn_IN
-    0x84EA434E54616C75LLU, // khb_Talu_CN
-    0xB4EA494E44657661LLU, // khn_Deva_IN
-    0xC0EA4D4C4C61746ELLU, // khq_Latn_ML
-    0xCCEA494E4D796D72LLU, // kht_Mymr_IN
-    0xD8EA504B41726162LLU, // khw_Arab_PK
-    0x6B694B454C61746ELLU, // ki_Latn_KE
-    0xD10A54524C61746ELLU, // kiu_Latn_TR
-    0x6B6A4E414C61746ELLU, // kj_Latn_NA
-    0x992A4C414C616F6FLLU, // kjg_Laoo_LA
-    0x6B6B434E41726162LLU, // kk_Arab_CN
-    0x6B6B4B5A4379726CLLU, // kk_Cyrl_KZ
-    0xA54A434D4C61746ELLU, // kkj_Latn_CM
-    0x6B6C474C4C61746ELLU, // kl_Latn_GL
-    0xB56A4B454C61746ELLU, // kln_Latn_KE
-    0x6B6D4B484B686D72LLU, // km_Khmr_KH
-    0x858A414F4C61746ELLU, // kmb_Latn_AO
-    0x6B6E494E4B6E6461LLU, // kn_Knda_IN
-    0x95AA47574C61746ELLU, // knf_Latn_GW
-    0x6B6F4B524B6F7265LLU, // ko_Kore_KR
-    0xA1CA52554379726CLLU, // koi_Cyrl_RU
-    0xA9CA494E44657661LLU, // kok_Deva_IN
-    0xC9CA464D4C61746ELLU, // kos_Latn_FM
-    0x91EA4C524C61746ELLU, // kpe_Latn_LR
-    0x8A2A52554379726CLLU, // krc_Cyrl_RU
-    0xA22A534C4C61746ELLU, // kri_Latn_SL
-    0xA62A50484C61746ELLU, // krj_Latn_PH
-    0xAE2A52554C61746ELLU, // krl_Latn_RU
-    0xD22A494E44657661LLU, // kru_Deva_IN
-    0x6B73494E41726162LLU, // ks_Arab_IN
-    0x864A545A4C61746ELLU, // ksb_Latn_TZ
-    0x964A434D4C61746ELLU, // ksf_Latn_CM
-    0x9E4A44454C61746ELLU, // ksh_Latn_DE
-    0xC66A4D594C61746ELLU, // ktr_Latn_MY
-    0x6B75495141726162LLU, // ku_Arab_IQ
-    0x6B7554524C61746ELLU, // ku_Latn_TR
-    0x6B75474559657A69LLU, // ku_Yezi_GE
-    0xB28A52554379726CLLU, // kum_Cyrl_RU
-    0x6B7652554379726CLLU, // kv_Cyrl_RU
-    0xC6AA49444C61746ELLU, // kvr_Latn_ID
-    0xDEAA504B41726162LLU, // kvx_Arab_PK
-    0x6B7747424C61746ELLU, // kw_Latn_GB
-    0xAACA43414C61746ELLU, // kwk_Latn_CA
-    0xAEEA494E44657661LLU, // kxl_Deva_IN
-    0xB2EA544854686169LLU, // kxm_Thai_TH
-    0xBEEA504B41726162LLU, // kxp_Arab_PK
-    0x6B79434E41726162LLU, // ky_Arab_CN
-    0x6B794B474379726CLLU, // ky_Cyrl_KG
-    0x6B7954524C61746ELLU, // ky_Latn_TR
-    0xA72A4D594C61746ELLU, // kzj_Latn_MY
-    0xCF2A4D594C61746ELLU, // kzt_Latn_MY
-    0x6C6156414C61746ELLU, // la_Latn_VA
-    0x840B47524C696E61LLU, // lab_Lina_GR
-    0x8C0B494C48656272LLU, // lad_Hebr_IL
-    0x980B545A4C61746ELLU, // lag_Latn_TZ
-    0x9C0B504B41726162LLU, // lah_Arab_PK
-    0xA40B55474C61746ELLU, // laj_Latn_UG
-    0x6C624C554C61746ELLU, // lb_Latn_LU
-    0x902B52554379726CLLU, // lbe_Cyrl_RU
-    0xD82B49444C61746ELLU, // lbw_Latn_ID
-    0xBC4B434E54686169LLU, // lcp_Thai_CN
-    0xBC8B494E4C657063LLU, // lep_Lepc_IN
-    0xE48B52554379726CLLU, // lez_Cyrl_RU
-    0x6C6755474C61746ELLU, // lg_Latn_UG
-    0x6C694E4C4C61746ELLU, // li_Latn_NL
-    0x950B4E5044657661LLU, // lif_Deva_NP
-    0x950B494E4C696D62LLU, // lif_Limb_IN
-    0xA50B49544C61746ELLU, // lij_Latn_IT
-    0xAD0B43414C61746ELLU, // lil_Latn_CA
-    0xC90B434E4C697375LLU, // lis_Lisu_CN
-    0xBD2B49444C61746ELLU, // ljp_Latn_ID
-    0xA14B495241726162LLU, // lki_Arab_IR
-    0xCD4B55534C61746ELLU, // lkt_Latn_US
-    0xB58B494E54656C75LLU, // lmn_Telu_IN
-    0xB98B49544C61746ELLU, // lmo_Latn_IT
-    0x6C6E43444C61746ELLU, // ln_Latn_CD
-    0x6C6F4C414C616F6FLLU, // lo_Laoo_LA
-    0xADCB43444C61746ELLU, // lol_Latn_CD
-    0xE5CB5A4D4C61746ELLU, // loz_Latn_ZM
-    0x8A2B495241726162LLU, // lrc_Arab_IR
-    0x6C744C544C61746ELLU, // lt_Latn_LT
-    0x9A6B4C564C61746ELLU, // ltg_Latn_LV
-    0x6C7543444C61746ELLU, // lu_Latn_CD
-    0x828B43444C61746ELLU, // lua_Latn_CD
-    0xBA8B4B454C61746ELLU, // luo_Latn_KE
-    0xE28B4B454C61746ELLU, // luy_Latn_KE
-    0xE68B495241726162LLU, // luz_Arab_IR
-    0x6C764C564C61746ELLU, // lv_Latn_LV
-    0xAECB544854686169LLU, // lwl_Thai_TH
-    0x9F2B434E48616E73LLU, // lzh_Hans_CN
-    0xE72B54524C61746ELLU, // lzz_Latn_TR
-    0x8C0C49444C61746ELLU, // mad_Latn_ID
-    0x940C434D4C61746ELLU, // maf_Latn_CM
-    0x980C494E44657661LLU, // mag_Deva_IN
-    0xA00C494E44657661LLU, // mai_Deva_IN
-    0xA80C49444C61746ELLU, // mak_Latn_ID
-    0xB40C474D4C61746ELLU, // man_Latn_GM
-    0xB40C474E4E6B6F6FLLU, // man_Nkoo_GN
-    0xC80C4B454C61746ELLU, // mas_Latn_KE
-    0xE40C4D584C61746ELLU, // maz_Latn_MX
-    0x946C52554379726CLLU, // mdf_Cyrl_RU
-    0x9C6C50484C61746ELLU, // mdh_Latn_PH
-    0xC46C49444C61746ELLU, // mdr_Latn_ID
-    0xB48C534C4C61746ELLU, // men_Latn_SL
-    0xC48C4B454C61746ELLU, // mer_Latn_KE
-    0x80AC544841726162LLU, // mfa_Arab_TH
-    0x90AC4D554C61746ELLU, // mfe_Latn_MU
-    0x6D674D474C61746ELLU, // mg_Latn_MG
-    0x9CCC4D5A4C61746ELLU, // mgh_Latn_MZ
-    0xB8CC434D4C61746ELLU, // mgo_Latn_CM
-    0xBCCC4E5044657661LLU, // mgp_Deva_NP
-    0xE0CC545A4C61746ELLU, // mgy_Latn_TZ
-    0x6D684D484C61746ELLU, // mh_Latn_MH
-    0x6D694E5A4C61746ELLU, // mi_Latn_NZ
-    0x890C43414C61746ELLU, // mic_Latn_CA
-    0xB50C49444C61746ELLU, // min_Latn_ID
-    0x6D6B4D4B4379726CLLU, // mk_Cyrl_MK
-    0x6D6C494E4D6C796DLLU, // ml_Mlym_IN
-    0xC96C53444C61746ELLU, // mls_Latn_SD
-    0x6D6E4D4E4379726CLLU, // mn_Cyrl_MN
-    0x6D6E434E4D6F6E67LLU, // mn_Mong_CN
-    0xA1AC494E42656E67LLU, // mni_Beng_IN
-    0xD9AC4D4D4D796D72LLU, // mnw_Mymr_MM
-    0x6D6F524F4C61746ELLU, // mo_Latn_RO
-    0x91CC43414C61746ELLU, // moe_Latn_CA
-    0x9DCC43414C61746ELLU, // moh_Latn_CA
-    0xC9CC42464C61746ELLU, // mos_Latn_BF
-    0x6D72494E44657661LLU, // mr_Deva_IN
-    0x8E2C4E5044657661LLU, // mrd_Deva_NP
-    0xA62C52554379726CLLU, // mrj_Cyrl_RU
-    0xBA2C42444D726F6FLLU, // mro_Mroo_BD
-    0x6D734D594C61746ELLU, // ms_Latn_MY
-    0x6D744D544C61746ELLU, // mt_Latn_MT
-    0xC66C494E44657661LLU, // mtr_Deva_IN
-    0x828C434D4C61746ELLU, // mua_Latn_CM
-    0xCA8C55534C61746ELLU, // mus_Latn_US
-    0xE2AC504B41726162LLU, // mvy_Arab_PK
-    0xAACC4D4C4C61746ELLU, // mwk_Latn_ML
-    0xC6CC494E44657661LLU, // mwr_Deva_IN
-    0xD6CC49444C61746ELLU, // mwv_Latn_ID
-    0xDACC5553486D6E70LLU, // mww_Hmnp_US
-    0x8AEC5A574C61746ELLU, // mxc_Latn_ZW
-    0x6D794D4D4D796D72LLU, // my_Mymr_MM
-    0xD70C52554379726CLLU, // myv_Cyrl_RU
-    0xDF0C55474C61746ELLU, // myx_Latn_UG
-    0xE70C49524D616E64LLU, // myz_Mand_IR
-    0xB72C495241726162LLU, // mzn_Arab_IR
-    0x6E614E524C61746ELLU, // na_Latn_NR
-    0xB40D434E48616E73LLU, // nan_Hans_CN
-    0xBC0D49544C61746ELLU, // nap_Latn_IT
-    0xC00D4E414C61746ELLU, // naq_Latn_NA
-    0x6E624E4F4C61746ELLU, // nb_Latn_NO
-    0x9C4D4D584C61746ELLU, // nch_Latn_MX
-    0x6E645A574C61746ELLU, // nd_Latn_ZW
-    0x886D4D5A4C61746ELLU, // ndc_Latn_MZ
-    0xC86D44454C61746ELLU, // nds_Latn_DE
-    0x6E654E5044657661LLU, // ne_Deva_NP
-    0xD88D4E5044657661LLU, // new_Deva_NP
-    0x6E674E414C61746ELLU, // ng_Latn_NA
-    0xACCD4D5A4C61746ELLU, // ngl_Latn_MZ
-    0x90ED4D584C61746ELLU, // nhe_Latn_MX
-    0xD8ED4D584C61746ELLU, // nhw_Latn_MX
-    0xA50D49444C61746ELLU, // nij_Latn_ID
-    0xD10D4E554C61746ELLU, // niu_Latn_NU
-    0xB92D494E4C61746ELLU, // njo_Latn_IN
-    0x6E6C4E4C4C61746ELLU, // nl_Latn_NL
-    0x998D434D4C61746ELLU, // nmg_Latn_CM
-    0x6E6E4E4F4C61746ELLU, // nn_Latn_NO
-    0x9DAD434D4C61746ELLU, // nnh_Latn_CM
-    0xBDAD494E5763686FLLU, // nnp_Wcho_IN
-    0x6E6F4E4F4C61746ELLU, // no_Latn_NO
-    0x8DCD54484C616E61LLU, // nod_Lana_TH
-    0x91CD494E44657661LLU, // noe_Deva_IN
-    0xB5CD534552756E72LLU, // non_Runr_SE
-    0xBA0D474E4E6B6F6FLLU, // nqo_Nkoo_GN
-    0x6E725A414C61746ELLU, // nr_Latn_ZA
-    0xAA4D434143616E73LLU, // nsk_Cans_CA
-    0xBA4D5A414C61746ELLU, // nso_Latn_ZA
-    0xCE4D494E546E7361LLU, // nst_Tnsa_IN
-    0xCA8D53534C61746ELLU, // nus_Latn_SS
-    0x6E7655534C61746ELLU, // nv_Latn_US
-    0xC2ED434E4C61746ELLU, // nxq_Latn_CN
-    0x6E794D574C61746ELLU, // ny_Latn_MW
-    0xB30D545A4C61746ELLU, // nym_Latn_TZ
-    0xB70D55474C61746ELLU, // nyn_Latn_UG
-    0xA32D47484C61746ELLU, // nzi_Latn_GH
-    0x6F6346524C61746ELLU, // oc_Latn_FR
-    0x6F6A434143616E73LLU, // oj_Cans_CA
-    0xC92E434143616E73LLU, // ojs_Cans_CA
-    0x814E43414C61746ELLU, // oka_Latn_CA
-    0x6F6D45544C61746ELLU, // om_Latn_ET
-    0x6F72494E4F727961LLU, // or_Orya_IN
-    0x6F7347454379726CLLU, // os_Cyrl_GE
-    0x824E55534F736765LLU, // osa_Osge_US
-    0xAA6E4D4E4F726B68LLU, // otk_Orkh_MN
-    0xA28E8C814F756772LLU, // oui_Ougr_143
-    0x7061504B41726162LLU, // pa_Arab_PK
-    0x7061494E47757275LLU, // pa_Guru_IN
-    0x980F50484C61746ELLU, // pag_Latn_PH
-    0xAC0F495250686C69LLU, // pal_Phli_IR
-    0xAC0F434E50686C70LLU, // pal_Phlp_CN
-    0xB00F50484C61746ELLU, // pam_Latn_PH
-    0xBC0F41574C61746ELLU, // pap_Latn_AW
-    0xD00F50574C61746ELLU, // pau_Latn_PW
-    0x8C4F46524C61746ELLU, // pcd_Latn_FR
-    0xB04F4E474C61746ELLU, // pcm_Latn_NG
-    0x886F55534C61746ELLU, // pdc_Latn_US
-    0xCC6F43414C61746ELLU, // pdt_Latn_CA
-    0xB88F49525870656FLLU, // peo_Xpeo_IR
-    0xACAF44454C61746ELLU, // pfl_Latn_DE
-    0xB4EF4C4250686E78LLU, // phn_Phnx_LB
-    0xC90F53424C61746ELLU, // pis_Latn_SB
-    0x814F494E42726168LLU, // pka_Brah_IN
-    0xB94F4B454C61746ELLU, // pko_Latn_KE
-    0x706C504C4C61746ELLU, // pl_Latn_PL
-    0xC98F49544C61746ELLU, // pms_Latn_IT
-    0xCDAF47524772656BLLU, // pnt_Grek_GR
-    0xB5CF464D4C61746ELLU, // pon_Latn_FM
-    0x81EF494E44657661LLU, // ppa_Deva_IN
-    0xB20F43414C61746ELLU, // pqm_Latn_CA
-    0x822F504B4B686172LLU, // pra_Khar_PK
-    0x8E2F495241726162LLU, // prd_Arab_IR
-    0x7073414641726162LLU, // ps_Arab_AF
-    0x707442524C61746ELLU, // pt_Latn_BR
-    0xD28F47414C61746ELLU, // puu_Latn_GA
-    0x717550454C61746ELLU, // qu_Latn_PE
-    0x8A9047544C61746ELLU, // quc_Latn_GT
-    0x9A9045434C61746ELLU, // qug_Latn_EC
-    0xA411494E44657661LLU, // raj_Deva_IN
-    0x945152454C61746ELLU, // rcf_Latn_RE
-    0xA49149444C61746ELLU, // rej_Latn_ID
-    0xB4D149544C61746ELLU, // rgn_Latn_IT
-    0x98F14D4D526F6867LLU, // rhg_Rohg_MM
-    0x8111494E4C61746ELLU, // ria_Latn_IN
-    0x95114D4154666E67LLU, // rif_Tfng_MA
-    0xC9314E5044657661LLU, // rjs_Deva_NP
-    0xCD51424442656E67LLU, // rkt_Beng_BD
-    0x726D43484C61746ELLU, // rm_Latn_CH
-    0x959146494C61746ELLU, // rmf_Latn_FI
-    0xB99143484C61746ELLU, // rmo_Latn_CH
-    0xCD91495241726162LLU, // rmt_Arab_IR
-    0xD19153454C61746ELLU, // rmu_Latn_SE
-    0x726E42494C61746ELLU, // rn_Latn_BI
-    0x99B14D5A4C61746ELLU, // rng_Latn_MZ
-    0x726F524F4C61746ELLU, // ro_Latn_RO
-    0x85D149444C61746ELLU, // rob_Latn_ID
-    0x95D1545A4C61746ELLU, // rof_Latn_TZ
-    0xB271464A4C61746ELLU, // rtm_Latn_FJ
-    0x727552554379726CLLU, // ru_Cyrl_RU
-    0x929155414379726CLLU, // rue_Cyrl_UA
-    0x9A9153424C61746ELLU, // rug_Latn_SB
-    0x727752574C61746ELLU, // rw_Latn_RW
-    0xAAD1545A4C61746ELLU, // rwk_Latn_TZ
-    0xD3114A504B616E61LLU, // ryu_Kana_JP
-    0x7361494E44657661LLU, // sa_Deva_IN
-    0x941247484C61746ELLU, // saf_Latn_GH
-    0x9C1252554379726CLLU, // sah_Cyrl_RU
-    0xC0124B454C61746ELLU, // saq_Latn_KE
-    0xC81249444C61746ELLU, // sas_Latn_ID
-    0xCC12494E4F6C636BLLU, // sat_Olck_IN
-    0xD412534E4C61746ELLU, // sav_Latn_SN
-    0xE412494E53617572LLU, // saz_Saur_IN
-    0xBC32545A4C61746ELLU, // sbp_Latn_TZ
-    0x736349544C61746ELLU, // sc_Latn_IT
-    0xA852494E44657661LLU, // sck_Deva_IN
-    0xB45249544C61746ELLU, // scn_Latn_IT
-    0xB85247424C61746ELLU, // sco_Latn_GB
-    0x7364504B41726162LLU, // sd_Arab_PK
-    0x7364494E44657661LLU, // sd_Deva_IN
-    0x7364494E4B686F6ALLU, // sd_Khoj_IN
-    0x7364494E53696E64LLU, // sd_Sind_IN
-    0x887249544C61746ELLU, // sdc_Latn_IT
-    0x9C72495241726162LLU, // sdh_Arab_IR
-    0x73654E4F4C61746ELLU, // se_Latn_NO
-    0x949243494C61746ELLU, // sef_Latn_CI
-    0x9C924D5A4C61746ELLU, // seh_Latn_MZ
-    0xA0924D584C61746ELLU, // sei_Latn_MX
-    0xC8924D4C4C61746ELLU, // ses_Latn_ML
-    0x736743464C61746ELLU, // sg_Latn_CF
-    0x80D249454F67616DLLU, // sga_Ogam_IE
-    0xC8D24C544C61746ELLU, // sgs_Latn_LT
-    0xA0F24D4154666E67LLU, // shi_Tfng_MA
-    0xB4F24D4D4D796D72LLU, // shn_Mymr_MM
-    0x73694C4B53696E68LLU, // si_Sinh_LK
-    0x8D1245544C61746ELLU, // sid_Latn_ET
-    0x736B534B4C61746ELLU, // sk_Latn_SK
-    0xC552504B41726162LLU, // skr_Arab_PK
-    0x736C53494C61746ELLU, // sl_Latn_SI
-    0xA172504C4C61746ELLU, // sli_Latn_PL
-    0xE17249444C61746ELLU, // sly_Latn_ID
-    0x736D57534C61746ELLU, // sm_Latn_WS
-    0x819253454C61746ELLU, // sma_Latn_SE
-    0x8D92414F4C61746ELLU, // smd_Latn_AO
-    0xA59253454C61746ELLU, // smj_Latn_SE
-    0xB59246494C61746ELLU, // smn_Latn_FI
-    0xBD92494C53616D72LLU, // smp_Samr_IL
-    0xC99246494C61746ELLU, // sms_Latn_FI
-    0x736E5A574C61746ELLU, // sn_Latn_ZW
-    0x85B24D594C61746ELLU, // snb_Latn_MY
-    0xA9B24D4C4C61746ELLU, // snk_Latn_ML
-    0x736F534F4C61746ELLU, // so_Latn_SO
-    0x99D2555A536F6764LLU, // sog_Sogd_UZ
-    0xD1D2544854686169LLU, // sou_Thai_TH
-    0x7371414C4C61746ELLU, // sq_Latn_AL
-    0x737252534379726CLLU, // sr_Cyrl_RS
-    0x737252534C61746ELLU, // sr_Latn_RS
-    0x8632494E536F7261LLU, // srb_Sora_IN
-    0xB63253524C61746ELLU, // srn_Latn_SR
-    0xC632534E4C61746ELLU, // srr_Latn_SN
-    0xDE32494E44657661LLU, // srx_Deva_IN
-    0x73735A414C61746ELLU, // ss_Latn_ZA
-    0xE25245524C61746ELLU, // ssy_Latn_ER
-    0x73745A414C61746ELLU, // st_Latn_ZA
-    0xC27244454C61746ELLU, // stq_Latn_DE
-    0x737549444C61746ELLU, // su_Latn_ID
-    0xAA92545A4C61746ELLU, // suk_Latn_TZ
-    0xCA92474E4C61746ELLU, // sus_Latn_GN
-    0x737653454C61746ELLU, // sv_Latn_SE
-    0x7377545A4C61746ELLU, // sw_Latn_TZ
-    0x86D2595441726162LLU, // swb_Arab_YT
-    0x8AD243444C61746ELLU, // swc_Latn_CD
-    0x9AD244454C61746ELLU, // swg_Latn_DE
-    0xD6D2494E44657661LLU, // swv_Deva_IN
-    0xB6F249444C61746ELLU, // sxn_Latn_ID
-    0xAF12424442656E67LLU, // syl_Beng_BD
-    0xC712495153797263LLU, // syr_Syrc_IQ
-    0xAF32504C4C61746ELLU, // szl_Latn_PL
-    0x7461494E54616D6CLLU, // ta_Taml_IN
-    0xA4134E5044657661LLU, // taj_Deva_NP
-    0xD83350484C61746ELLU, // tbw_Latn_PH
-    0xE053494E4B6E6461LLU, // tcy_Knda_IN
-    0x8C73434E54616C65LLU, // tdd_Tale_CN
-    0x98734E5044657661LLU, // tdg_Deva_NP
-    0x9C734E5044657661LLU, // tdh_Deva_NP
-    0xD0734D594C61746ELLU, // tdu_Latn_MY
-    0x7465494E54656C75LLU, // te_Telu_IN
-    0xB093534C4C61746ELLU, // tem_Latn_SL
-    0xB89355474C61746ELLU, // teo_Latn_UG
-    0xCC93544C4C61746ELLU, // tet_Latn_TL
-    0x7467504B41726162LLU, // tg_Arab_PK
-    0x7467544A4379726CLLU, // tg_Cyrl_TJ
-    0x7468544854686169LLU, // th_Thai_TH
-    0xACF34E5044657661LLU, // thl_Deva_NP
-    0xC0F34E5044657661LLU, // thq_Deva_NP
-    0xC4F34E5044657661LLU, // thr_Deva_NP
-    0x7469455445746869LLU, // ti_Ethi_ET
-    0x9913455245746869LLU, // tig_Ethi_ER
-    0xD5134E474C61746ELLU, // tiv_Latn_NG
-    0x746B544D4C61746ELLU, // tk_Latn_TM
-    0xAD53544B4C61746ELLU, // tkl_Latn_TK
-    0xC553415A4C61746ELLU, // tkr_Latn_AZ
-    0xCD534E5044657661LLU, // tkt_Deva_NP
-    0x746C50484C61746ELLU, // tl_Latn_PH
-    0xE173415A4C61746ELLU, // tly_Latn_AZ
-    0x9D934E454C61746ELLU, // tmh_Latn_NE
-    0x746E5A414C61746ELLU, // tn_Latn_ZA
-    0x746F544F4C61746ELLU, // to_Latn_TO
-    0x99D34D574C61746ELLU, // tog_Latn_MW
-    0xA1F350474C61746ELLU, // tpi_Latn_PG
-    0x747254524C61746ELLU, // tr_Latn_TR
-    0xD23354524C61746ELLU, // tru_Latn_TR
-    0xD63354574C61746ELLU, // trv_Latn_TW
-    0xDA33504B41726162LLU, // trw_Arab_PK
-    0x74735A414C61746ELLU, // ts_Latn_ZA
-    0x8E5347524772656BLLU, // tsd_Grek_GR
-    0x96534E5044657661LLU, // tsf_Deva_NP
-    0x9A5350484C61746ELLU, // tsg_Latn_PH
-    0xA653425454696274LLU, // tsj_Tibt_BT
-    0x747452554379726CLLU, // tt_Cyrl_RU
-    0xA67355474C61746ELLU, // ttj_Latn_UG
-    0xCA73544854686169LLU, // tts_Thai_TH
-    0xCE73415A4C61746ELLU, // ttt_Latn_AZ
-    0xB2934D574C61746ELLU, // tum_Latn_MW
-    0xAEB354564C61746ELLU, // tvl_Latn_TV
-    0xC2D34E454C61746ELLU, // twq_Latn_NE
-    0x9AF3434E54616E67LLU, // txg_Tang_CN
-    0xBAF3494E546F746FLLU, // txo_Toto_IN
-    0x747950464C61746ELLU, // ty_Latn_PF
-    0xD71352554379726CLLU, // tyv_Cyrl_RU
-    0xB3334D414C61746ELLU, // tzm_Latn_MA
-    0xA074525541676862LLU, // udi_Aghb_RU
-    0xB07452554379726CLLU, // udm_Cyrl_RU
-    0x7567434E41726162LLU, // ug_Arab_CN
-    0x75674B5A4379726CLLU, // ug_Cyrl_KZ
-    0x80D4535955676172LLU, // uga_Ugar_SY
-    0x756B55414379726CLLU, // uk_Cyrl_UA
-    0xA174464D4C61746ELLU, // uli_Latn_FM
-    0x8594414F4C61746ELLU, // umb_Latn_AO
-    0xC5B4494E42656E67LLU, // unr_Beng_IN
-    0xC5B44E5044657661LLU, // unr_Deva_NP
-    0xDDB4494E42656E67LLU, // unx_Beng_IN
-    0x7572504B41726162LLU, // ur_Arab_PK
-    0x757A414641726162LLU, // uz_Arab_AF
-    0x757A555A4C61746ELLU, // uz_Latn_UZ
-    0xA0154C5256616969LLU, // vai_Vaii_LR
-    0x76655A414C61746ELLU, // ve_Latn_ZA
-    0x889549544C61746ELLU, // vec_Latn_IT
-    0xBC9552554C61746ELLU, // vep_Latn_RU
-    0x7669564E4C61746ELLU, // vi_Latn_VN
-    0x891553584C61746ELLU, // vic_Latn_SX
-    0xC97542454C61746ELLU, // vls_Latn_BE
-    0x959544454C61746ELLU, // vmf_Latn_DE
-    0xD9954D5A4C61746ELLU, // vmw_Latn_MZ
-    0xCDD552554C61746ELLU, // vot_Latn_RU
-    0xBA3545454C61746ELLU, // vro_Latn_EE
-    0xB695545A4C61746ELLU, // vun_Latn_TZ
-    0x776142454C61746ELLU, // wa_Latn_BE
-    0x901643484C61746ELLU, // wae_Latn_CH
-    0xAC16455445746869LLU, // wal_Ethi_ET
-    0xC41650484C61746ELLU, // war_Latn_PH
-    0xBC3641554C61746ELLU, // wbp_Latn_AU
-    0xC036494E54656C75LLU, // wbq_Telu_IN
-    0xC436494E44657661LLU, // wbr_Deva_IN
-    0xC97657464C61746ELLU, // wls_Latn_WF
-    0xA1B64B4D41726162LLU, // wni_Arab_KM
-    0x776F534E4C61746ELLU, // wo_Latn_SN
-    0x9A56494E476F6E67LLU, // wsg_Gong_IN
-    0xB276494E44657661LLU, // wtm_Deva_IN
-    0xD296434E48616E73LLU, // wuu_Hans_CN
-    0xD41742524C61746ELLU, // xav_Latn_BR
-    0xB857555A43687273LLU, // xco_Chrs_UZ
-    0xC457545243617269LLU, // xcr_Cari_TR
-    0x78685A414C61746ELLU, // xh_Latn_ZA
-    0x897754524C796369LLU, // xlc_Lyci_TR
-    0x8D7754524C796469LLU, // xld_Lydi_TR
-    0x9597474547656F72LLU, // xmf_Geor_GE
-    0xB597434E4D616E69LLU, // xmn_Mani_CN
-    0xC59753444D657263LLU, // xmr_Merc_SD
-    0x81B753414E617262LLU, // xna_Narb_SA
-    0xC5B7494E44657661LLU, // xnr_Deva_IN
-    0x99D755474C61746ELLU, // xog_Latn_UG
-    0xC5F7495250727469LLU, // xpr_Prti_IR
-    0x8257594553617262LLU, // xsa_Sarb_YE
-    0xC6574E5044657661LLU, // xsr_Deva_NP
-    0xB8184D5A4C61746ELLU, // yao_Latn_MZ
-    0xBC18464D4C61746ELLU, // yap_Latn_FM
-    0xD418434D4C61746ELLU, // yav_Latn_CM
-    0x8438434D4C61746ELLU, // ybb_Latn_CM
-    0x796F4E474C61746ELLU, // yo_Latn_NG
-    0xAE3842524C61746ELLU, // yrl_Latn_BR
-    0x82984D584C61746ELLU, // yua_Latn_MX
-    0x9298434E48616E73LLU, // yue_Hans_CN
-    0x9298484B48616E74LLU, // yue_Hant_HK
-    0x7A61434E4C61746ELLU, // za_Latn_CN
-    0x981953444C61746ELLU, // zag_Latn_SD
-    0xA4794B4D41726162LLU, // zdj_Arab_KM
-    0x80994E4C4C61746ELLU, // zea_Latn_NL
-    0x9CD94D4154666E67LLU, // zgh_Tfng_MA
-    0x7A685457426F706FLLU, // zh_Bopo_TW
-    0x7A68545748616E62LLU, // zh_Hanb_TW
-    0x7A68434E48616E73LLU, // zh_Hans_CN
-    0x7A68545748616E74LLU, // zh_Hant_TW
-    0xDCF9434E4E736875LLU, // zhx_Nshu_CN
-    0xCD59434E4B697473LLU, // zkt_Kits_CN
-    0xB17954474C61746ELLU, // zlm_Latn_TG
-    0xA1994D594C61746ELLU, // zmi_Latn_MY
-    0x7A755A414C61746ELLU, // zu_Latn_ZA
-    0x833954524C61746ELLU, // zza_Latn_TR
-});
-
-const std::unordered_map<uint32_t, uint32_t> ARAB_PARENTS({
-    {0x61724145u, 0x61729420u}, // ar-AE -> ar-015
-    {0x6172445Au, 0x61729420u}, // ar-DZ -> ar-015
-    {0x61724548u, 0x61729420u}, // ar-EH -> ar-015
-    {0x61724C59u, 0x61729420u}, // ar-LY -> ar-015
-    {0x61724D41u, 0x61729420u}, // ar-MA -> ar-015
-    {0x6172544Eu, 0x61729420u}, // ar-TN -> ar-015
-});
-
-const std::unordered_map<uint32_t, uint32_t> DEVA_PARENTS({
-    {0x68690000u, 0x656E494Eu}, // hi-Latn -> en-IN
-});
-
-const std::unordered_map<uint32_t, uint32_t> HANT_PARENTS({
-    {0x7A684D4Fu, 0x7A68484Bu}, // zh-Hant-MO -> zh-Hant-HK
-});
-
-const std::unordered_map<uint32_t, uint32_t> LATN_PARENTS({
-    {0x656E80A1u, 0x656E8400u}, // en-150 -> en-001
-    {0x656E4147u, 0x656E8400u}, // en-AG -> en-001
-    {0x656E4149u, 0x656E8400u}, // en-AI -> en-001
-    {0x656E4154u, 0x656E80A1u}, // en-AT -> en-150
-    {0x656E4155u, 0x656E8400u}, // en-AU -> en-001
-    {0x656E4242u, 0x656E8400u}, // en-BB -> en-001
-    {0x656E4245u, 0x656E80A1u}, // en-BE -> en-150
-    {0x656E424Du, 0x656E8400u}, // en-BM -> en-001
-    {0x656E4253u, 0x656E8400u}, // en-BS -> en-001
-    {0x656E4257u, 0x656E8400u}, // en-BW -> en-001
-    {0x656E425Au, 0x656E8400u}, // en-BZ -> en-001
-    {0x656E4343u, 0x656E8400u}, // en-CC -> en-001
-    {0x656E4348u, 0x656E80A1u}, // en-CH -> en-150
-    {0x656E434Bu, 0x656E8400u}, // en-CK -> en-001
-    {0x656E434Du, 0x656E8400u}, // en-CM -> en-001
-    {0x656E4358u, 0x656E8400u}, // en-CX -> en-001
-    {0x656E4359u, 0x656E8400u}, // en-CY -> en-001
-    {0x656E4445u, 0x656E80A1u}, // en-DE -> en-150
-    {0x656E4447u, 0x656E8400u}, // en-DG -> en-001
-    {0x656E444Bu, 0x656E80A1u}, // en-DK -> en-150
-    {0x656E444Du, 0x656E8400u}, // en-DM -> en-001
-    {0x656E4552u, 0x656E8400u}, // en-ER -> en-001
-    {0x656E4649u, 0x656E80A1u}, // en-FI -> en-150
-    {0x656E464Au, 0x656E8400u}, // en-FJ -> en-001
-    {0x656E464Bu, 0x656E8400u}, // en-FK -> en-001
-    {0x656E464Du, 0x656E8400u}, // en-FM -> en-001
-    {0x656E4742u, 0x656E8400u}, // en-GB -> en-001
-    {0x656E4744u, 0x656E8400u}, // en-GD -> en-001
-    {0x656E4747u, 0x656E8400u}, // en-GG -> en-001
-    {0x656E4748u, 0x656E8400u}, // en-GH -> en-001
-    {0x656E4749u, 0x656E8400u}, // en-GI -> en-001
-    {0x656E474Du, 0x656E8400u}, // en-GM -> en-001
-    {0x656E4759u, 0x656E8400u}, // en-GY -> en-001
-    {0x656E484Bu, 0x656E8400u}, // en-HK -> en-001
-    {0x656E4945u, 0x656E8400u}, // en-IE -> en-001
-    {0x656E494Cu, 0x656E8400u}, // en-IL -> en-001
-    {0x656E494Du, 0x656E8400u}, // en-IM -> en-001
-    {0x656E494Eu, 0x656E8400u}, // en-IN -> en-001
-    {0x656E494Fu, 0x656E8400u}, // en-IO -> en-001
-    {0x656E4A45u, 0x656E8400u}, // en-JE -> en-001
-    {0x656E4A4Du, 0x656E8400u}, // en-JM -> en-001
-    {0x656E4B45u, 0x656E8400u}, // en-KE -> en-001
-    {0x656E4B49u, 0x656E8400u}, // en-KI -> en-001
-    {0x656E4B4Eu, 0x656E8400u}, // en-KN -> en-001
-    {0x656E4B59u, 0x656E8400u}, // en-KY -> en-001
-    {0x656E4C43u, 0x656E8400u}, // en-LC -> en-001
-    {0x656E4C52u, 0x656E8400u}, // en-LR -> en-001
-    {0x656E4C53u, 0x656E8400u}, // en-LS -> en-001
-    {0x656E4D47u, 0x656E8400u}, // en-MG -> en-001
-    {0x656E4D4Fu, 0x656E8400u}, // en-MO -> en-001
-    {0x656E4D53u, 0x656E8400u}, // en-MS -> en-001
-    {0x656E4D54u, 0x656E8400u}, // en-MT -> en-001
-    {0x656E4D55u, 0x656E8400u}, // en-MU -> en-001
-    {0x656E4D56u, 0x656E8400u}, // en-MV -> en-001
-    {0x656E4D57u, 0x656E8400u}, // en-MW -> en-001
-    {0x656E4D59u, 0x656E8400u}, // en-MY -> en-001
-    {0x656E4E41u, 0x656E8400u}, // en-NA -> en-001
-    {0x656E4E46u, 0x656E8400u}, // en-NF -> en-001
-    {0x656E4E47u, 0x656E8400u}, // en-NG -> en-001
-    {0x656E4E4Cu, 0x656E80A1u}, // en-NL -> en-150
-    {0x656E4E52u, 0x656E8400u}, // en-NR -> en-001
-    {0x656E4E55u, 0x656E8400u}, // en-NU -> en-001
-    {0x656E4E5Au, 0x656E8400u}, // en-NZ -> en-001
-    {0x656E5047u, 0x656E8400u}, // en-PG -> en-001
-    {0x656E504Bu, 0x656E8400u}, // en-PK -> en-001
-    {0x656E504Eu, 0x656E8400u}, // en-PN -> en-001
-    {0x656E5057u, 0x656E8400u}, // en-PW -> en-001
-    {0x656E5257u, 0x656E8400u}, // en-RW -> en-001
-    {0x656E5342u, 0x656E8400u}, // en-SB -> en-001
-    {0x656E5343u, 0x656E8400u}, // en-SC -> en-001
-    {0x656E5344u, 0x656E8400u}, // en-SD -> en-001
-    {0x656E5345u, 0x656E80A1u}, // en-SE -> en-150
-    {0x656E5347u, 0x656E8400u}, // en-SG -> en-001
-    {0x656E5348u, 0x656E8400u}, // en-SH -> en-001
-    {0x656E5349u, 0x656E80A1u}, // en-SI -> en-150
-    {0x656E534Cu, 0x656E8400u}, // en-SL -> en-001
-    {0x656E5353u, 0x656E8400u}, // en-SS -> en-001
-    {0x656E5358u, 0x656E8400u}, // en-SX -> en-001
-    {0x656E535Au, 0x656E8400u}, // en-SZ -> en-001
-    {0x656E5443u, 0x656E8400u}, // en-TC -> en-001
-    {0x656E544Bu, 0x656E8400u}, // en-TK -> en-001
-    {0x656E544Fu, 0x656E8400u}, // en-TO -> en-001
-    {0x656E5454u, 0x656E8400u}, // en-TT -> en-001
-    {0x656E5456u, 0x656E8400u}, // en-TV -> en-001
-    {0x656E545Au, 0x656E8400u}, // en-TZ -> en-001
-    {0x656E5547u, 0x656E8400u}, // en-UG -> en-001
-    {0x656E5643u, 0x656E8400u}, // en-VC -> en-001
-    {0x656E5647u, 0x656E8400u}, // en-VG -> en-001
-    {0x656E5655u, 0x656E8400u}, // en-VU -> en-001
-    {0x656E5753u, 0x656E8400u}, // en-WS -> en-001
-    {0x656E5A41u, 0x656E8400u}, // en-ZA -> en-001
-    {0x656E5A4Du, 0x656E8400u}, // en-ZM -> en-001
-    {0x656E5A57u, 0x656E8400u}, // en-ZW -> en-001
-    {0x65734152u, 0x6573A424u}, // es-AR -> es-419
-    {0x6573424Fu, 0x6573A424u}, // es-BO -> es-419
-    {0x65734252u, 0x6573A424u}, // es-BR -> es-419
-    {0x6573425Au, 0x6573A424u}, // es-BZ -> es-419
-    {0x6573434Cu, 0x6573A424u}, // es-CL -> es-419
-    {0x6573434Fu, 0x6573A424u}, // es-CO -> es-419
-    {0x65734352u, 0x6573A424u}, // es-CR -> es-419
-    {0x65734355u, 0x6573A424u}, // es-CU -> es-419
-    {0x6573444Fu, 0x6573A424u}, // es-DO -> es-419
-    {0x65734543u, 0x6573A424u}, // es-EC -> es-419
-    {0x65734754u, 0x6573A424u}, // es-GT -> es-419
-    {0x6573484Eu, 0x6573A424u}, // es-HN -> es-419
-    {0x65734D58u, 0x6573A424u}, // es-MX -> es-419
-    {0x65734E49u, 0x6573A424u}, // es-NI -> es-419
-    {0x65735041u, 0x6573A424u}, // es-PA -> es-419
-    {0x65735045u, 0x6573A424u}, // es-PE -> es-419
-    {0x65735052u, 0x6573A424u}, // es-PR -> es-419
-    {0x65735059u, 0x6573A424u}, // es-PY -> es-419
-    {0x65735356u, 0x6573A424u}, // es-SV -> es-419
-    {0x65735553u, 0x6573A424u}, // es-US -> es-419
-    {0x65735559u, 0x6573A424u}, // es-UY -> es-419
-    {0x65735645u, 0x6573A424u}, // es-VE -> es-419
-    {0x6E620000u, 0x6E6F0000u}, // nb -> no
-    {0x6E6E0000u, 0x6E6F0000u}, // nn -> no
-    {0x7074414Fu, 0x70745054u}, // pt-AO -> pt-PT
-    {0x70744348u, 0x70745054u}, // pt-CH -> pt-PT
-    {0x70744356u, 0x70745054u}, // pt-CV -> pt-PT
-    {0x70744751u, 0x70745054u}, // pt-GQ -> pt-PT
-    {0x70744757u, 0x70745054u}, // pt-GW -> pt-PT
-    {0x70744C55u, 0x70745054u}, // pt-LU -> pt-PT
-    {0x70744D4Fu, 0x70745054u}, // pt-MO -> pt-PT
-    {0x70744D5Au, 0x70745054u}, // pt-MZ -> pt-PT
-    {0x70745354u, 0x70745054u}, // pt-ST -> pt-PT
-    {0x7074544Cu, 0x70745054u}, // pt-TL -> pt-PT
-});
-
-const std::unordered_map<uint32_t, uint32_t> ___B_PARENTS({
-    {0x61725842u, 0x61729420u}, // ar-XB -> ar-015
-});
-
-const struct {
-    const char script[4];
-    const std::unordered_map<uint32_t, uint32_t>* map;
-} SCRIPT_PARENTS[] = {
-    {{'L', 'a', 't', 'n'}, &LATN_PARENTS},
-    {{'A', 'r', 'a', 'b'}, &ARAB_PARENTS},
-    {{'D', 'e', 'v', 'a'}, &DEVA_PARENTS},
-    {{'H', 'a', 'n', 't'}, &HANT_PARENTS},
-    {{'~', '~', '~', 'B'}, &___B_PARENTS},
-};
-
-const size_t MAX_PARENT_DEPTH = 3;
diff --git a/libs/androidfw/OWNERS b/libs/androidfw/OWNERS
index ef4cc46..47b2a1e 100644
--- a/libs/androidfw/OWNERS
+++ b/libs/androidfw/OWNERS
@@ -3,4 +3,4 @@
 patb@google.com
 
 per-file CursorWindow.cpp=omakoto@google.com
-per-file LocaleDataTables.cpp=vichang@google.com,ngeoffray@google.com
+per-file LocaleDataLookup.cpp=vichang@google.com,ngeoffray@google.com
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index de9991a..a8eb062 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -152,12 +152,11 @@
     patch->colorsOffset = patch->yDivsOffset + (patch->numYDivs * sizeof(int32_t));
 }
 
-void Res_value::copyFrom_dtoh(const Res_value& src)
-{
-    size = dtohs(src.size);
-    res0 = src.res0;
-    dataType = src.dataType;
-    data = dtohl(src.data);
+void Res_value::copyFrom_dtoh_slow(const Res_value& src) {
+  size = dtohs(src.size);
+  res0 = src.res0;
+  dataType = src.dataType;
+  data = dtohl(src.data);
 }
 
 void Res_png_9patch::deviceToFile()
@@ -2031,16 +2030,6 @@
 // --------------------------------------------------------------------
 // --------------------------------------------------------------------
 
-void ResTable_config::copyFromDeviceNoSwap(const ResTable_config& o) {
-    const size_t size = dtohl(o.size);
-    if (size >= sizeof(ResTable_config)) {
-        *this = o;
-    } else {
-        memcpy(this, &o, size);
-        memset(((uint8_t*)this)+size, 0, sizeof(ResTable_config)-size);
-    }
-}
-
 /* static */ size_t unpackLanguageOrRegion(const char in[2], const char base,
         char out[4]) {
   if (in[0] & 0x80) {
@@ -2105,34 +2094,33 @@
     return unpackLanguageOrRegion(this->country, '0', region);
 }
 
-
-void ResTable_config::copyFromDtoH(const ResTable_config& o) {
-    copyFromDeviceNoSwap(o);
-    size = sizeof(ResTable_config);
-    mcc = dtohs(mcc);
-    mnc = dtohs(mnc);
-    density = dtohs(density);
-    screenWidth = dtohs(screenWidth);
-    screenHeight = dtohs(screenHeight);
-    sdkVersion = dtohs(sdkVersion);
-    minorVersion = dtohs(minorVersion);
-    smallestScreenWidthDp = dtohs(smallestScreenWidthDp);
-    screenWidthDp = dtohs(screenWidthDp);
-    screenHeightDp = dtohs(screenHeightDp);
+void ResTable_config::copyFromDtoH_slow(const ResTable_config& o) {
+  copyFromDeviceNoSwap(o);
+  size = sizeof(ResTable_config);
+  mcc = dtohs(mcc);
+  mnc = dtohs(mnc);
+  density = dtohs(density);
+  screenWidth = dtohs(screenWidth);
+  screenHeight = dtohs(screenHeight);
+  sdkVersion = dtohs(sdkVersion);
+  minorVersion = dtohs(minorVersion);
+  smallestScreenWidthDp = dtohs(smallestScreenWidthDp);
+  screenWidthDp = dtohs(screenWidthDp);
+  screenHeightDp = dtohs(screenHeightDp);
 }
 
-void ResTable_config::swapHtoD() {
-    size = htodl(size);
-    mcc = htods(mcc);
-    mnc = htods(mnc);
-    density = htods(density);
-    screenWidth = htods(screenWidth);
-    screenHeight = htods(screenHeight);
-    sdkVersion = htods(sdkVersion);
-    minorVersion = htods(minorVersion);
-    smallestScreenWidthDp = htods(smallestScreenWidthDp);
-    screenWidthDp = htods(screenWidthDp);
-    screenHeightDp = htods(screenHeightDp);
+void ResTable_config::swapHtoD_slow() {
+  size = htodl(size);
+  mcc = htods(mcc);
+  mnc = htods(mnc);
+  density = htods(density);
+  screenWidth = htods(screenWidth);
+  screenHeight = htods(screenHeight);
+  sdkVersion = htods(sdkVersion);
+  minorVersion = htods(minorVersion);
+  smallestScreenWidthDp = htods(smallestScreenWidthDp);
+  screenWidthDp = htods(screenWidthDp);
+  screenHeightDp = htods(screenHeightDp);
 }
 
 /* static */ inline int compareLocales(const ResTable_config &l, const ResTable_config &r) {
@@ -2145,7 +2133,7 @@
     // systems should happen very infrequently (if at all.)
     // The comparison code relies on memcmp low-level optimizations that make it
     // more efficient than strncmp.
-    const char emptyScript[sizeof(l.localeScript)] = {'\0', '\0', '\0', '\0'};
+    static constexpr char emptyScript[sizeof(l.localeScript)] = {'\0', '\0', '\0', '\0'};
     const char *lScript = l.localeScriptWasComputed ? emptyScript : l.localeScript;
     const char *rScript = r.localeScriptWasComputed ? emptyScript : r.localeScript;
 
diff --git a/libs/androidfw/Util.cpp b/libs/androidfw/Util.cpp
index be55fe8..86c459f 100644
--- a/libs/androidfw/Util.cpp
+++ b/libs/androidfw/Util.cpp
@@ -32,13 +32,18 @@
 namespace util {
 
 void ReadUtf16StringFromDevice(const uint16_t* src, size_t len, std::string* out) {
-  char buf[5];
-  while (*src && len != 0) {
-    char16_t c = static_cast<char16_t>(dtohs(*src));
-    utf16_to_utf8(&c, 1, buf, sizeof(buf));
-    out->append(buf, strlen(buf));
-    ++src;
-    --len;
+  static constexpr bool kDeviceEndiannessSame = dtohs(0x1001) == 0x1001;
+  if constexpr (kDeviceEndiannessSame) {
+    *out = Utf16ToUtf8({(const char16_t*)src, strnlen16((const char16_t*)src, len)});
+  } else {
+    char buf[5];
+    while (*src && len != 0) {
+      char16_t c = static_cast<char16_t>(dtohs(*src));
+      utf16_to_utf8(&c, 1, buf, sizeof(buf));
+      out->append(buf, strlen(buf));
+      ++src;
+      --len;
+    }
   }
 }
 
@@ -63,8 +68,10 @@
   }
 
   std::string utf8;
-  utf8.resize(utf8_length);
-  utf16_to_utf8(utf16.data(), utf16.length(), &*utf8.begin(), utf8_length + 1);
+  utf8.resize_and_overwrite(utf8_length, [&utf16](char* data, size_t size) {
+    utf16_to_utf8(utf16.data(), utf16.length(), data, size + 1);
+    return size;
+  });
   return utf8;
 }
 
diff --git a/libs/androidfw/include/androidfw/ApkAssets.h b/libs/androidfw/include/androidfw/ApkAssets.h
index 231808b..3f6f466 100644
--- a/libs/androidfw/include/androidfw/ApkAssets.h
+++ b/libs/androidfw/include/androidfw/ApkAssets.h
@@ -116,7 +116,7 @@
     return resources_asset_ != nullptr && resources_asset_->isAllocated();
   }
 
-  bool IsUpToDate() const;
+  UpToDate IsUpToDate() const;
 
   // DANGER!
   // This is a destructive method that rips the assets provider out of ApkAssets object.
diff --git a/libs/androidfw/include/androidfw/AssetsProvider.h b/libs/androidfw/include/androidfw/AssetsProvider.h
index d33c325..e3b3ae4 100644
--- a/libs/androidfw/include/androidfw/AssetsProvider.h
+++ b/libs/androidfw/include/androidfw/AssetsProvider.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef ANDROIDFW_ASSETSPROVIDER_H
-#define ANDROIDFW_ASSETSPROVIDER_H
+#pragma once
 
 #include <memory>
 #include <string>
@@ -58,7 +57,7 @@
   WARN_UNUSED virtual const std::string& GetDebugName() const = 0;
 
   // Returns whether the interface provides the most recent version of its files.
-  WARN_UNUSED virtual bool IsUpToDate() const = 0;
+  WARN_UNUSED virtual UpToDate IsUpToDate() const = 0;
 
   // Creates an Asset from a file on disk.
   static std::unique_ptr<Asset> CreateAssetFromFile(const std::string& path);
@@ -95,7 +94,7 @@
 
   WARN_UNUSED std::optional<std::string_view> GetPath() const override;
   WARN_UNUSED const std::string& GetDebugName() const override;
-  WARN_UNUSED bool IsUpToDate() const override;
+  WARN_UNUSED UpToDate IsUpToDate() const override;
   WARN_UNUSED std::optional<uint32_t> GetCrc(std::string_view path) const;
 
   ~ZipAssetsProvider() override = default;
@@ -106,7 +105,7 @@
  private:
   struct PathOrDebugName;
   ZipAssetsProvider(ZipArchive* handle, PathOrDebugName&& path, package_property_t flags,
-                    time_t last_mod_time);
+                    ModDate last_mod_time);
 
   struct PathOrDebugName {
     static PathOrDebugName Path(std::string value) {
@@ -135,7 +134,7 @@
   std::unique_ptr<ZipArchive, ZipCloser> zip_handle_;
   PathOrDebugName name_;
   package_property_t flags_;
-  time_t last_mod_time_;
+  ModDate last_mod_time_;
 };
 
 // Supplies assets from a root directory.
@@ -147,7 +146,7 @@
 
   WARN_UNUSED std::optional<std::string_view> GetPath() const override;
   WARN_UNUSED const std::string& GetDebugName() const override;
-  WARN_UNUSED bool IsUpToDate() const override;
+  WARN_UNUSED UpToDate IsUpToDate() const override;
 
   ~DirectoryAssetsProvider() override = default;
  protected:
@@ -156,23 +155,23 @@
                                       bool* file_exists) const override;
 
  private:
-  explicit DirectoryAssetsProvider(std::string&& path, time_t last_mod_time);
+  explicit DirectoryAssetsProvider(std::string&& path, ModDate last_mod_time);
   std::string dir_;
-  time_t last_mod_time_;
+  ModDate last_mod_time_;
 };
 
 // Supplies assets from a `primary` asset provider and falls back to supplying assets from the
 // `secondary` asset provider if the asset cannot be found in the `primary`.
 struct MultiAssetsProvider : public AssetsProvider {
   static std::unique_ptr<AssetsProvider> Create(std::unique_ptr<AssetsProvider>&& primary,
-                                                std::unique_ptr<AssetsProvider>&& secondary);
+                                                std::unique_ptr<AssetsProvider>&& secondary = {});
 
   bool ForEachFile(const std::string& root_path,
                    base::function_ref<void(StringPiece, FileType)> f) const override;
 
   WARN_UNUSED std::optional<std::string_view> GetPath() const override;
   WARN_UNUSED const std::string& GetDebugName() const override;
-  WARN_UNUSED bool IsUpToDate() const override;
+  WARN_UNUSED UpToDate IsUpToDate() const override;
 
   ~MultiAssetsProvider() override = default;
  protected:
@@ -199,7 +198,7 @@
 
   WARN_UNUSED std::optional<std::string_view> GetPath() const override;
   WARN_UNUSED const std::string& GetDebugName() const override;
-  WARN_UNUSED bool IsUpToDate() const override;
+  WARN_UNUSED UpToDate IsUpToDate() const override;
 
   ~EmptyAssetsProvider() override = default;
  protected:
@@ -212,5 +211,3 @@
 };
 
 }  // namespace android
-
-#endif /* ANDROIDFW_ASSETSPROVIDER_H */
diff --git a/libs/androidfw/include/androidfw/Idmap.h b/libs/androidfw/include/androidfw/Idmap.h
index ac75eb3..87f3c9d 100644
--- a/libs/androidfw/include/androidfw/Idmap.h
+++ b/libs/androidfw/include/androidfw/Idmap.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef IDMAP_H_
-#define IDMAP_H_
+#pragma once
 
 #include <memory>
 #include <string>
@@ -32,6 +31,31 @@
 
 namespace android {
 
+// An enum that tracks more states than just 'up to date' or 'not' for a resources container:
+// there are several cases where we know for sure that the object can't change and won't get
+// out of date. Reporting those states to the managed layer allows it to stop checking here
+// completely, speeding up the cache lookups by dozens of milliseconds.
+enum class UpToDate : int { False, True, Always };
+
+// Combines two UpToDate values, and only accesses the second one if it matters to the result.
+template <class Getter>
+UpToDate combine(UpToDate first, Getter secondGetter) {
+  switch (first) {
+    case UpToDate::False:
+      return UpToDate::False;
+    case UpToDate::True: {
+      const auto second = secondGetter();
+      return second == UpToDate::False ? UpToDate::False : UpToDate::True;
+    }
+    case UpToDate::Always:
+      return secondGetter();
+  }
+}
+
+inline UpToDate fromBool(bool value) {
+  return value ? UpToDate::True : UpToDate::False;
+}
+
 class LoadedIdmap;
 class IdmapResMap;
 struct Idmap_header;
@@ -196,7 +220,7 @@
 
   // Returns whether the idmap file on disk has not been modified since the construction of this
   // LoadedIdmap.
-  bool IsUpToDate() const;
+  UpToDate IsUpToDate() const;
 
  protected:
   // Exposed as protected so that tests can subclass and mock this class out.
@@ -231,5 +255,3 @@
 };
 
 }  // namespace android
-
-#endif  // IDMAP_H_
diff --git a/libs/androidfw/include/androidfw/LocaleDataLookup.h b/libs/androidfw/include/androidfw/LocaleDataLookup.h
new file mode 100644
index 0000000..5a24e83
--- /dev/null
+++ b/libs/androidfw/include/androidfw/LocaleDataLookup.h
@@ -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.
+ */
+
+#pragma once
+
+#include <stddef.h>
+#include <stdint.h>
+
+
+namespace android {
+
+constexpr size_t SCRIPT_LENGTH = 4;
+
+constexpr inline uint32_t packLocale(const char* language, const char* region) {
+    const unsigned char* lang = reinterpret_cast<const unsigned char*>(language);
+    const unsigned char* reg = reinterpret_cast<const unsigned char*>(region);
+    return (static_cast<uint32_t>(lang[0]) << 24u) |
+            (static_cast<uint32_t>(lang[1]) << 16u) |
+            (static_cast<uint32_t>(reg[0]) << 8u) |
+            static_cast<uint32_t>(reg[1]);
+}
+
+constexpr inline uint32_t dropRegion(uint32_t packed_locale) {
+    return packed_locale & 0xFFFF0000LU;
+}
+
+constexpr inline bool hasRegion(uint32_t packed_locale) {
+    return (packed_locale & 0x0000FFFFLU) != 0;
+}
+
+constexpr inline uint32_t packScript(const char* script) {
+    const unsigned char* s = reinterpret_cast<const unsigned char*>(script);
+    return ((static_cast<uint32_t>(s[0]) << 24u) |
+            (static_cast<uint32_t>(s[1]) << 16u) |
+            (static_cast<uint32_t>(s[2]) <<  8u) |
+            static_cast<uint32_t>(s[3]));
+}
+
+/**
+ * Return nullptr if the key isn't found. The input packed_lang_region can be computed
+ * by android::packLocale.
+ * Note that the returned char* is either nullptr or 4-byte char seqeuence, but isn't
+ * a null-terminated string.
+ */
+const char* lookupLikelyScript(uint32_t packed_lang_region);
+/**
+ * Return false if the key isn't representative. The input lookup key can be computed
+ * by android::packLocale.
+ */
+bool isLocaleRepresentative(uint32_t language_and_region, const char* script);
+
+/**
+ * Return a parent packed key for a given script and child packed key. Return 0 if
+ * no parent is found.
+ */
+uint32_t findParentLocalePackedKey(const char* script, uint32_t packed_lang_region);
+
+uint32_t getMaxAncestorTreeDepth();
+
+} // namespace android
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index e330410..819fe4b 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -47,6 +47,8 @@
 
 namespace android {
 
+constexpr const bool kDeviceEndiannessSame = dtohs(0x1001) == 0x1001;
+
 constexpr const uint32_t kIdmapMagic = 0x504D4449u;
 constexpr const uint32_t kIdmapCurrentVersion = 0x0000000Au;
 
@@ -408,7 +410,16 @@
     typedef uint32_t data_type;
     data_type data;
 
-    void copyFrom_dtoh(const Res_value& src);
+    void copyFrom_dtoh(const Res_value& src) {
+      if constexpr (kDeviceEndiannessSame) {
+        *this = src;
+      } else {
+        copyFrom_dtoh_slow(src);
+      }
+    }
+
+   private:
+    void copyFrom_dtoh_slow(const Res_value& src);
 };
 
 /**
@@ -1254,11 +1265,32 @@
     // Varies in length from 3 to 8 chars. Zero-filled value.
     char localeNumberingSystem[8];
 
-    void copyFromDeviceNoSwap(const ResTable_config& o);
-    
-    void copyFromDtoH(const ResTable_config& o);
-    
-    void swapHtoD();
+    void copyFromDeviceNoSwap(const ResTable_config& o) {
+      const auto o_size = dtohl(o.size);
+      if (o_size >= sizeof(ResTable_config)) [[likely]] {
+        *this = o;
+      } else {
+        memcpy(this, &o, o_size);
+        memset(((uint8_t*)this) + o_size, 0, sizeof(ResTable_config) - o_size);
+      }
+      this->size = sizeof(*this);
+    }
+
+    void copyFromDtoH(const ResTable_config& o) {
+      if constexpr (kDeviceEndiannessSame) {
+        copyFromDeviceNoSwap(o);
+      } else {
+        copyFromDtoH_slow(o);
+      }
+    }
+
+    void swapHtoD() {
+      if constexpr (kDeviceEndiannessSame) {
+        ;  // noop
+      } else {
+        swapHtoD_slow();
+      }
+    }
 
     int compare(const ResTable_config& o) const;
     int compareLogical(const ResTable_config& o) const;
@@ -1384,6 +1416,10 @@
     bool isBetterThanBeforeLocale(const ResTable_config& o, const ResTable_config* requested) const;
 
     String8 toString() const;
+
+   private:
+    void copyFromDtoH_slow(const ResTable_config& o);
+    void swapHtoD_slow();
 };
 
 /**
diff --git a/libs/androidfw/include/androidfw/misc.h b/libs/androidfw/include/androidfw/misc.h
index c9ba8a0..d8ca64a 100644
--- a/libs/androidfw/include/androidfw/misc.h
+++ b/libs/androidfw/include/androidfw/misc.h
@@ -15,6 +15,7 @@
  */
 #pragma once
 
+#include <sys/stat.h>
 #include <time.h>
 
 //
@@ -64,10 +65,15 @@
 /* same, but also returns -1 if the file has already been deleted */
 ModDate getFileModDate(int fd);
 
+// Extract the modification date from the stat structure.
+ModDate getModDate(const struct ::stat& st);
+
 // Check if |path| or |fd| resides on a readonly filesystem.
 bool isReadonlyFilesystem(const char* path);
 bool isReadonlyFilesystem(int fd);
 
+bool isKnownWritablePath(const char* path);
+
 }  // namespace android
 
 // Whoever uses getFileModDate() will need this as well
diff --git a/libs/androidfw/misc.cpp b/libs/androidfw/misc.cpp
index 32f3624..26eb320 100644
--- a/libs/androidfw/misc.cpp
+++ b/libs/androidfw/misc.cpp
@@ -16,10 +16,10 @@
 
 #define LOG_TAG "misc"
 
-//
-// Miscellaneous utility functions.
-//
-#include <androidfw/misc.h>
+#include "androidfw/misc.h"
+
+#include <errno.h>
+#include <sys/stat.h>
 
 #include "android-base/logging.h"
 
@@ -28,9 +28,7 @@
 #include <sys/vfs.h>
 #endif  // __linux__
 
-#include <errno.h>
-#include <sys/stat.h>
-
+#include <array>
 #include <cstdio>
 #include <cstring>
 #include <tuple>
@@ -40,28 +38,26 @@
 /*
  * Get a file's type.
  */
-FileType getFileType(const char* fileName)
-{
-    struct stat sb;
-
-    if (stat(fileName, &sb) < 0) {
-        if (errno == ENOENT || errno == ENOTDIR)
-            return kFileTypeNonexistent;
-        else {
-            PLOG(ERROR) << "getFileType(): stat(" << fileName << ") failed";
-            return kFileTypeUnknown;
-        }
-    } else {
-        if (S_ISREG(sb.st_mode))
-            return kFileTypeRegular;
-        else if (S_ISDIR(sb.st_mode))
-            return kFileTypeDirectory;
-        else if (S_ISCHR(sb.st_mode))
-            return kFileTypeCharDev;
-        else if (S_ISBLK(sb.st_mode))
-            return kFileTypeBlockDev;
-        else if (S_ISFIFO(sb.st_mode))
-            return kFileTypeFifo;
+FileType getFileType(const char* fileName) {
+  struct stat sb;
+  if (stat(fileName, &sb) < 0) {
+    if (errno == ENOENT || errno == ENOTDIR)
+      return kFileTypeNonexistent;
+    else {
+      PLOG(ERROR) << "getFileType(): stat(" << fileName << ") failed";
+      return kFileTypeUnknown;
+    }
+  } else {
+    if (S_ISREG(sb.st_mode))
+      return kFileTypeRegular;
+    else if (S_ISDIR(sb.st_mode))
+      return kFileTypeDirectory;
+    else if (S_ISCHR(sb.st_mode))
+      return kFileTypeCharDev;
+    else if (S_ISBLK(sb.st_mode))
+      return kFileTypeBlockDev;
+    else if (S_ISFIFO(sb.st_mode))
+      return kFileTypeFifo;
 #if defined(S_ISLNK)
         else if (S_ISLNK(sb.st_mode))
             return kFileTypeSymlink;
@@ -75,7 +71,7 @@
     }
 }
 
-static ModDate getModDate(const struct stat& st) {
+ModDate getModDate(const struct stat& st) {
 #ifdef _WIN32
   return st.st_mtime;
 #elif defined(__APPLE__)
@@ -113,8 +109,14 @@
 bool isReadonlyFilesystem(int) {
     return false;
 }
+bool isKnownWritablePath(const char*) {
+  return false;
+}
 #else   // __linux__
 bool isReadonlyFilesystem(const char* path) {
+  if (isKnownWritablePath(path)) {
+    return false;
+  }
     struct statfs sfs;
     if (::statfs(path, &sfs)) {
         PLOG(ERROR) << "isReadonlyFilesystem(): statfs(" << path << ") failed";
@@ -131,6 +133,13 @@
     }
     return (sfs.f_flags & ST_RDONLY) != 0;
 }
+
+bool isKnownWritablePath(const char* path) {
+  // We know that all paths in /data/ are writable.
+  static constexpr char kRwPrefix[] = "/data/";
+  return strncmp(kRwPrefix, path, std::size(kRwPrefix) - 1) == 0;
+}
+
 #endif  // __linux__
 
 }  // namespace android
diff --git a/libs/androidfw/tests/Idmap_test.cpp b/libs/androidfw/tests/Idmap_test.cpp
index cb2e56f..22b9e69 100644
--- a/libs/androidfw/tests/Idmap_test.cpp
+++ b/libs/androidfw/tests/Idmap_test.cpp
@@ -218,10 +218,11 @@
 
   auto apk_assets = ApkAssets::LoadOverlay(temp_file.path);
   ASSERT_NE(nullptr, apk_assets);
-  ASSERT_TRUE(apk_assets->IsUpToDate());
+  ASSERT_TRUE(apk_assets->IsOverlay());
+  ASSERT_EQ(UpToDate::True, apk_assets->IsUpToDate());
 
   unlink(temp_file.path);
-  ASSERT_FALSE(apk_assets->IsUpToDate());
+  ASSERT_EQ(UpToDate::False, apk_assets->IsUpToDate());
 
   const auto sleep_duration =
       std::chrono::nanoseconds(std::max(kModDateResolutionNs, 1'000'000ull));
@@ -230,7 +231,27 @@
   base::WriteStringToFile("hello", temp_file.path);
   std::this_thread::sleep_for(sleep_duration);
 
-  ASSERT_FALSE(apk_assets->IsUpToDate());
+  ASSERT_EQ(UpToDate::False, apk_assets->IsUpToDate());
+}
+
+TEST(IdmapTestUpToDate, Combine) {
+  ASSERT_EQ(UpToDate::False, combine(UpToDate::False, [] {
+              ADD_FAILURE();  // Shouldn't get called at all.
+              return UpToDate::False;
+            }));
+
+  ASSERT_EQ(UpToDate::False, combine(UpToDate::True, [] { return UpToDate::False; }));
+
+  ASSERT_EQ(UpToDate::True, combine(UpToDate::True, [] { return UpToDate::True; }));
+  ASSERT_EQ(UpToDate::True, combine(UpToDate::True, [] { return UpToDate::Always; }));
+  ASSERT_EQ(UpToDate::True, combine(UpToDate::Always, [] { return UpToDate::True; }));
+
+  ASSERT_EQ(UpToDate::Always, combine(UpToDate::Always, [] { return UpToDate::Always; }));
+}
+
+TEST(IdmapTestUpToDate, FromBool) {
+  ASSERT_EQ(UpToDate::False, fromBool(false));
+  ASSERT_EQ(UpToDate::True, fromBool(true));
 }
 
 }  // namespace
diff --git a/libs/androidfw/tests/LocaleDataLookup_test.cpp b/libs/androidfw/tests/LocaleDataLookup_test.cpp
new file mode 100644
index 0000000..26b220d
--- /dev/null
+++ b/libs/androidfw/tests/LocaleDataLookup_test.cpp
@@ -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.
+ */
+
+#include "androidfw/LocaleDataLookup.h"
+
+#include <cstddef>
+#include <string>
+
+#include "gtest/gtest.h"
+#include "gmock/gmock.h"
+
+
+namespace android {
+
+constexpr const char NULL_SCRIPT[4] = {'\0', '\0', '\0','\0' };
+
+#define EXPECT_SCEIPT_EQ(ex, s) EXPECT_EQ(0, s == nullptr ? -1 : memcmp(ex, s, 4))
+
+// Similar to packLanguageOrRegion() in ResourceTypes.cpp
+static uint32_t encodeLanguageOrRegionLiteral(const char* in, const char base) {
+  size_t len = strlen(in);
+  if (len <= 1) {
+    return 0;
+  }
+
+  if (len == 2) {
+      return (((uint8_t) in[0]) << 8) | ((uint8_t) in[1]);
+  }
+  uint8_t first = (in[0] - base) & 0x007f;
+  uint8_t second = (in[1] - base) & 0x007f;
+  uint8_t third = (in[2] - base) & 0x007f;
+
+  return ((uint8_t) (0x80 | (third << 2) | (second >> 3)) << 8) | ((second << 5) | first);
+}
+
+static uint32_t encodeLocale(const char* language, const char* region) {
+    return (encodeLanguageOrRegionLiteral(language, 'a') << 16) |
+            encodeLanguageOrRegionLiteral(region, '0');
+}
+
+TEST(LocaleDataLookupTest, lookupLikelyScript) {
+  EXPECT_EQ(nullptr, lookupLikelyScript(encodeLocale("", "")));
+  EXPECT_SCEIPT_EQ("Latn", lookupLikelyScript(encodeLocale("en", "")));
+  EXPECT_EQ(nullptr, lookupLikelyScript(encodeLocale("en", "US")));
+  EXPECT_EQ(nullptr, lookupLikelyScript(encodeLocale("en", "GB")));
+  EXPECT_SCEIPT_EQ("Latn", lookupLikelyScript(encodeLocale("fr", "")));
+  EXPECT_EQ(nullptr, lookupLikelyScript(encodeLocale("fr", "FR")));
+
+
+  EXPECT_SCEIPT_EQ("~~~A", lookupLikelyScript(encodeLocale("en", "XA")));
+  EXPECT_SCEIPT_EQ("Latn", lookupLikelyScript(encodeLocale("ha", "")));
+  EXPECT_SCEIPT_EQ("Arab", lookupLikelyScript(encodeLocale("ha", "SD")));
+  EXPECT_EQ(nullptr, lookupLikelyScript(encodeLocale("ha", "Sd"))); // case sensitive
+  EXPECT_SCEIPT_EQ("Hans", lookupLikelyScript(encodeLocale("zh", "")));
+  EXPECT_EQ(nullptr, lookupLikelyScript(encodeLocale("zh", "CN")));
+  EXPECT_SCEIPT_EQ("Hant", lookupLikelyScript(encodeLocale("zh", "HK")));
+
+  EXPECT_SCEIPT_EQ("Nshu", lookupLikelyScript(encodeLocale("zhx", "")));
+  EXPECT_SCEIPT_EQ("Nshu", lookupLikelyScript(0xDCF90000u)); // encoded "zhx"
+}
+
+TEST(LocaleDataLookupTest, isLocaleRepresentative) {
+  EXPECT_TRUE(isLocaleRepresentative(encodeLocale("en", "US"), "Latn"));
+  EXPECT_TRUE(isLocaleRepresentative(encodeLocale("en", "GB"), "Latn"));
+  EXPECT_FALSE(isLocaleRepresentative(encodeLocale("en", "US"), NULL_SCRIPT));
+  EXPECT_FALSE(isLocaleRepresentative(encodeLocale("en", ""), "Latn"));
+  EXPECT_FALSE(isLocaleRepresentative(encodeLocale("en", ""), NULL_SCRIPT));
+  EXPECT_FALSE(isLocaleRepresentative(encodeLocale("en", "US"), "Arab"));
+
+  EXPECT_TRUE(isLocaleRepresentative(encodeLocale("fr", "FR"), "Latn"));
+
+  EXPECT_TRUE(isLocaleRepresentative(encodeLocale("zh", "CN"), "Hans"));
+  EXPECT_FALSE(isLocaleRepresentative(encodeLocale("zh", "TW"), "Hans"));
+  EXPECT_FALSE(isLocaleRepresentative(encodeLocale("zhx", "CN"), "Hans"));
+  EXPECT_FALSE(isLocaleRepresentative(0xDCF9434E, "Hans"));
+  EXPECT_TRUE(isLocaleRepresentative(encodeLocale("zhx", "CN"), "Nshu"));
+  EXPECT_TRUE(isLocaleRepresentative(0xDCF9434E, "Nshu"));
+}
+
+TEST(LocaleDataLookupTest, findParentLocalePackedKey) {
+  EXPECT_EQ(encodeLocale("en", "001"), findParentLocalePackedKey("Latn", encodeLocale("en", "GB")));
+  EXPECT_EQ(0x656E8400u, findParentLocalePackedKey("Latn", encodeLocale("en", "GB")));
+
+  EXPECT_EQ(encodeLocale("en", "IN"), findParentLocalePackedKey("Deva", encodeLocale("hi", "")));
+
+  EXPECT_EQ(encodeLocale("ar", "015"), findParentLocalePackedKey("Arab", encodeLocale("ar", "AE")));
+  EXPECT_EQ(0x61729420u, findParentLocalePackedKey("Arab", encodeLocale("ar", "AE")));
+
+  EXPECT_EQ(encodeLocale("ar", "015"), findParentLocalePackedKey("~~~B", encodeLocale("ar", "XB")));
+  EXPECT_EQ(0x61729420u, findParentLocalePackedKey("Arab", encodeLocale("ar", "AE")));
+
+  EXPECT_EQ(encodeLocale("zh", "HK"), findParentLocalePackedKey("Hant", encodeLocale("zh", "MO")));
+}
+
+}  // namespace android
diff --git a/libs/appfunctions/java/com/android/extensions/appfunctions/AppFunctionService.java b/libs/appfunctions/java/com/android/extensions/appfunctions/AppFunctionService.java
index 55f5791..81d9d81 100644
--- a/libs/appfunctions/java/com/android/extensions/appfunctions/AppFunctionService.java
+++ b/libs/appfunctions/java/com/android/extensions/appfunctions/AppFunctionService.java
@@ -22,6 +22,7 @@
 import android.annotation.MainThread;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SdkConstant;
 import android.app.Service;
 import android.content.Intent;
 import android.os.Binder;
@@ -64,6 +65,7 @@
      * service must also require the {@link BIND_APP_FUNCTION_SERVICE} permission so that other
      * applications can not abuse it.
      */
+    @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
     @NonNull
     public static final String SERVICE_INTERFACE = "android.app.appfunctions.AppFunctionService";
 
@@ -72,6 +74,7 @@
                     /* context= */ this,
                     /* onExecuteFunction= */ (platformRequest,
                             callingPackage,
+                            callingPackageSigningInfo,
                             cancellationSignal,
                             callback) -> {
                         AppFunctionService.this.onExecuteFunction(
@@ -103,15 +106,17 @@
     /**
      * 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>This method is the entry point for handling all app function requests in an app. When the
+     * system needs your AppFunctionService to perform a function, it will invoke this method.
      *
-     * <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>Each function you've registered is identified by a unique identifier. 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 is
+     * automatically generated by the AppFunctions SDK.
+     *
+     * <p>You can determine which function to execute by calling {@link
+     * ExecuteAppFunctionRequest#getFunctionIdentifier()}. This allows your service to route the
+     * incoming request to the appropriate logic for handling the specific function.
      *
      * <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
@@ -130,7 +135,5 @@
             @NonNull ExecuteAppFunctionRequest request,
             @NonNull String callingPackage,
             @NonNull CancellationSignal cancellationSignal,
-            @NonNull
-                    OutcomeReceiver<ExecuteAppFunctionResponse, AppFunctionException>
-                            callback);
+            @NonNull OutcomeReceiver<ExecuteAppFunctionResponse, AppFunctionException> callback);
 }
diff --git a/libs/hwui/jni/Typeface.cpp b/libs/hwui/jni/Typeface.cpp
index 707577d..c5095c1 100644
--- a/libs/hwui/jni/Typeface.cpp
+++ b/libs/hwui/jni/Typeface.cpp
@@ -354,6 +354,7 @@
         typeface->fStyle = minikin::FontStyle(&reader);
         typeface->fAPIStyle = reader.read<Typeface::Style>();
         typeface->fBaseWeight = reader.read<int>();
+        typeface->fIsVariationInstance = false;
         faceHandles.push_back(toJLong(typeface));
     }
     const jlongArray result = env->NewLongArray(typefaceCount);
diff --git a/libs/hwui/renderthread/HintSessionWrapper.h b/libs/hwui/renderthread/HintSessionWrapper.h
index 859cc57..4c96567 100644
--- a/libs/hwui/renderthread/HintSessionWrapper.h
+++ b/libs/hwui/renderthread/HintSessionWrapper.h
@@ -20,6 +20,7 @@
 #include <private/performance_hint_private.h>
 
 #include <future>
+#include <memory>
 #include <optional>
 #include <vector>
 
diff --git a/media/TEST_MAPPING b/media/TEST_MAPPING
index e52e0b1..6a21496 100644
--- a/media/TEST_MAPPING
+++ b/media/TEST_MAPPING
@@ -1,7 +1,10 @@
 {
   "presubmit": [
     {
-      "name": "CtsMediaBetterTogetherTestCases"
+      "name": "CtsMediaRouterTestCases"
+    },
+    {
+      "name": "CtsMediaSessionTestCases"
     },
     {
       "name": "mediaroutertest"
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
index 36f62da..c9625c4 100644
--- a/media/java/android/media/MediaCodec.java
+++ b/media/java/android/media/MediaCodec.java
@@ -5866,19 +5866,31 @@
                 @NonNull MediaCodec codec, @NonNull MediaFormat format);
 
         /**
-         * Called when the metrics for this codec have been flushed due to the
-         * start of a new subsession.
+         * Called when the metrics for this codec have been flushed "mid-stream"
+         * due to the start of a new subsession during execution.
          * <p>
-         * This can happen when the codec is reconfigured after stop(), or
-         * mid-stream e.g. if the video size changes. When this happens, the
-         * metrics for the previous subsession are flushed, and
-         * {@link MediaCodec#getMetrics} will return the metrics for the
-         * new subsession. This happens just before the {@link Callback#onOutputFormatChanged}
+         * A new codec subsession normally starts when the codec is reconfigured
+         * after stop(), but it can also happen mid-stream e.g. if the video size
+         * changes. When this happens, the metrics for the previous subsession
+         * are flushed, and {@link MediaCodec#getMetrics} will return the metrics
+         * for the new subsession.
+         * <p>
+         * For subsessions that begin due to a reconfiguration, the metrics for
+         * the prior subsession can be retrieved via {@link MediaCodec#getMetrics}
+         * prior to calling {@link #configure}.
+         * <p>
+         * When a new subsession begins "mid-stream", the metrics for the prior
+         * subsession are flushed just before the {@link Callback#onOutputFormatChanged}
          * event, so this <b>optional</b> callback is provided to be able to
          * capture the final metrics for the previous subsession.
          *
          * @param codec The MediaCodec object.
-         * @param metrics The flushed metrics for this codec.
+         * @param metrics The flushed metrics for this codec. This is a
+         *                {@link PersistableBundle} containing the set of
+         *                attributes and values available for the media being
+         *                handled by this instance of MediaCodec. The attributes
+         *                are described in {@link MetricsConstants}. Additional
+         *                vendor-specific fields may also be present.
          */
         @FlaggedApi(FLAG_SUBSESSION_METRICS)
         public void onMetricsFlushed(
diff --git a/media/java/android/media/MediaMuxer.java b/media/java/android/media/MediaMuxer.java
index 678150b..4c5efc1 100644
--- a/media/java/android/media/MediaMuxer.java
+++ b/media/java/android/media/MediaMuxer.java
@@ -18,6 +18,8 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.media.MediaCodec.BufferInfo;
 import android.os.Build;
@@ -257,6 +259,8 @@
          */
         private OutputFormat() {}
         /** @hide */
+        @SuppressLint("UnflaggedApi")
+        @TestApi
         public static final int MUXER_OUTPUT_FIRST   = 0;
         /** MPEG4 media file format*/
         public static final int MUXER_OUTPUT_MPEG_4 = MUXER_OUTPUT_FIRST;
@@ -269,6 +273,8 @@
         /** Ogg media file format*/
         public static final int MUXER_OUTPUT_OGG   = MUXER_OUTPUT_FIRST + 4;
         /** @hide */
+        @SuppressLint("UnflaggedApi")
+        @TestApi
         public static final int MUXER_OUTPUT_LAST   = MUXER_OUTPUT_OGG;
     };
 
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 245360c..3738312 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -1386,14 +1386,21 @@
                         "requestCreateSessionByManager | requestId: %d, oldSession: %s, route: %s",
                         managerRequestId, oldSession, route));
         RoutingController controller;
+        String oldSessionId = oldSession.getId();
         if (oldSession.isSystemSession()) {
             controller = getSystemController();
         } else {
             synchronized (mLock) {
-                controller = mNonSystemRoutingControllers.get(oldSession.getId());
+                controller = mNonSystemRoutingControllers.get(oldSessionId);
             }
         }
         if (controller == null) {
+            Log.w(
+                    TAG,
+                    TextUtils.formatSimple(
+                            "Ignoring requestCreateSessionByManager (requestId: %d) because no"
+                                + " controller for old session (id: %s) was found.",
+                            managerRequestId, oldSessionId));
             return;
         }
         requestCreateController(controller, route, managerRequestId);
diff --git a/media/java/android/media/quality/AmbientBacklightEvent.java b/media/java/android/media/quality/AmbientBacklightEvent.java
index b1483c6..a582c28 100644
--- a/media/java/android/media/quality/AmbientBacklightEvent.java
+++ b/media/java/android/media/quality/AmbientBacklightEvent.java
@@ -37,7 +37,7 @@
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef({AMBIENT_BACKLIGHT_EVENT_ENABLED, AMBIENT_BACKLIGHT_EVENT_DISABLED,
-            AMBIENT_BACKLIGHT_EVENT_METADATA,
+            AMBIENT_BACKLIGHT_EVENT_METADATA_AVAILABLE,
             AMBIENT_BACKLIGHT_EVENT_INTERRUPTED})
     public @interface Type {}
 
@@ -55,7 +55,7 @@
      * Event type for ambient backlight events. The ambient backlight metadata is
      * available.
      */
-    public static final int AMBIENT_BACKLIGHT_EVENT_METADATA = 3;
+    public static final int AMBIENT_BACKLIGHT_EVENT_METADATA_AVAILABLE = 3;
 
     /**
      * Event type for ambient backlight events. The ambient backlight event is preempted by another
@@ -93,7 +93,7 @@
      * Gets ambient backlight metadata.
      *
      * @return the metadata of the event. It's non-null only for
-     * {@link #AMBIENT_BACKLIGHT_EVENT_METADATA}.
+     * {@link #AMBIENT_BACKLIGHT_EVENT_METADATA_AVAILABLE}.
      */
     @Nullable
     public AmbientBacklightMetadata getMetadata() {
diff --git a/media/java/android/media/quality/AmbientBacklightMetadata.java b/media/java/android/media/quality/AmbientBacklightMetadata.java
index c295946..64eb8d9 100644
--- a/media/java/android/media/quality/AmbientBacklightMetadata.java
+++ b/media/java/android/media/quality/AmbientBacklightMetadata.java
@@ -17,6 +17,7 @@
 package android.media.quality;
 
 import android.annotation.FlaggedApi;
+import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.graphics.PixelFormat;
 import android.media.tv.flags.Flags;
@@ -25,16 +26,34 @@
 
 import androidx.annotation.NonNull;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.Arrays;
 
 /**
  * Metadata of ambient backlight.
  *
  * <p>A metadata instance is sent from ambient backlight hardware in a {@link AmbientBacklightEvent}
- * with {@link AmbientBacklightEvent#AMBIENT_BACKLIGHT_EVENT_METADATA}.
+ * with {@link AmbientBacklightEvent#AMBIENT_BACKLIGHT_EVENT_METADATA_AVAILABLE}.
  */
 @FlaggedApi(Flags.FLAG_MEDIA_QUALITY_FW)
 public final class AmbientBacklightMetadata implements Parcelable {
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({ALGORITHM_NONE, ALGORITHM_RLE})
+    public @interface CompressionAlgorithm {}
+
+    /**
+     * The compress algorithm is disabled.
+     */
+    public static final int ALGORITHM_NONE = 0;
+
+    /**
+     * The compress algorithm is run length encoding (RLE).
+     */
+    public static final int ALGORITHM_RLE = 1;
+
     @NonNull
     private final String mPackageName;
     private final int mCompressAlgorithm;
@@ -50,7 +69,7 @@
      */
     public AmbientBacklightMetadata(
             @NonNull String packageName,
-            @AmbientBacklightSettings.CompressAlgorithm int compressAlgorithm,
+            @CompressionAlgorithm int compressAlgorithm,
             @AmbientBacklightSettings.Source int source,
             @PixelFormat.Format int colorFormat,
             int horizontalZonesNumber,
@@ -86,8 +105,8 @@
     /**
      * Gets compress algorithm.
      */
-    @AmbientBacklightSettings.CompressAlgorithm
-    public int getCompressAlgorithm() {
+    @CompressionAlgorithm
+    public int getCompressionAlgorithm() {
         return mCompressAlgorithm;
     }
 
@@ -114,7 +133,7 @@
      * larger than 128.
      */
     @IntRange(from = 0, to = 128)
-    public int getHorizontalZonesNumber() {
+    public int getHorizontalZonesCount() {
         return mHorizontalZonesNumber;
     }
 
@@ -125,7 +144,7 @@
      * larger than 80.
      */
     @IntRange(from = 0, to = 80)
-    public int getVerticalZonesNumber() {
+    public int getVerticalZonesCount() {
         return mVerticalZonesNumber;
     }
 
@@ -137,11 +156,11 @@
      * @return an array of color data, in row by row (left-to-right then top-to-bottom) order of the
      * color zones.
      *
-     * @see #getHorizontalZonesNumber()
-     * @see #getVerticalZonesNumber()
+     * @see #getHorizontalZonesCount()
+     * @see #getVerticalZonesCount()
      */
     @NonNull
-    public int[] getZonesColors() {
+    public int[] getZoneColors() {
         return mZonesColors;
     }
 
diff --git a/media/java/android/media/quality/AmbientBacklightSettings.java b/media/java/android/media/quality/AmbientBacklightSettings.java
index aa06341..bd73f76 100644
--- a/media/java/android/media/quality/AmbientBacklightSettings.java
+++ b/media/java/android/media/quality/AmbientBacklightSettings.java
@@ -60,21 +60,6 @@
     public static final int SOURCE_AUDIO_VIDEO = 3;
 
 
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef({ALGORITHM_NONE, ALGORITHM_RLE})
-    public @interface CompressAlgorithm {}
-
-    /**
-     * The compress algorithm is disabled.
-     */
-    public static final int ALGORITHM_NONE = 0;
-
-    /**
-     * The compress algorithm is run length encoding (RLE).
-     */
-    public static final int ALGORITHM_RLE = 1;
-
     /**
      * The source of the ambient backlight.
      */
@@ -170,7 +155,7 @@
      * <p>A color zone is a group of lights that always display the same color.
      */
     @IntRange(from = 0)
-    public int getHorizontalZonesNumber() {
+    public int getHorizontalZonesCount() {
         return mHorizontalZonesNumber;
     }
 
@@ -180,7 +165,7 @@
      * <p>A color zone is a group of lights that always display the same color.
      */
     @IntRange(from = 0)
-    public int getVerticalZonesNumber() {
+    public int getVerticalZonesCount() {
         return mVerticalZonesNumber;
     }
 
diff --git a/media/java/android/media/quality/IMediaQualityManager.aidl b/media/java/android/media/quality/IMediaQualityManager.aidl
index 253c2d8..6e9fa1d 100644
--- a/media/java/android/media/quality/IMediaQualityManager.aidl
+++ b/media/java/android/media/quality/IMediaQualityManager.aidl
@@ -20,11 +20,12 @@
 import android.media.quality.IAmbientBacklightCallback;
 import android.media.quality.IPictureProfileCallback;
 import android.media.quality.ISoundProfileCallback;
-import android.media.quality.ParamCapability;
+import android.media.quality.ParameterCapability;
 import android.media.quality.PictureProfileHandle;
 import android.media.quality.PictureProfile;
 import android.media.quality.SoundProfileHandle;
 import android.media.quality.SoundProfile;
+import android.os.Bundle;
 import android.os.UserHandle;
 
 /**
@@ -37,10 +38,10 @@
     void removePictureProfile(in String id, in UserHandle user);
     boolean setDefaultPictureProfile(in String id, in UserHandle user);
     PictureProfile getPictureProfile(
-            in int type, in String name, in boolean includeParams, in UserHandle user);
+            in int type, in String name, in Bundle options, in UserHandle user);
     List<PictureProfile> getPictureProfilesByPackage(
-            in String packageName, in boolean includeParams, in UserHandle user);
-    List<PictureProfile> getAvailablePictureProfiles(in boolean includeParams, in UserHandle user);
+            in String packageName, in Bundle options, in UserHandle user);
+    List<PictureProfile> getAvailablePictureProfiles(in Bundle options, in UserHandle user);
     List<String> getPictureProfilePackageNames(in UserHandle user);
     List<String> getPictureProfileAllowList(in UserHandle user);
     void setPictureProfileAllowList(in List<String> packages, in UserHandle user);
@@ -51,10 +52,10 @@
     void removeSoundProfile(in String id, in UserHandle user);
     boolean setDefaultSoundProfile(in String id, in UserHandle user);
     SoundProfile getSoundProfile(
-            in int type, in String name, in boolean includeParams, in UserHandle user);
+            in int type, in String name, in Bundle options, in UserHandle user);
     List<SoundProfile> getSoundProfilesByPackage(
-            in String packageName, in boolean includeParams, in UserHandle user);
-    List<SoundProfile> getAvailableSoundProfiles(in boolean includeParams, in UserHandle user);
+            in String packageName, in Bundle options, in UserHandle user);
+    List<SoundProfile> getAvailableSoundProfiles(in Bundle options, in UserHandle user);
     List<String> getSoundProfilePackageNames(in UserHandle user);
     List<String> getSoundProfileAllowList(in UserHandle user);
     void setSoundProfileAllowList(in List<String> packages, in UserHandle user);
@@ -64,7 +65,7 @@
     void registerSoundProfileCallback(in ISoundProfileCallback cb);
     void registerAmbientBacklightCallback(in IAmbientBacklightCallback cb);
 
-    List<ParamCapability> getParamCapabilities(in List<String> names, in UserHandle user);
+    List<ParameterCapability> getParameterCapabilities(in List<String> names, in UserHandle user);
 
     boolean isSupported(in UserHandle user);
     void setAutoPictureQualityEnabled(in boolean enabled, in UserHandle user);
diff --git a/media/java/android/media/quality/IPictureProfileCallback.aidl b/media/java/android/media/quality/IPictureProfileCallback.aidl
index 7071a16..eed77f6 100644
--- a/media/java/android/media/quality/IPictureProfileCallback.aidl
+++ b/media/java/android/media/quality/IPictureProfileCallback.aidl
@@ -17,7 +17,7 @@
 
 package android.media.quality;
 
-import android.media.quality.ParamCapability;
+import android.media.quality.ParameterCapability;
 import android.media.quality.PictureProfile;
 
 /**
@@ -28,6 +28,6 @@
     void onPictureProfileAdded(in String id, in PictureProfile p);
     void onPictureProfileUpdated(in String id, in PictureProfile p);
     void onPictureProfileRemoved(in String id, in PictureProfile p);
-    void onParamCapabilitiesChanged(in String id, in List<ParamCapability> caps);
+    void onParameterCapabilitiesChanged(in String id, in List<ParameterCapability> caps);
     void onError(in String id, in int err);
 }
diff --git a/media/java/android/media/quality/ISoundProfileCallback.aidl b/media/java/android/media/quality/ISoundProfileCallback.aidl
index 30bb106..3871fb2 100644
--- a/media/java/android/media/quality/ISoundProfileCallback.aidl
+++ b/media/java/android/media/quality/ISoundProfileCallback.aidl
@@ -17,7 +17,7 @@
 
 package android.media.quality;
 
-import android.media.quality.ParamCapability;
+import android.media.quality.ParameterCapability;
 import android.media.quality.SoundProfile;
 
 /**
@@ -28,6 +28,6 @@
     void onSoundProfileAdded(in String id, in SoundProfile p);
     void onSoundProfileUpdated(in String id, in SoundProfile p);
     void onSoundProfileRemoved(in String id, in SoundProfile p);
-    void onParamCapabilitiesChanged(in String id, in List<ParamCapability> caps);
+    void onParameterCapabilitiesChanged(in String id, in List<ParameterCapability> caps);
     void onError(in String id, in int err);
 }
diff --git a/media/java/android/media/quality/MediaQualityContract.java b/media/java/android/media/quality/MediaQualityContract.java
index 6a52bcb..d1f6340 100644
--- a/media/java/android/media/quality/MediaQualityContract.java
+++ b/media/java/android/media/quality/MediaQualityContract.java
@@ -18,8 +18,12 @@
 
 
 import android.annotation.FlaggedApi;
+import android.annotation.StringDef;
 import android.media.tv.flags.Flags;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * The contract between the media quality service and applications. Contains definitions for the
  * commonly used parameter names.
@@ -27,6 +31,48 @@
 @FlaggedApi(Flags.FLAG_MEDIA_QUALITY_FW)
 public class MediaQualityContract {
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @StringDef(prefix = "LEVEL_", value = {
+            LEVEL_LOW,
+            LEVEL_MEDIUM,
+            LEVEL_HIGH,
+            LEVEL_OFF
+    })
+    public @interface Level {}
+
+    /**
+     * Low level option for a parameter.
+     *
+     * <p>This level represents that the corresponding feature is turned on with the low level
+     * option.
+     */
+    public static final String LEVEL_LOW = "level_low";
+
+    /**
+     * Medium level option for a parameter.
+     *
+     * <p>This level represents that the corresponding feature is turned on with the medium level
+     * option.
+     */
+    public static final String LEVEL_MEDIUM = "level_medium";
+
+    /**
+     * High level option for a parameter.
+     *
+     * <p>This level represents that the corresponding feature is turned on with the high level
+     * option.
+     */
+    public static final String LEVEL_HIGH = "level_high";
+
+    /**
+     * Off level for parameters.
+     *
+     * <p>This level represents that the corresponding feature is turned off.
+     */
+    public static final String LEVEL_OFF = "level_off";
+
+
     /**
      * @hide
      */
@@ -46,14 +92,22 @@
         /**
          * The brightness.
          *
-         * <p>Type: INTEGER
+         * <p>Brightness value range are from 0.0 to 1.0 (inclusive), where 0.0 represents the
+         * minimum brightness and 1.0 represents the maximum brightness. The content-unmodified
+         * value is 0.5.
+         *
+         * <p>Type: FLOAT
          */
         public static final String PARAMETER_BRIGHTNESS = "brightness";
 
         /**
          * The contrast.
          *
-         * <p>The ratio between the luminance of the brightest white and the darkest black.
+         * <p>This value represents the image contrast on an arbitrary scale from 0 to 100,
+         * where 0 represents the darkest black (black screen) and 100 represents the brightest
+         * white (brighter).
+         * The default/unmodified value for contrast is 50.
+         *
          * <p>Type: INTEGER
          */
         public static final String PARAMETER_CONTRAST = "contrast";
@@ -61,7 +115,12 @@
         /**
          * The sharpness.
          *
-         * <p>Sharpness indicates the clarity of detail.
+         * <p>Sharpness value range are from 0 to 100 (inclusive), where 0 represents the minimum
+         * sharpness that makes the image appear softer with less defined edges, 100 represents the
+         * maximum sharpness that makes the image appear halos around objects due to excessive
+         * edges.
+         * The default/unmodified value for sharpness is 50.
+         *
          * <p>Type: INTEGER
          */
         public static final String PARAMETER_SHARPNESS = "sharpness";
@@ -69,7 +128,11 @@
         /**
          * The saturation.
          *
-         * <p>Saturation indicates the intensity of the color.
+         * <p>Saturation value controls the intensity or purity of colors.
+         * Saturation values are from 0 to 100, where 0 represents grayscale (no color) and 100
+         * represents the most vivid colors.
+         * The default/unmodified value for saturation is 50.
+         *
          * <p>Type: INTEGER
          */
         public static final String PARAMETER_SATURATION = "saturation";
@@ -77,20 +140,23 @@
         /**
          * The hue.
          *
+         * <p>Hue affects the balance between red, green and blue primary colors on the screen.
+         * Hue values are from -50 to 50, where -50 represents cooler and 50 represents warmer.
+         * The default/unmodified value for hue is 0.
+         *
          * <p>Type: INTEGER
          */
         public static final String PARAMETER_HUE = "hue";
 
         /**
-         * @hide
-         */
-        public static final String PARAMETER_BACKLIGHT = "backlight";
-
-        /**
          * Adjust brightness in advance color engine. Similar to a "brightness" control on a TV
          * but acts at a lower level.
          *
+         * <p>The range is from 0 to 100 (inclusive), where 0 represents the minimum brightness and
+         * 100 represents the maximum brightness. The default/unmodified value is 50.
+         *
          * <p>Type: INTEGER
+         * @see #PARAMETER_BRIGHTNESS
          */
         public static final String PARAMETER_COLOR_TUNER_BRIGHTNESS = "color_tuner_brightness";
 
@@ -98,7 +164,11 @@
          * Adjust saturation in advance color engine. Similar to a "saturation" control on a TV
          * but acts at a lower level.
          *
+         * <p>The range is from 0 to 100 (inclusive), where 0 being completely desaturated/grayscale
+         * and 100 being the most saturated. The default/unmodified value is 50.
+         *
          * <p>Type: INTEGER
+         * @see #PARAMETER_SATURATION
          */
         public static final String PARAMETER_COLOR_TUNER_SATURATION = "color_tuner_saturation";
 
@@ -106,14 +176,21 @@
          * Adjust hue in advance color engine. Similar to a "hue" control on a TV but acts at a
          * lower level.
          *
+         * <p>The range is from -50 to 50 (inclusive), where -50 represents cooler setting for a
+         * specific color and 50 represents warmer setting for a specific color. The
+         * default/unmodified value is 0.
+         *
          * <p>Type: INTEGER
+         * @see #PARAMETER_HUE
          */
         public static final String PARAMETER_COLOR_TUNER_HUE = "color_tuner_hue";
 
         /**
-         * Advance setting for red offset. Adjust the black level of red color channels, it
-         * controls the minimum intensity of each color, affecting the shadows and
-         * dark areas of the image.
+         * Advance setting for red offset. Adjust the black level of red color channels, it controls
+         * the minimum intensity of each color, affecting the shadows and dark areas of the image.
+         *
+         * <p>The range is from 0 to 100 (inclusive), where 0 makes shadows darker and 100 makes
+         * shadows brighter. The default/unmodified value is 50.
          *
          * <p>Type: INTEGER
          */
@@ -121,8 +198,11 @@
 
         /**
          * Advance setting for green offset. Adjust the black level of green color channels, it
-         * controls the minimum intensity of each color, affecting the shadows and dark
-         * areas of the image.
+         * controls the minimum intensity of each color, affecting the shadows and dark areas of the
+         * image.
+         *
+         * <p>The range is from 0 to 100 (inclusive), where 0 makes shadows darker and 100 makes
+         * shadows brighter. The default/unmodified value is 50.
          *
          * <p>Type: INTEGER
          */
@@ -130,8 +210,11 @@
 
         /**
          * Advance setting for blue offset. Adjust the black level of blue color channels, it
-         * controls the minimum intensity of each color, affecting the shadows and dark areas
-         * of the image.
+         * controls the minimum intensity of each color, affecting the shadows and dark areas of the
+         * image.
+         *
+         * <p>The range is from 0 to 100 (inclusive), where 0 makes shadows darker and 100 makes
+         * shadows brighter. The default/unmodified value is 50.
          *
          * <p>Type: INTEGER
          */
@@ -141,6 +224,9 @@
          * Advance setting for red gain. Adjust the gain or amplification of the red color channels.
          * They control the overall intensity and white balance of red.
          *
+         * <p>The range is from 0 to 100 (inclusive), where 0 makes the red dimmer and 100 makes the
+         * red brighter. The default/unmodified value is 50.
+         *
          * <p>Type: INTEGER
          */
         public static final String PARAMETER_COLOR_TUNER_RED_GAIN = "color_tuner_red_gain";
@@ -149,49 +235,67 @@
          * Advance setting for green gain. Adjust the gain or amplification of the green color
          * channels. They control the overall intensity and white balance of green.
          *
+         * <p>The range is from 0 to 100 (inclusive), where 0 makes the green dimmer and 100 makes
+         * the green brighter. The default/unmodified value is 50.
+         *
          * <p>Type: INTEGER
          */
         public static final String PARAMETER_COLOR_TUNER_GREEN_GAIN = "color_tuner_green_gain";
 
         /**
          * Advance setting for blue gain. Adjust the gain or amplification of the blue color
-         * channels.They control the overall intensity and white balance of blue.
+         * channels. They control the overall intensity and white balance of blue.
+         *
+         * <p>The range is from 0 to 100 (inclusive), where 0 makes the blue dimmer and 100 makes
+         * the blue brighter. The default/unmodified value is 50.
          *
          * <p>Type: INTEGER
          */
         public static final String PARAMETER_COLOR_TUNER_BLUE_GAIN = "color_tuner_blue_gain";
 
         /**
-         * @hide
-         */
-        public static final String PARAMETER_AI_PQ = "ai_pq";
-
-        /**
-         * @hide
-         */
-        public static final String PARAMETER_AI_SUPER_RESOLUTION = "ai_super_resolution";
-
-        /** Noise reduction.
-         * (Off, Low, Medium, High)
-         * @see android.hardware.tv.mediaquality.QualityLevel
+         * Noise reduction.
+         *
+         * <p>Possible values:
+         * <ul>
+         *   <li>{@link #LEVEL_LOW}
+         *   <li>{@link #LEVEL_MEDIUM}
+         *   <li>{@link #LEVEL_HIGH}
+         *   <li>{@link #LEVEL_OFF}
+         * </ul>
+         * The default value is {@link #LEVEL_OFF}.
          *
          * <p>Type: STRING
          */
         public static final String PARAMETER_NOISE_REDUCTION = "noise_reduction";
 
         /**
-         *  MPEG (moving picture experts group) noise reduction
-         *  (Off, Low, Medium, High)
-         *  @see android.hardware.tv.mediaquality.QualityLevel
+         * MPEG (moving picture experts group) noise reduction.
          *
-         *  <p>Type: STRING
-         *  */
+         * <p>Possible values:
+         * <ul>
+         *   <li>{@link #LEVEL_LOW}
+         *   <li>{@link #LEVEL_MEDIUM}
+         *   <li>{@link #LEVEL_HIGH}
+         *   <li>{@link #LEVEL_OFF}
+         * </ul>
+         * The default value is {@link #LEVEL_OFF}.
+         *
+         * <p>Type: STRING
+         */
         public static final String PARAMETER_MPEG_NOISE_REDUCTION = "mpeg_noise_reduction";
 
         /**
          * Refine the flesh colors in the pictures without affecting the other colors on the screen.
-         * (Off, Low, Medium, High)
-         * @see android.hardware.tv.mediaquality.QualityLevel
+         *
+         * <p>Possible values:
+         * <ul>
+         *   <li>{@link #LEVEL_LOW}
+         *   <li>{@link #LEVEL_MEDIUM}
+         *   <li>{@link #LEVEL_HIGH}
+         *   <li>{@link #LEVEL_OFF}
+         * </ul>
+         * The default value is {@link #LEVEL_OFF}.
          *
          * <p>Type: STRING
          */
@@ -199,66 +303,75 @@
 
         /**
          * Contour noise reduction.
-         * (Off, Low, Medium, High)
-         * @see android.hardware.tv.mediaquality.QualityLevel
+         *
+         * <p>Possible values:
+         * <ul>
+         *   <li>{@link #LEVEL_LOW}
+         *   <li>{@link #LEVEL_MEDIUM}
+         *   <li>{@link #LEVEL_HIGH}
+         *   <li>{@link #LEVEL_OFF}
+         * </ul>
+         * The default value is {@link #LEVEL_OFF}.
          *
          * <p>Type: STRING
          */
         public static final String PARAMETER_DECONTOUR = "decontour";
 
         /**
-         *  Dynamically change picture luma to enhance contrast.
-         *  (Off, Low, Medium, High)
-         *  @see android.hardware.tv.mediaquality.QualityLevel
+         * Dynamically change picture luma to enhance contrast.
          *
-         *  <p>Type: STRING
+         * <p>Possible values:
+         * <ul>
+         *   <li>{@link #LEVEL_LOW}
+         *   <li>{@link #LEVEL_MEDIUM}
+         *   <li>{@link #LEVEL_HIGH}
+         *   <li>{@link #LEVEL_OFF}
+         * </ul>
+         * The default value is {@link #LEVEL_OFF}.
+         *
+         * <p>Type: STRING
          */
         public static final String PARAMETER_DYNAMIC_LUMA_CONTROL = "dynamic_luma_control";
 
         /**
-         *  Enable/disable film mode
+         * Enable/disable film mode.
          *
-         *  <p>Type: BOOLEAN
+         * <p>Type: BOOLEAN
          */
         public static final String PARAMETER_FILM_MODE = "film_mode";
 
         /**
-         * @hide
-         */
-        public static final String PARAMETER_BLACK_STRETCH = "black_stretch";
-
-        /**
-         *  Enable/disable blue color auto stretch
+         * Enable/disable blue color auto stretch
          *
-         *  <p>Type: BOOLEAN
+         * <p>Type: BOOLEAN
          */
         public static final String PARAMETER_BLUE_STRETCH = "blue_stretch";
 
         /**
-         *  Enable/disable the overall color tuning feature.
+         * Enable/disable the overall color tuning feature.
          *
-         *  <p>Type: BOOLEAN
+         * <p>Type: BOOLEAN
          */
         public static final String PARAMETER_COLOR_TUNE = "color_tune";
 
         /**
-         *  Adjust color temperature type
+         * Adjust color temperature type
          *
-         *  <p>Type: INTEGER
+         * <p>Type: STRING
          */
         public static final String PARAMETER_COLOR_TEMPERATURE = "color_temperature";
 
         /**
-         *  Enable/disable globe dimming.
+         * Enable/disable globe dimming.
          *
-         *  <p>Type: BOOLEAN
+         * <p>Type: BOOLEAN
          */
         public static final String PARAMETER_GLOBAL_DIMMING = "global_dimming";
 
         /**
-         *  Enable/disable auto adjust picture parameter based on the TV content.
+         * Enable/disable auto adjust picture parameter based on the TV content.
          *
-         *  <p>Type: BOOLEAN
+         * <p>Type: BOOLEAN
          */
         public static final String PARAMETER_AUTO_PICTURE_QUALITY_ENABLED =
                 "auto_picture_quality_enabled";
@@ -283,6 +396,12 @@
         /**
          * The audio volume balance.
          *
+         * <p>This parameter controls the balance between the left and right speakers.
+         * The valid range is -50 to 50 (inclusive), where:
+         *   - Negative values shift the balance towards the left speaker.
+         *   - Positive values shift the balance towards the right speaker.
+         *   - 0 represents a balanced output.
+         *
          * <p>Type: INTEGER
          */
         public static final String PARAMETER_BALANCE = "balance";
@@ -290,7 +409,9 @@
         /**
          * The bass.
          *
-         * <p>Bass setting adjust the low sound frequencies.
+         * <p>Bass controls the intensity of low-frequency sounds.
+         * The valid range is 0 - 100 (inclusive).
+         *
          * <p>Type: INTEGER
          */
         public static final String PARAMETER_BASS = "bass";
@@ -298,18 +419,17 @@
         /**
          * The treble.
          *
-         * <p>Treble setting adjust the high sound frequencies.
+         * <p>Treble controls the intensity of high-frequency sounds.
+         * The valid range is 0 - 100 (inclusive).
+         *
          * <p>Type: INTEGER
          */
         public static final String PARAMETER_TREBLE = "treble";
 
         /**
-         * @hide
-         */
-        public static final String PARAMETER_SOUND_MODE = "sound_mode";
-
-        /**
-         * @hide
+         * Enable/disable surround sound.
+         *
+         * <p>Type: BOOLEAN
          */
         public static final String PARAMETER_SURROUND_SOUND = "surround_sound";
 
@@ -319,32 +439,46 @@
         public static final String PARAMETER_EQUALIZER_DETAIL = "equalizer_detail";
 
         /**
-         * @hide
+         * Enable/disable speaker output.
+         *
+         * <p>Type: BOOLEAN
          */
         public static final String PARAMETER_SPEAKERS = "speakers";
 
         /**
-         * @hide
+         * Speaker delay in milliseconds.
+         *
+         * <p>Type: INTEGER
          */
-        public static final String PARAMETER_SPEAKERS_DELAY = "speakers_delay";
+        public static final String PARAMETER_SPEAKERS_DELAY_MILLIS = "speakers_delay_millis";
 
         /**
-         * @hide
+         * Enable/disable enhanced audio return channel (eARC).
+         *
+         * <p>eARC allows for higher bandwidth audio transmission over HDMI.
+         *
+         * <p>Type: BOOLEAN
          */
         public static final String PARAMETER_EARC = "earc";
 
         /**
-         * @hide
+         * Enable/disable auto volume control sound effect.
+         *
+         * <p>Type: BOOLEAN
          */
         public static final String PARAMETER_AUTO_VOLUME_CONTROL = "auto_volume_control";
 
         /**
-         * @hide
+         * Downmix mode.
+         *
+         * <p>Type: STRING
          */
         public static final String PARAMETER_DOWN_MIX_MODE = "down_mix_mode";
 
         /**
-         * @hide
+         * Enable/disable dynamic range compression (DRC) of digital theater system (DTS).
+         *
+         * <p>Type: BOOLEAN
          */
         public static final String PARAMETER_DTS_DRC = "dts_drc";
 
@@ -354,31 +488,67 @@
         public static final String PARAMETER_DOLBY_AUDIO_PROCESSING = "dolby_audio_processing";
 
         /**
-         * @hide
+         * Sound mode for dolby audio processing.
+         *
+         * <p>Type: STRING
          */
         public static final String PARAMETER_DOLBY_AUDIO_PROCESSING_SOUND_MODE =
                 "dolby_audio_processing_sound_mode";
 
         /**
-         * @hide
+         * Enable/disable Volume Leveler.
+         *
+         * <p>Volume Leveler helps to maintain a consistent volume level across different
+         * types of content and even within the same program. It minimizes the jarring jumps
+         * between loud commercials or action sequences and quiet dialogue.
+         *
+         * <p>Type: BOOLEAN
          */
         public static final String PARAMETER_DOLBY_AUDIO_PROCESSING_VOLUME_LEVELER =
                 "dolby_audio_processing_volume_leveler";
 
         /**
-         * @hide
+         * Enable/disable Surround Virtualizer.
+         *
+         * <p>Surround Virtualizer creates a virtual surround sound experience from stereo
+         * content, making it seem like the sound is coming from multiple speakers, even if
+         * you only have your TV's built-in speakers. It expands the soundstage and adds
+         * depth to the audio.
+         *
+         * <p>Type: BOOLEAN
          */
         public static final String PARAMETER_DOLBY_AUDIO_PROCESSING_SURROUND_VIRTUALIZER =
                 "dolby_audio_processing_surround_virtualizer";
 
         /**
-         * @hide
+         * Enable/disable Dolby Atmos.
+         *
+         * <p>Dolby Atmos creates a more immersive and realistic sound experience by adding
+         * a height dimension to surround sound. It allows sound to be placed and moved
+         * precisely around you, including overhead.
+         *
+         * <p>Note: To experience Dolby Atmos, you need content that has been specifically
+         * mixed in Dolby Atmos and a compatible sound system with upward-firing speakers
+         * or a Dolby Atmos soundbar.
+         *
+         * <p>Type: BOOLEAN
          */
         public static final String PARAMETER_DOLBY_AUDIO_PROCESSING_DOLBY_ATMOS =
                 "dolby_audio_processing_dolby_atmos";
 
         /**
-         * @hide
+         * Dialogue enhancer.
+         *
+         * <p>Possible values:
+         * <ul>
+         *   <li>{@link #LEVEL_LOW}
+         *   <li>{@link #LEVEL_MEDIUM}
+         *   <li>{@link #LEVEL_HIGH}
+         *   <li>{@link #LEVEL_OFF}
+         * </ul>
+         * The default value is {@link #LEVEL_OFF}.
+         *
+         * <p>Type: STRING
          */
         public static final String PARAMETER_DIALOGUE_ENHANCER = "dialogue_enhancer";
 
@@ -388,43 +558,89 @@
         public static final String PARAMETER_DTS_VIRTUAL_X = "dts_virtual_x";
 
         /**
-         * @hide
+         * Enable/disable Total Bass Harmonic Distortion (X).
+         *
+         * <p>TBHDX bass enhancement provides a richer low-frequency experience, simulating deeper
+         * bass.
+         *
+         * <p>Type: BOOLEAN
          */
         public static final String PARAMETER_DTS_VIRTUAL_X_TBHDX = "dts_virtual_x_tbhdx";
 
         /**
-         * @hide
+         * Enable/disable audio limiter.
+         *
+         * <p>It prevents excessive volume peaks that could cause distortion or speaker damage.
+         *
+         * <p>Type: BOOLEAN
          */
         public static final String PARAMETER_DTS_VIRTUAL_X_LIMITER = "dts_virtual_x_limiter";
 
         /**
-         * @hide
+         * Enable/disable the core DTS Virtual:X surround sound processing.
+         *
+         * <p>It creates an immersive, multi-channel audio experience from the speaker
+         * configuration.
+         *
+         * <p>Type: BOOLEAN
          */
         public static final String PARAMETER_DTS_VIRTUAL_X_TRU_SURROUND_X =
                 "dts_virtual_x_tru_surround_x";
 
         /**
-         * @hide
+         * Enable/disable DTS TruVolume HD.
+         *
+         * <p>It reduces the dynamic range of audio, minimizing loudness variations between content
+         * and channels.
+         *
+         * <p>Type: BOOLEAN
          */
         public static final String PARAMETER_DTS_VIRTUAL_X_TRU_VOLUME_HD =
                 "dts_virtual_x_tru_volume_hd";
 
         /**
-         * @hide
+         * Enable/disable dialog clarity.
+         *
+         * <p>It enhances the clarity and intelligibility of speech in audio content.
+         *
+         * <p>Type: BOOLEAN
          */
         public static final String PARAMETER_DTS_VIRTUAL_X_DIALOG_CLARITY =
                 "dts_virtual_x_dialog_clarity";
 
         /**
-         * @hide
+         * Enable/disable virtual X definition.
+         *
+         * <p>It applies audio processing to improve overall sound definition and clarity.
+         *
+         * <p>Type: BOOLEAN
          */
         public static final String PARAMETER_DTS_VIRTUAL_X_DEFINITION = "dts_virtual_x_definition";
 
         /**
-         * @hide
+         * Enable/disable the processing of virtual height channels.
+         *
+         * <p>It creates a more immersive audio experience by simulating sounds from above.
+         *
+         * <p>Type: BOOLEAN
          */
         public static final String PARAMETER_DTS_VIRTUAL_X_HEIGHT = "dts_virtual_x_height";
 
+        /**
+         * Digital output delay in milliseconds.
+         *
+         * <p>Type: INTEGER
+         */
+        public static final String PARAMETER_DIGITAL_OUTPUT_DELAY_MILLIS =
+                "digital_output_delay_millis";
+
+        /**
+         * Digital output mode.
+         *
+         * <p>Type: STRING
+         */
+        public static final String PARAMETER_DIGITAL_OUTPUT_MODE = "digital_output_mode";
+
 
         private SoundQuality() {
         }
diff --git a/media/java/android/media/quality/MediaQualityManager.java b/media/java/android/media/quality/MediaQualityManager.java
index 7e87462..191b938 100644
--- a/media/java/android/media/quality/MediaQualityManager.java
+++ b/media/java/android/media/quality/MediaQualityManager.java
@@ -25,6 +25,9 @@
 import android.annotation.SystemService;
 import android.content.Context;
 import android.media.tv.flags.Flags;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
 import android.os.RemoteException;
 import android.os.UserHandle;
 
@@ -36,6 +39,7 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.concurrent.Executor;
+import java.util.function.Consumer;
 
 /**
  * Central system API to the overall media quality, which arbitrates interaction between
@@ -61,6 +65,12 @@
     private final List<ActiveProcessingPictureListenerRecord> mApListenerRecords =
             new ArrayList<>();
 
+    /**
+     * A query option to include parameters in the profile. The default value is {@code false}.
+     * @hide
+     */
+    public static final String OPTION_INCLUDE_PARAMETERS = "include_parameters";
+
 
     /**
      * @hide
@@ -98,11 +108,12 @@
                 }
             }
             @Override
-            public void onParamCapabilitiesChanged(String profileId, List<ParamCapability> caps) {
+            public void onParameterCapabilitiesChanged(
+                    String profileId, List<ParameterCapability> caps) {
                 synchronized (mLock) {
                     for (PictureProfileCallbackRecord record : mPpCallbackRecords) {
                         // TODO: filter callback record
-                        record.postParamCapabilitiesChanged(profileId, caps);
+                        record.postParameterCapabilitiesChanged(profileId, caps);
                     }
                 }
             }
@@ -145,11 +156,12 @@
                 }
             }
             @Override
-            public void onParamCapabilitiesChanged(String profileId, List<ParamCapability> caps) {
+            public void onParameterCapabilitiesChanged(
+                    String profileId, List<ParameterCapability> caps) {
                 synchronized (mLock) {
                     for (SoundProfileCallbackRecord record : mSpCallbackRecords) {
                         // TODO: filter callback record
-                        record.postParamCapabilitiesChanged(profileId, caps);
+                        record.postParameterCapabilitiesChanged(profileId, caps);
                     }
                 }
             }
@@ -218,18 +230,25 @@
     /**
      * Gets picture profile by given profile type and name.
      *
+     * <p>If {@link ProfileQueryParams#areParametersIncluded()} is {@code false},
+     * {@link PictureProfile#getParameters()} of the returned profile is an empty bundle.
+     *
      * @param type the type of the profile.
      * @param name the name of the profile.
-     * @param includeParams {@code true} to include parameters in the profile; {@code false}
-     *                      otherwise.
+     * @param options the options of the query. {@code null} if default options are used.
+     *
      * @return the corresponding picture profile if available; {@code null} if the name doesn't
      * exist.
      */
     @Nullable
     public PictureProfile getPictureProfile(
-            @PictureProfile.ProfileType int type, @NonNull String name, boolean includeParams) {
+            @PictureProfile.ProfileType int type,
+            @NonNull String name,
+            @Nullable ProfileQueryParams options) {
         try {
-            return mService.getPictureProfile(type, name, includeParams, mUserHandle);
+            Bundle optionsBundle = options == null
+                    ? ProfileQueryParams.DEFAULT.toBundle() : options.toBundle();
+            return mService.getPictureProfile(type, name, optionsBundle, mUserHandle);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -239,18 +258,23 @@
     /**
      * Gets profiles that available to the given package.
      *
+     * <p>If {@link ProfileQueryParams#areParametersIncluded()} is {@code false},
+     * {@link PictureProfile#getParameters()} of the returned profiles are empty bundles.
+     *
      * @param packageName the package name of the profiles.
-     * @param includeParams {@code true} to include parameters in the profile; {@code false}
-     *                      otherwise.
+     * @param options the options of the query. {@code null} if default options are used.
      * @hide
      */
     @SystemApi
     @NonNull
     @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE)
     public List<PictureProfile> getPictureProfilesByPackage(
-            @NonNull String packageName, boolean includeParams) {
+            @NonNull String packageName, @Nullable ProfileQueryParams options) {
         try {
-            return mService.getPictureProfilesByPackage(packageName, includeParams, mUserHandle);
+            Bundle optionsBundle = options == null
+                    ? ProfileQueryParams.DEFAULT.toBundle() : options.toBundle();
+            return mService.getPictureProfilesByPackage(
+                    packageName, optionsBundle, mUserHandle);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -259,15 +283,19 @@
     /**
      * Gets profiles that available to the caller.
      *
-     * @param includeParams {@code true} to include parameters in the profile; {@code false}
-     *                      otherwise.
+     * <p>If {@link ProfileQueryParams#areParametersIncluded()} is {@code false},
+     * {@link PictureProfile#getParameters()} of the returned profiles are empty bundles.
+     *
+     * @param options the options of the query. {@code null} if default options are used.
      * @return the corresponding picture profile if available; {@code null} if the name doesn't
      * exist.
      */
     @NonNull
-    public List<PictureProfile> getAvailablePictureProfiles(boolean includeParams) {
+    public List<PictureProfile> getAvailablePictureProfiles(@Nullable ProfileQueryParams options) {
         try {
-            return mService.getAvailablePictureProfiles(includeParams, mUserHandle);
+            Bundle optionsBundle = options == null
+                    ? ProfileQueryParams.DEFAULT.toBundle() : options.toBundle();
+            return mService.getAvailablePictureProfiles(optionsBundle, mUserHandle);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -276,16 +304,19 @@
     /**
      * Sets preferred default picture profile.
      *
-     * @param id the ID of the default profile. {@code null} to unset the default profile.
+     * @param pictureProfileId the ID of the default profile. {@code null} to unset the default
+     *                         profile.
      * @return {@code true} if it's set successfully; {@code false} otherwise.
      *
+     * @see PictureProfile#getProfileId()
+     *
      * @hide
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE)
-    public boolean setDefaultPictureProfile(@Nullable String id) {
+    public boolean setDefaultPictureProfile(@Nullable String pictureProfileId) {
         try {
-            return mService.setDefaultPictureProfile(id, mUserHandle);
+            return mService.setDefaultPictureProfile(pictureProfileId, mUserHandle);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -410,17 +441,24 @@
     /**
      * Gets sound profile by given profile type and name.
      *
+     * <p>If {@link ProfileQueryParams#areParametersIncluded()} is {@code false},
+     * {@link SoundProfile#getParameters()} of the returned profile is an empty bundle.
+     *
      * @param type the type of the profile.
      * @param name the name of the profile.
-     * @param includeParams {@code true} to include parameters in the profile; {@code false}
-     *                      otherwise.
+     * @param options the options of the query. {@code null} if default options are used.
+     *
      * @return the corresponding sound profile if available; {@code null} if the name doesn't exist.
      */
     @Nullable
     public SoundProfile getSoundProfile(
-            @SoundProfile.ProfileType int type, @NonNull String name, boolean includeParams) {
+            @SoundProfile.ProfileType int type,
+            @NonNull String name,
+            @Nullable ProfileQueryParams options) {
         try {
-            return mService.getSoundProfile(type, name, includeParams, mUserHandle);
+            Bundle optionsBundle = options == null
+                    ? ProfileQueryParams.DEFAULT.toBundle() : options.toBundle();
+            return mService.getSoundProfile(type, name, optionsBundle, mUserHandle);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -430,18 +468,23 @@
     /**
      * Gets profiles that available to the given package.
      *
+     * <p>If {@link ProfileQueryParams#areParametersIncluded()} is {@code false},
+     * {@link SoundProfile#getParameters()} of the returned profiles are empty bundles.
+     *
      * @param packageName the package name of the profiles.
-     * @param includeParams {@code true} to include parameters in the profile; {@code false}
-     *                      otherwise.
+     * @param options the options of the query. {@code null} if default options are used.
+     *
      * @hide
      */
     @SystemApi
     @NonNull
     @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE)
     public List<SoundProfile> getSoundProfilesByPackage(
-            @NonNull String packageName, boolean includeParams) {
+            @NonNull String packageName, @Nullable ProfileQueryParams options) {
         try {
-            return mService.getSoundProfilesByPackage(packageName, includeParams, mUserHandle);
+            Bundle optionsBundle = options == null
+                    ? ProfileQueryParams.DEFAULT.toBundle() : options.toBundle();
+            return mService.getSoundProfilesByPackage(packageName, optionsBundle, mUserHandle);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -450,15 +493,19 @@
     /**
      * Gets profiles that available to the caller package.
      *
-     * @param includeParams {@code true} to include parameters in the profile; {@code false}
-     *                      otherwise.
+     * <p>If {@link ProfileQueryParams#areParametersIncluded()} is {@code false},
+     * {@link SoundProfile#getParameters()} of the returned profiles are empty bundles.
+     *
+     * @param options the options of the query. {@code null} if default options are used.
      *
      * @return the corresponding sound profile if available; {@code null} if the none available.
      */
     @NonNull
-    public List<SoundProfile> getAvailableSoundProfiles(boolean includeParams) {
+    public List<SoundProfile> getAvailableSoundProfiles(@Nullable ProfileQueryParams options) {
         try {
-            return mService.getAvailableSoundProfiles(includeParams, mUserHandle);
+            Bundle optionsBundle = options == null
+                    ? ProfileQueryParams.DEFAULT.toBundle() : options.toBundle();
+            return mService.getAvailableSoundProfiles(optionsBundle, mUserHandle);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -467,16 +514,19 @@
     /**
      * Sets preferred default sound profile.
      *
-     * @param id the ID of the default profile. {@code null} to unset the default profile.
+     * @param soundProfileId the ID of the default profile. {@code null} to unset the default
+     *                       profile.
      * @return {@code true} if it's set successfully; {@code false} otherwise.
      *
+     * @see SoundProfile#getProfileId()
+     *
      * @hide
      */
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_SOUND_QUALITY_SERVICE)
-    public boolean setDefaultSoundProfile(@Nullable String id) {
+    public boolean setDefaultSoundProfile(@Nullable String soundProfileId) {
         try {
-            return mService.setDefaultSoundProfile(id, mUserHandle);
+            return mService.setDefaultSoundProfile(soundProfileId, mUserHandle);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -548,11 +598,17 @@
 
     /**
      * Gets capability information of the given parameters.
+     *
+     * <p>If a name isn't found, a corresponding {@link ParameterCapability} instance is in the
+     * return list, and {@link ParameterCapability#isSupported()} is {@code false}.
+     *
+     * @param names the parameter names. Commonly used names can be found in
+     * {@link MediaQualityContract}. Vendor-defined names are also permitted.
      */
     @NonNull
-    public List<ParamCapability> getParamCapabilities(@NonNull List<String> names) {
+    public List<ParameterCapability> getParameterCapabilities(@NonNull List<String> names) {
         try {
-            return mService.getParamCapabilities(names, mUserHandle);
+            return mService.getParameterCapabilities(names, mUserHandle);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -836,11 +892,12 @@
             });
         }
 
-        public void postParamCapabilitiesChanged(final String id, List<ParamCapability> caps) {
+        public void postParameterCapabilitiesChanged(
+                final String id, List<ParameterCapability> caps) {
             mExecutor.execute(new Runnable() {
                 @Override
                 public void run() {
-                    mCallback.onParamCapabilitiesChanged(id, caps);
+                    mCallback.onParameterCapabilitiesChanged(id, caps);
                 }
             });
         }
@@ -896,11 +953,12 @@
             });
         }
 
-        public void postParamCapabilitiesChanged(final String id, List<ParamCapability> caps) {
+        public void postParameterCapabilitiesChanged(
+                final String id, List<ParameterCapability> caps) {
             mExecutor.execute(new Runnable() {
                 @Override
                 public void run() {
-                    mCallback.onParamCapabilitiesChanged(id, caps);
+                    mCallback.onParameterCapabilitiesChanged(id, caps);
                 }
             });
         }
@@ -990,8 +1048,8 @@
          *                  is no associated profile
          * @param updatedCaps the updated capabilities.
          */
-        public void onParamCapabilitiesChanged(
-                @Nullable String profileId, @NonNull List<ParamCapability> updatedCaps) {
+        public void onParameterCapabilitiesChanged(
+                @Nullable String profileId, @NonNull List<ParameterCapability> updatedCaps) {
         }
     }
 
@@ -1047,33 +1105,19 @@
          *                  is no associated profile
          * @param updatedCaps the updated capabilities.
          */
-        public void onParamCapabilitiesChanged(
-                @Nullable String profileId, @NonNull List<ParamCapability> updatedCaps) {
+        public void onParameterCapabilitiesChanged(
+                @Nullable String profileId, @NonNull List<ParameterCapability> updatedCaps) {
         }
     }
 
     /**
      * Callback used to monitor status of ambient backlight.
      */
-    public abstract static class AmbientBacklightCallback {
+    public interface AmbientBacklightCallback {
         /**
          * Called when new ambient backlight event is emitted.
          */
-        public void onAmbientBacklightEvent(@NonNull AmbientBacklightEvent event) {
-        }
-    }
-
-    /**
-     * Listener used to monitor status of active pictures.
-     */
-    public interface ActiveProcessingPictureListener {
-        /**
-         * Called when active pictures are changed.
-         *
-         * @param activeProcessingPictures contents currently undergoing picture processing.
-         */
-        void onActiveProcessingPicturesChanged(
-                @NonNull List<ActiveProcessingPicture> activeProcessingPictures);
+        void onAmbientBacklightEvent(@NonNull AmbientBacklightEvent event);
     }
 
     /**
@@ -1081,7 +1125,7 @@
      */
     public void addActiveProcessingPictureListener(
             @CallbackExecutor @NonNull Executor executor,
-            @NonNull ActiveProcessingPictureListener listener) {
+            @NonNull Consumer<List<ActiveProcessingPicture>> listener) {
         Preconditions.checkNotNull(listener);
         Preconditions.checkNotNull(executor);
         synchronized (mLock) {
@@ -1100,7 +1144,7 @@
     @RequiresPermission(android.Manifest.permission.MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE)
     public void addGlobalActiveProcessingPictureListener(
             @NonNull Executor executor,
-            @NonNull ActiveProcessingPictureListener listener) {
+            @NonNull Consumer<List<ActiveProcessingPicture>> listener) {
         Preconditions.checkNotNull(listener);
         Preconditions.checkNotNull(executor);
         synchronized (mLock) {
@@ -1114,7 +1158,7 @@
      * Removes an active picture listener for the contents.
      */
     public void removeActiveProcessingPictureListener(
-            @NonNull ActiveProcessingPictureListener listener) {
+            @NonNull Consumer<List<ActiveProcessingPicture>> listener) {
         Preconditions.checkNotNull(listener);
         synchronized (mLock) {
             for (Iterator<ActiveProcessingPictureListenerRecord> it = mApListenerRecords.iterator();
@@ -1129,19 +1173,107 @@
     }
 
     private static final class ActiveProcessingPictureListenerRecord {
-        private final ActiveProcessingPictureListener mListener;
+        private final Consumer<List<ActiveProcessingPicture>> mListener;
         private final Executor mExecutor;
         private final boolean mIsGlobal;
 
         ActiveProcessingPictureListenerRecord(
-                ActiveProcessingPictureListener listener, Executor executor, boolean isGlobal) {
+                Consumer<List<ActiveProcessingPicture>> listener,
+                Executor executor,
+                boolean isGlobal) {
             mListener = listener;
             mExecutor = executor;
             mIsGlobal = isGlobal;
         }
 
-        public ActiveProcessingPictureListener getListener() {
+        public Consumer<List<ActiveProcessingPicture>> getListener() {
             return mListener;
         }
     }
+
+    /**
+     * Options for profile queries.
+     */
+    public static final class ProfileQueryParams implements Parcelable {
+
+        private final boolean mParametersIncluded;
+        private static final ProfileQueryParams DEFAULT = new Builder().build();
+
+        private ProfileQueryParams(Parcel in) {
+            mParametersIncluded = in.readBoolean();
+        }
+
+        /** @hide */
+        public ProfileQueryParams(boolean parametersIncluded) {
+            mParametersIncluded = parametersIncluded;
+        }
+
+        @NonNull
+        public static final Creator<ProfileQueryParams> CREATOR =
+                new Creator<ProfileQueryParams>() {
+                    @Override
+                    public ProfileQueryParams createFromParcel(Parcel in) {
+                        return new ProfileQueryParams(in);
+                    }
+
+                    @Override
+                    public ProfileQueryParams[] newArray(int size) {
+                        return new ProfileQueryParams[size];
+                    }
+                };
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
+            dest.writeBoolean(mParametersIncluded);
+        }
+
+        /**
+         * Returns {@code true} if the parameters need to be included in query results.
+         * {@code false} otherwise.
+         */
+        public boolean areParametersIncluded() {
+            return mParametersIncluded;
+        }
+
+        private Bundle toBundle() {
+            Bundle bundle = new Bundle();
+            bundle.putBoolean(OPTION_INCLUDE_PARAMETERS, mParametersIncluded);
+            return bundle;
+        }
+
+        /**
+         * A builder for {@link ProfileQueryParams}.
+         */
+        public static final class Builder {
+            private boolean mParametersIncluded;
+
+            /**
+             * Sets the query option to include parameters in the profile or not.
+             *
+             * <p>The default value is {@code false}.
+             *
+             * @see ProfileQueryParams#areParametersIncluded()
+             */
+            @SuppressLint("MissingGetterMatchingBuilder")
+            @NonNull
+            public Builder setParametersIncluded(boolean included) {
+                mParametersIncluded = included;
+                return this;
+            }
+
+
+            /**
+             * Builds the instance.
+             */
+            @NonNull
+            public ProfileQueryParams build() {
+                return new ProfileQueryParams(mParametersIncluded);
+            }
+        }
+    }
 }
diff --git a/media/java/android/media/quality/ParamCapability.aidl b/media/java/android/media/quality/ParameterCapability.aidl
similarity index 95%
rename from media/java/android/media/quality/ParamCapability.aidl
rename to media/java/android/media/quality/ParameterCapability.aidl
index b43409d..eb2ac97 100644
--- a/media/java/android/media/quality/ParamCapability.aidl
+++ b/media/java/android/media/quality/ParameterCapability.aidl
@@ -16,4 +16,4 @@
 
 package android.media.quality;
 
-parcelable ParamCapability;
+parcelable ParameterCapability;
diff --git a/media/java/android/media/quality/ParamCapability.java b/media/java/android/media/quality/ParameterCapability.java
similarity index 81%
rename from media/java/android/media/quality/ParamCapability.java
rename to media/java/android/media/quality/ParameterCapability.java
index ed11abd..7a28a36 100644
--- a/media/java/android/media/quality/ParamCapability.java
+++ b/media/java/android/media/quality/ParameterCapability.java
@@ -33,17 +33,23 @@
  * Capability info of media quality parameters
  */
 @FlaggedApi(Flags.FLAG_MEDIA_QUALITY_FW)
-public final class ParamCapability implements Parcelable {
+public final class ParameterCapability implements Parcelable {
 
     /** @hide */
     @IntDef(flag = true, prefix = { "TYPE_" }, value = {
+            TYPE_NONE,
             TYPE_INT,
             TYPE_LONG,
             TYPE_DOUBLE,
             TYPE_STRING,
     })
     @Retention(RetentionPolicy.SOURCE)
-    public @interface ParamType {}
+    public @interface ParameterType {}
+
+    /**
+     * None parameter type. It's used when a parameter is not supported.
+     */
+    public static final int TYPE_NONE = 0;
 
     /**
      * Integer parameter type
@@ -98,13 +104,13 @@
     @NonNull
     private final String mName;
     private final boolean mIsSupported;
-    @ParamType
+    @ParameterType
     private final int mType;
     @NonNull
     private final Bundle mCaps;
 
     /** @hide */
-    protected ParamCapability(Parcel in) {
+    protected ParameterCapability(Parcel in) {
         mName = in.readString();
         mIsSupported = in.readBoolean();
         mType = in.readInt();
@@ -125,25 +131,25 @@
     }
 
     @NonNull
-    public static final Creator<ParamCapability> CREATOR = new Creator<ParamCapability>() {
+    public static final Creator<ParameterCapability> CREATOR = new Creator<ParameterCapability>() {
         @Override
-        public ParamCapability createFromParcel(Parcel in) {
-            return new ParamCapability(in);
+        public ParameterCapability createFromParcel(Parcel in) {
+            return new ParameterCapability(in);
         }
 
         @Override
-        public ParamCapability[] newArray(int size) {
-            return new ParamCapability[size];
+        public ParameterCapability[] newArray(int size) {
+            return new ParameterCapability[size];
         }
     };
 
 
     /**
-     * Creates a new ParamCapability.
+     * Creates a new ParameterCapability.
      *
      * @hide
      */
-    public ParamCapability(
+    public ParameterCapability(
             @NonNull String name,
             boolean isSupported,
             int type,
@@ -158,7 +164,7 @@
      * Gets parameter name.
      */
     @NonNull
-    public String getParamName() {
+    public String getParameterName() {
         return mName;
     }
 
@@ -171,9 +177,11 @@
 
     /**
      * Gets parameter type.
+     *
+     * <p>It's {@link #TYPE_NONE} if {@link #isSupported()} is {@code false}.
      */
-    @ParamType
-    public int getParamType() {
+    @ParameterType
+    public int getParameterType() {
         return mType;
     }
 
diff --git a/media/java/android/media/quality/PictureProfile.java b/media/java/android/media/quality/PictureProfile.java
index 6064485..8a585ef 100644
--- a/media/java/android/media/quality/PictureProfile.java
+++ b/media/java/android/media/quality/PictureProfile.java
@@ -181,7 +181,7 @@
      * Gets profile ID.
      *
      * <p>A profile ID is a globally unique ID generated and assigned by the system. For profile
-     * objects retrieved from system (e.g {@link MediaQualityManager#getAvailablePictureProfiles()})
+     * objects retrieved from system (e.g {@link MediaQualityManager#getAvailablePictureProfiles})
      * this profile ID is non-null; For profiles built locally with {@link Builder}, it's
      * {@code null}.
      *
@@ -249,6 +249,8 @@
      *
      * <p>The keys of commonly used parameters can be found in
      * {@link MediaQualityContract.PictureQuality}.
+     *
+     * @return The profile parameters. Empty bundle if parameters are not included in a query.
      */
     @NonNull
     public PersistableBundle getParameters() {
diff --git a/media/java/android/media/quality/SoundProfile.java b/media/java/android/media/quality/SoundProfile.java
index 1dd59ab..971aa6f 100644
--- a/media/java/android/media/quality/SoundProfile.java
+++ b/media/java/android/media/quality/SoundProfile.java
@@ -50,6 +50,7 @@
     private final PersistableBundle mParams;
     private final SoundProfileHandle mHandle;
 
+
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(flag = false, prefix = "TYPE_", value = {
@@ -180,7 +181,7 @@
      * Gets profile ID.
      *
      * <p>A profile ID is a globally unique ID generated and assigned by the system. For profile
-     * objects retrieved from system (e.g {@link MediaQualityManager#getAvailableSoundProfiles()})
+     * objects retrieved from system (e.g {@link MediaQualityManager#getAvailableSoundProfiles})
      * this profile ID is non-null; For profiles built locally with {@link Builder}, it's
      * {@code null}.
      *
@@ -248,6 +249,8 @@
      *
      * <p>The keys of commonly used parameters can be found in
      * {@link MediaQualityContract.SoundQuality}.
+     *
+     * @return The profile parameters. Empty bundle if parameters are not included in a query.
      */
     @NonNull
     public PersistableBundle getParameters() {
diff --git a/media/java/android/media/soundtrigger/SoundTriggerManager.java b/media/java/android/media/soundtrigger/SoundTriggerManager.java
index 3d0c406..213bc06 100644
--- a/media/java/android/media/soundtrigger/SoundTriggerManager.java
+++ b/media/java/android/media/soundtrigger/SoundTriggerManager.java
@@ -27,6 +27,7 @@
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.annotation.TestApi;
+import android.annotation.WorkerThread;
 import android.app.ActivityThread;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
@@ -475,6 +476,7 @@
     @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER)
     @UnsupportedAppUsage
     @FlaggedApi(Flags.FLAG_MANAGER_API)
+    @WorkerThread
     public int loadSoundModel(@NonNull SoundModel soundModel) {
         if (mSoundTriggerSession == null) {
             throw new IllegalStateException("No underlying SoundTriggerModule available");
@@ -518,6 +520,7 @@
     @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER)
     @UnsupportedAppUsage
     @FlaggedApi(Flags.FLAG_MANAGER_API)
+    @WorkerThread
     public int startRecognition(@NonNull UUID soundModelId, @Nullable Bundle params,
         @NonNull ComponentName detectionService, @NonNull RecognitionConfig config) {
         Objects.requireNonNull(soundModelId);
@@ -544,6 +547,7 @@
     @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER)
     @UnsupportedAppUsage
     @FlaggedApi(Flags.FLAG_MANAGER_API)
+    @WorkerThread
     public int stopRecognition(@NonNull UUID soundModelId) {
         if (mSoundTriggerSession == null) {
             throw new IllegalStateException("No underlying SoundTriggerModule available");
@@ -568,6 +572,7 @@
     @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER)
     @UnsupportedAppUsage
     @FlaggedApi(Flags.FLAG_MANAGER_API)
+    @WorkerThread
     public int unloadSoundModel(@NonNull UUID soundModelId) {
         if (mSoundTriggerSession == null) {
             throw new IllegalStateException("No underlying SoundTriggerModule available");
@@ -587,6 +592,7 @@
     @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER)
     @UnsupportedAppUsage
     @FlaggedApi(Flags.FLAG_MANAGER_API)
+    @WorkerThread
     public boolean isRecognitionActive(@NonNull UUID soundModelId) {
         if (soundModelId == null || mSoundTriggerSession == null) {
             return false;
@@ -624,6 +630,7 @@
     @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER)
     @UnsupportedAppUsage
     @FlaggedApi(Flags.FLAG_MANAGER_API)
+    @WorkerThread
     public int getModelState(@NonNull UUID soundModelId) {
         if (mSoundTriggerSession == null) {
             throw new IllegalStateException("No underlying SoundTriggerModule available");
diff --git a/media/java/android/media/tv/TvInputServiceExtensionManager.java b/media/java/android/media/tv/TvInputServiceExtensionManager.java
index b876bcf..d33ac92 100644
--- a/media/java/android/media/tv/TvInputServiceExtensionManager.java
+++ b/media/java/android/media/tv/TvInputServiceExtensionManager.java
@@ -22,7 +22,6 @@
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
 import android.annotation.StringDef;
-import android.annotation.SystemApi;
 import android.media.tv.flags.Flags;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -45,7 +44,6 @@
  *
  * @hide
  */
-@SystemApi
 @FlaggedApi(Flags.FLAG_TIF_EXTENSION_STANDARDIZATION)
 public final class TvInputServiceExtensionManager {
     private static final String TAG = "TvInputServiceExtensionManager";
@@ -65,7 +63,6 @@
     private static final String ANALOG_PACKAGE = "android.media.tv.extension.analog.";
     private static final String TUNE_PACKAGE = "android.media.tv.extension.tune.";
 
-    /** @hide */
     @IntDef(prefix = {"REGISTER_"}, value = {
             REGISTER_SUCCESS,
             REGISTER_FAIL_NAME_NOT_STANDARDIZED,
@@ -93,7 +90,6 @@
      */
     public static final int REGISTER_FAIL_REMOTE_EXCEPTION = 3;
 
-    /** @hide */
     @StringDef({
             ISCAN_INTERFACE,
             ISCAN_SESSION,
@@ -685,8 +681,6 @@
 
     /**
      * Function to return available extension interface names
-     *
-     * @hide
      */
     public static @NonNull List<String> getStandardExtensionInterfaceNames() {
         return new ArrayList<>(sTisExtensions);
@@ -711,10 +705,7 @@
      *         {@link #REGISTER_FAIL_IMPLEMENTATION_NOT_STANDARDIZED} on failure due to IBinder not
      *              implementing standardized AIDL interface
      *         {@link #REGISTER_FAIL_REMOTE_EXCEPTION} on failure due to remote exception
-     *
-     * @hide
      */
-    @SystemApi
     @RequiresPermission(android.Manifest.permission.TV_INPUT_HARDWARE)
     @RegisterResult
     public int registerExtensionIBinder(@StandardizedExtensionName @NonNull String extensionName,
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index f629c88..b30b779 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -320,6 +320,9 @@
     ASystemFontIterator_open; # introduced=29
     ASystemFontIterator_close; # introduced=29
     ASystemFontIterator_next; # introduced=29
+    ASystemHealth_getMaxCpuHeadroomTidsSize; # introduced=36
+    ASystemHealth_getCpuHeadroomCalculationWindowRange; # introduced=36
+    ASystemHealth_getGpuHeadroomCalculationWindowRange; # introduced=36
     ASystemHealth_getCpuHeadroom; # introduced=36
     ASystemHealth_getGpuHeadroom; # introduced=36
     ASystemHealth_getCpuHeadroomMinIntervalMillis; # introduced=36
diff --git a/native/android/performance_hint.cpp b/native/android/performance_hint.cpp
index 7e65523..1e6a7b7 100644
--- a/native/android/performance_hint.cpp
+++ b/native/android/performance_hint.cpp
@@ -859,7 +859,7 @@
         std::vector<ANativeWindow*> windowVec(windows, windows + numWindows);
         for (auto&& window : windowVec) {
             Surface* surface = static_cast<Surface*>(window);
-            if (Surface::isValid(surface)) {
+            if (surface != nullptr) {
                 const sp<IBinder>& handle = surface->getSurfaceControlHandle();
                 if (handle != nullptr) {
                     out.push_back(handle);
@@ -1269,9 +1269,6 @@
                                             const char* debugName) {
     VALIDATE_PTR(session)
     VALIDATE_PTR(debugName)
-    if (!useNewLoadHintBehavior()) {
-        return ENOTSUP;
-    }
     return session->notifyWorkloadIncrease(cpu, gpu, debugName);
 }
 
@@ -1279,9 +1276,6 @@
                                          const char* debugName) {
     VALIDATE_PTR(session)
     VALIDATE_PTR(debugName)
-    if (!useNewLoadHintBehavior()) {
-        return ENOTSUP;
-    }
     return session->notifyWorkloadReset(cpu, gpu, debugName);
 }
 
@@ -1289,9 +1283,6 @@
                                          const char* debugName) {
     VALIDATE_PTR(session)
     VALIDATE_PTR(debugName)
-    if (!useNewLoadHintBehavior()) {
-        return ENOTSUP;
-    }
     return session->notifyWorkloadSpike(cpu, gpu, debugName);
 }
 
diff --git a/native/android/system_health.cpp b/native/android/system_health.cpp
index f3fa9f6..5c07ac7 100644
--- a/native/android/system_health.cpp
+++ b/native/android/system_health.cpp
@@ -31,26 +31,28 @@
 struct ACpuHeadroomParams : public CpuHeadroomParamsInternal {};
 struct AGpuHeadroomParams : public GpuHeadroomParamsInternal {};
 
-const int CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN = 50;
-const int CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX = 10000;
-const int GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN = 50;
-const int GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX = 10000;
-const int CPU_HEADROOM_MAX_TID_COUNT = 5;
-
 struct ASystemHealthManager {
 public:
     static ASystemHealthManager* getInstance();
-    ASystemHealthManager(std::shared_ptr<IHintManager>& hintManager);
+
+    ASystemHealthManager(std::shared_ptr<IHintManager>& hintManager,
+                         IHintManager::HintManagerClientData&& clientData);
     ASystemHealthManager() = delete;
     ~ASystemHealthManager();
     int getCpuHeadroom(const ACpuHeadroomParams* params, float* outHeadroom);
     int getGpuHeadroom(const AGpuHeadroomParams* params, float* outHeadroom);
     int getCpuHeadroomMinIntervalMillis(int64_t* outMinIntervalMillis);
     int getGpuHeadroomMinIntervalMillis(int64_t* outMinIntervalMillis);
+    int getMaxCpuHeadroomTidsSize(size_t* outSize);
+    int getCpuHeadroomCalculationWindowRange(int32_t* _Nonnull outMinMillis,
+                                             int32_t* _Nonnull outMaxMillis);
+    int getGpuHeadroomCalculationWindowRange(int32_t* _Nonnull outMinMillis,
+                                             int32_t* _Nonnull outMaxMillis);
 
 private:
     static ASystemHealthManager* create(std::shared_ptr<IHintManager> hintManager);
     std::shared_ptr<IHintManager> mHintManager;
+    IHintManager::HintManagerClientData mClientData;
 };
 
 ASystemHealthManager* ASystemHealthManager::getInstance() {
@@ -60,10 +62,11 @@
     return instance;
 }
 
-ASystemHealthManager::ASystemHealthManager(std::shared_ptr<IHintManager>& hintManager)
-      : mHintManager(std::move(hintManager)) {}
+ASystemHealthManager::ASystemHealthManager(std::shared_ptr<IHintManager>& hintManager,
+                                           IHintManager::HintManagerClientData&& clientData)
+      : mHintManager(std::move(hintManager)), mClientData(clientData) {}
 
-ASystemHealthManager::~ASystemHealthManager() {}
+ASystemHealthManager::~ASystemHealthManager() = default;
 
 ASystemHealthManager* ASystemHealthManager::create(std::shared_ptr<IHintManager> hintManager) {
     if (!hintManager) {
@@ -74,20 +77,37 @@
         ALOGE("%s: PerformanceHint service is not ready ", __FUNCTION__);
         return nullptr;
     }
-    return new ASystemHealthManager(hintManager);
-}
-
-ASystemHealthManager* ASystemHealth_acquireManager() {
-    return ASystemHealthManager::getInstance();
+    IHintManager::HintManagerClientData clientData;
+    ndk::ScopedAStatus ret = hintManager->getClientData(&clientData);
+    if (!ret.isOk()) {
+        ALOGE("%s: PerformanceHint service is not initialized %s", __FUNCTION__, ret.getMessage());
+        return nullptr;
+    }
+    return new ASystemHealthManager(hintManager, std::move(clientData));
 }
 
 int ASystemHealthManager::getCpuHeadroom(const ACpuHeadroomParams* params, float* outHeadroom) {
+    if (!mClientData.supportInfo.headroom.isCpuSupported) return ENOTSUP;
     std::optional<hal::CpuHeadroomResult> res;
     ::ndk::ScopedAStatus ret;
     CpuHeadroomParamsInternal internalParams;
     if (!params) {
         ret = mHintManager->getCpuHeadroom(internalParams, &res);
     } else {
+        LOG_ALWAYS_FATAL_IF((int)params->tids.size() > mClientData.maxCpuHeadroomThreads,
+                            "%s: tids size should not exceed %d", __FUNCTION__,
+                            mClientData.maxCpuHeadroomThreads);
+        LOG_ALWAYS_FATAL_IF(params->calculationWindowMillis <
+                                            mClientData.supportInfo.headroom
+                                                    .cpuMinCalculationWindowMillis ||
+                                    params->calculationWindowMillis >
+                                            mClientData.supportInfo.headroom
+                                                    .cpuMaxCalculationWindowMillis,
+                            "%s: calculationWindowMillis should be in range [%d, %d] but got %d",
+                            __FUNCTION__,
+                            mClientData.supportInfo.headroom.cpuMinCalculationWindowMillis,
+                            mClientData.supportInfo.headroom.cpuMaxCalculationWindowMillis,
+                            params->calculationWindowMillis);
         ret = mHintManager->getCpuHeadroom(*params, &res);
     }
     if (!ret.isOk()) {
@@ -106,12 +126,24 @@
 }
 
 int ASystemHealthManager::getGpuHeadroom(const AGpuHeadroomParams* params, float* outHeadroom) {
+    if (!mClientData.supportInfo.headroom.isGpuSupported) return ENOTSUP;
     std::optional<hal::GpuHeadroomResult> res;
     ::ndk::ScopedAStatus ret;
     GpuHeadroomParamsInternal internalParams;
     if (!params) {
         ret = mHintManager->getGpuHeadroom(internalParams, &res);
     } else {
+        LOG_ALWAYS_FATAL_IF(params->calculationWindowMillis <
+                                            mClientData.supportInfo.headroom
+                                                    .gpuMinCalculationWindowMillis ||
+                                    params->calculationWindowMillis >
+                                            mClientData.supportInfo.headroom
+                                                    .gpuMaxCalculationWindowMillis,
+                            "%s: calculationWindowMillis should be in range [%d, %d] but got %d",
+                            __FUNCTION__,
+                            mClientData.supportInfo.headroom.gpuMinCalculationWindowMillis,
+                            mClientData.supportInfo.headroom.gpuMaxCalculationWindowMillis,
+                            params->calculationWindowMillis);
         ret = mHintManager->getGpuHeadroom(*params, &res);
     }
     if (!ret.isOk()) {
@@ -128,6 +160,7 @@
 }
 
 int ASystemHealthManager::getCpuHeadroomMinIntervalMillis(int64_t* outMinIntervalMillis) {
+    if (!mClientData.supportInfo.headroom.isCpuSupported) return ENOTSUP;
     int64_t minIntervalMillis = 0;
     ::ndk::ScopedAStatus ret = mHintManager->getCpuHeadroomMinIntervalMillis(&minIntervalMillis);
     if (!ret.isOk()) {
@@ -142,6 +175,7 @@
 }
 
 int ASystemHealthManager::getGpuHeadroomMinIntervalMillis(int64_t* outMinIntervalMillis) {
+    if (!mClientData.supportInfo.headroom.isGpuSupported) return ENOTSUP;
     int64_t minIntervalMillis = 0;
     ::ndk::ScopedAStatus ret = mHintManager->getGpuHeadroomMinIntervalMillis(&minIntervalMillis);
     if (!ret.isOk()) {
@@ -155,6 +189,57 @@
     return OK;
 }
 
+int ASystemHealthManager::getMaxCpuHeadroomTidsSize(size_t* outSize) {
+    if (!mClientData.supportInfo.headroom.isGpuSupported) return ENOTSUP;
+    *outSize = mClientData.maxCpuHeadroomThreads;
+    return OK;
+}
+
+int ASystemHealthManager::getCpuHeadroomCalculationWindowRange(int32_t* _Nonnull outMinMillis,
+                                                               int32_t* _Nonnull outMaxMillis) {
+    if (!mClientData.supportInfo.headroom.isCpuSupported) return ENOTSUP;
+    *outMinMillis = mClientData.supportInfo.headroom.cpuMinCalculationWindowMillis;
+    *outMaxMillis = mClientData.supportInfo.headroom.cpuMaxCalculationWindowMillis;
+    return OK;
+}
+
+int ASystemHealthManager::getGpuHeadroomCalculationWindowRange(int32_t* _Nonnull outMinMillis,
+                                                               int32_t* _Nonnull outMaxMillis) {
+    if (!mClientData.supportInfo.headroom.isGpuSupported) return ENOTSUP;
+    *outMinMillis = mClientData.supportInfo.headroom.gpuMinCalculationWindowMillis;
+    *outMaxMillis = mClientData.supportInfo.headroom.gpuMaxCalculationWindowMillis;
+    return OK;
+}
+
+int ASystemHealth_getMaxCpuHeadroomTidsSize(size_t* _Nonnull outSize) {
+    LOG_ALWAYS_FATAL_IF(outSize == nullptr, "%s: outSize should not be null", __FUNCTION__);
+    auto manager = ASystemHealthManager::getInstance();
+    if (manager == nullptr) return ENOTSUP;
+    return manager->getMaxCpuHeadroomTidsSize(outSize);
+}
+
+int ASystemHealth_getCpuHeadroomCalculationWindowRange(int32_t* _Nonnull outMinMillis,
+                                                       int32_t* _Nonnull outMaxMillis) {
+    LOG_ALWAYS_FATAL_IF(outMinMillis == nullptr, "%s: outMinMillis should not be null",
+                        __FUNCTION__);
+    LOG_ALWAYS_FATAL_IF(outMaxMillis == nullptr, "%s: outMaxMillis should not be null",
+                        __FUNCTION__);
+    auto manager = ASystemHealthManager::getInstance();
+    if (manager == nullptr) return ENOTSUP;
+    return manager->getCpuHeadroomCalculationWindowRange(outMinMillis, outMaxMillis);
+}
+
+int ASystemHealth_getGpuHeadroomCalculationWindowRange(int32_t* _Nonnull outMinMillis,
+                                                       int32_t* _Nonnull outMaxMillis) {
+    LOG_ALWAYS_FATAL_IF(outMinMillis == nullptr, "%s: outMinMillis should not be null",
+                        __FUNCTION__);
+    LOG_ALWAYS_FATAL_IF(outMaxMillis == nullptr, "%s: outMaxMillis should not be null",
+                        __FUNCTION__);
+    auto manager = ASystemHealthManager::getInstance();
+    if (manager == nullptr) return ENOTSUP;
+    return manager->getGpuHeadroomCalculationWindowRange(outMinMillis, outMaxMillis);
+}
+
 int ASystemHealth_getCpuHeadroom(const ACpuHeadroomParams* _Nullable params,
                                  float* _Nonnull outHeadroom) {
     LOG_ALWAYS_FATAL_IF(outHeadroom == nullptr, "%s: outHeadroom should not be null", __FUNCTION__);
@@ -189,19 +274,15 @@
 
 void ACpuHeadroomParams_setCalculationWindowMillis(ACpuHeadroomParams* _Nonnull params,
                                                    int windowMillis) {
-    LOG_ALWAYS_FATAL_IF(windowMillis < CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN ||
-                                windowMillis > CPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX,
-                        "%s: windowMillis should be in range [50, 10000] but got %d", __FUNCTION__,
-                        windowMillis);
+    LOG_ALWAYS_FATAL_IF(windowMillis <= 0, "%s: windowMillis should be positive but got %d",
+                        __FUNCTION__, windowMillis);
     params->calculationWindowMillis = windowMillis;
 }
 
 void AGpuHeadroomParams_setCalculationWindowMillis(AGpuHeadroomParams* _Nonnull params,
                                                    int windowMillis) {
-    LOG_ALWAYS_FATAL_IF(windowMillis < GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MIN ||
-                                windowMillis > GPU_HEADROOM_CALCULATION_WINDOW_MILLIS_MAX,
-                        "%s: windowMillis should be in range [50, 10000] but got %d", __FUNCTION__,
-                        windowMillis);
+    LOG_ALWAYS_FATAL_IF(windowMillis <= 0, "%s: windowMillis should be positive but got %d",
+                        __FUNCTION__, windowMillis);
     params->calculationWindowMillis = windowMillis;
 }
 
@@ -214,13 +295,11 @@
 }
 
 void ACpuHeadroomParams_setTids(ACpuHeadroomParams* _Nonnull params, const int* _Nonnull tids,
-                                int tidsSize) {
+                                size_t tidsSize) {
     LOG_ALWAYS_FATAL_IF(tids == nullptr, "%s: tids should not be null", __FUNCTION__);
-    LOG_ALWAYS_FATAL_IF(tidsSize > CPU_HEADROOM_MAX_TID_COUNT, "%s: tids size should not exceed 5",
-                        __FUNCTION__);
     params->tids.resize(tidsSize);
     params->tids.clear();
-    for (int i = 0; i < tidsSize; ++i) {
+    for (int i = 0; i < (int)tidsSize; ++i) {
         LOG_ALWAYS_FATAL_IF(tids[i] <= 0, "ACpuHeadroomParams_setTids: Invalid non-positive tid %d",
                             tids[i]);
         params->tids[i] = tids[i];
@@ -269,10 +348,10 @@
     return new AGpuHeadroomParams();
 }
 
-void ACpuHeadroomParams_destroy(ACpuHeadroomParams* _Nonnull params) {
+void ACpuHeadroomParams_destroy(ACpuHeadroomParams* _Nullable params) {
     delete params;
 }
 
-void AGpuHeadroomParams_destroy(AGpuHeadroomParams* _Nonnull params) {
+void AGpuHeadroomParams_destroy(AGpuHeadroomParams* _Nullable params) {
     delete params;
 }
diff --git a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
index f68fa1a..0fa92ea 100644
--- a/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
+++ b/native/android/tests/performance_hint/PerformanceHintNativeTest.cpp
@@ -161,6 +161,9 @@
                          clientDataIn,
                  ::aidl::android::os::IHintManager::HintManagerClientData* _aidl_return),
                 (override));
+    MOCK_METHOD(ScopedAStatus, getClientData,
+                (::aidl::android::os::IHintManager::HintManagerClientData * _aidl_return),
+                (override));
     MOCK_METHOD(SpAIBinder, asBinder, (), (override));
     MOCK_METHOD(bool, isRemote, (), (override));
 };
@@ -602,6 +605,15 @@
     ASSERT_NE(config, nullptr);
 }
 
+TEST_F(PerformanceHintTest, TestSessionCreationWithNullLayers) {
+    EXPECT_CALL(*mMockIHintManager, createHintSessionWithConfig(_, _, _, _, _)).Times(1);
+    auto&& config = configFromCreator(
+            {.tids = mTids, .nativeWindows = {nullptr}, .surfaceControls = {nullptr}});
+    APerformanceHintManager* manager = createManager();
+    auto&& session = createSessionUsingConfig(manager, config);
+    ASSERT_TRUE(session);
+}
+
 TEST_F(PerformanceHintTest, TestSupportObject) {
     // Disable GPU and Power Efficiency support to test partial enabling
     mClientData.supportInfo.sessionModes &= ~(1 << (int)hal::SessionMode::AUTO_GPU);
diff --git a/packages/CompanionDeviceManager/res/anim/progress_indeterminate_horizontal_rect1.xml b/packages/CompanionDeviceManager/res/anim/progress_indeterminate_horizontal_rect1.xml
new file mode 100644
index 0000000..a9fd55f
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/anim/progress_indeterminate_horizontal_rect1.xml
@@ -0,0 +1,30 @@
+<?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.
+  -->
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="2000"
+        android:propertyXName="translateX"
+        android:pathData="M -522.59998,0 c 48.89972,0 166.02656,0 301.21729,0 c 197.58128,0 420.9827,0 420.9827,0 "
+        android:interpolator="@interpolator/progress_indeterminate_horizontal_rect1_translatex"
+        android:repeatCount="infinite" />
+    <objectAnimator
+        android:duration="2000"
+        android:propertyYName="scaleX"
+        android:pathData="M 0 0.1 L 1 0.826849212646 L 2 0.1"
+        android:interpolator="@interpolator/progress_indeterminate_horizontal_rect1_scalex"
+        android:repeatCount="infinite" />
+</set>
\ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/anim/progress_indeterminate_horizontal_rect2.xml b/packages/CompanionDeviceManager/res/anim/progress_indeterminate_horizontal_rect2.xml
new file mode 100644
index 0000000..7b5b6a9
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/anim/progress_indeterminate_horizontal_rect2.xml
@@ -0,0 +1,30 @@
+<?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.
+  -->
+<set xmlns:android="http://schemas.android.com/apk/res/android" >
+    <objectAnimator
+        android:duration="2000"
+        android:propertyXName="translateX"
+        android:pathData="M -197.60001,0 c 14.28182,0 85.07782,0 135.54689,0 c 54.26191,0 90.42461,0 168.24331,0 c 144.72154,0 316.40982,0 316.40982,0 "
+        android:interpolator="@interpolator/progress_indeterminate_horizontal_rect2_translatex"
+        android:repeatCount="infinite" />
+    <objectAnimator
+        android:duration="2000"
+        android:propertyYName="scaleX"
+        android:pathData="M 0.0,0.1 L 1.0,0.571379510698 L 2.0,0.909950256348 L 3.0,0.1"
+        android:interpolator="@interpolator/progress_indeterminate_horizontal_rect2_scalex"
+        android:repeatCount="infinite" />
+</set>
\ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/drawable/indeterminate_progress_drawable.xml b/packages/CompanionDeviceManager/res/drawable/indeterminate_progress_drawable.xml
new file mode 100644
index 0000000..1029590
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/drawable/indeterminate_progress_drawable.xml
@@ -0,0 +1,25 @@
+<?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.
+  -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:drawable="@drawable/indeterminate_progress_vector" >
+    <target
+        android:name="rect2_grp"
+        android:animation="@anim/progress_indeterminate_horizontal_rect2" />
+    <target
+        android:name="rect1_grp"
+        android:animation="@anim/progress_indeterminate_horizontal_rect1" />
+</animated-vector>
\ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/drawable/indeterminate_progress_vector.xml b/packages/CompanionDeviceManager/res/drawable/indeterminate_progress_vector.xml
new file mode 100644
index 0000000..4db3356
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/drawable/indeterminate_progress_vector.xml
@@ -0,0 +1,49 @@
+<?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:height="10dp"
+    android:width="360dp"
+    android:viewportHeight="10"
+    android:viewportWidth="360" >
+    <group
+        android:name="progress_group"
+        android:translateX="180"
+        android:translateY="5" >
+        <path
+            android:name="background_track"
+            android:pathData="M -180.0,-5.0 l 360.0,0 l 0,10.0 l -360.0,0 Z"
+            android:fillColor="@color/progress_bg" />
+        <group
+            android:name="rect2_grp"
+            android:translateX="-197.60001"
+            android:scaleX="0.1" >
+            <path
+                android:name="rect2"
+                android:pathData="M -144.0,-5.0 l 288.0,0 l 0,10.0 l -288.0,0 Z"
+                android:fillColor="@color/progress_fg" />
+        </group>
+        <group
+            android:name="rect1_grp"
+            android:translateX="-522.59998"
+            android:scaleX="0.1" >
+            <path
+                android:name="rect1"
+                android:pathData="M -144.0,-5.0 l 288.0,0 l 0,10.0 l -288.0,0 Z"
+                android:fillColor="@color/progress_fg" />
+        </group>
+    </group>
+</vector>
\ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/interpolator/progress_indeterminate_horizontal_rect1_scalex.xml b/packages/CompanionDeviceManager/res/interpolator/progress_indeterminate_horizontal_rect1_scalex.xml
new file mode 100644
index 0000000..453e092
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/interpolator/progress_indeterminate_horizontal_rect1_scalex.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.
+  -->
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:pathData="M 0 0 L 0.3665 0 C 0.47252618112021,0.062409910275 0.61541608570164,0.5 0.68325,0.5 C 0.75475061236836,0.5 0.75725829093844,0.814510098964 1.0,1.0" />
diff --git a/packages/CompanionDeviceManager/res/interpolator/progress_indeterminate_horizontal_rect1_translatex.xml b/packages/CompanionDeviceManager/res/interpolator/progress_indeterminate_horizontal_rect1_translatex.xml
new file mode 100644
index 0000000..a6da0eb
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/interpolator/progress_indeterminate_horizontal_rect1_translatex.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.
+  -->
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:pathData="M 0.0,0.0 L 0.2 0 C 0.3958333333336,0.0 0.474845090492,0.206797621729 0.5916666666664,0.417082932942 C 0.7151610251224,0.639379624869 0.81625,0.974556908664 1.0,1.0 " />
diff --git a/packages/CompanionDeviceManager/res/interpolator/progress_indeterminate_horizontal_rect2_scalex.xml b/packages/CompanionDeviceManager/res/interpolator/progress_indeterminate_horizontal_rect2_scalex.xml
new file mode 100644
index 0000000..785d7ab
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/interpolator/progress_indeterminate_horizontal_rect2_scalex.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.
+  -->
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:pathData="M 0,0 C 0.06834272400867,0.01992566661414 0.19220331656133,0.15855429260523 0.33333333333333,0.34926160892842 C 0.38410433133433,0.41477913453861 0.54945792615267,0.68136029463551 0.66666666666667,0.68279962777002 C 0.752586273196,0.68179620963216 0.737253971954,0.878896194318 1,1" />
diff --git a/packages/CompanionDeviceManager/res/interpolator/progress_indeterminate_horizontal_rect2_translatex.xml b/packages/CompanionDeviceManager/res/interpolator/progress_indeterminate_horizontal_rect2_translatex.xml
new file mode 100644
index 0000000..931dff1
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/interpolator/progress_indeterminate_horizontal_rect2_translatex.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.
+  -->
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:pathData="M 0.0,0.0 C 0.0375,0.0 0.128764607715,0.0895380946618 0.25,0.218553507947 C 0.322410320025,0.295610602487 0.436666666667,0.417591408114 0.483333333333,0.489826169306 C 0.69,0.80972296795 0.793333333333,0.950016125212 1.0,1.0 " />
diff --git a/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml b/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml
index 5805332..40a786e 100644
--- a/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml
+++ b/packages/CompanionDeviceManager/res/layout/activity_confirmation.xml
@@ -71,10 +71,19 @@
                     android:layout_height="wrap_content"
                     android:visibility="gone">
 
+                    <TextView
+                        android:id="@+id/timeout_message"
+                        android:layout_width="match_parent"
+                        android:layout_height="100dp"
+                        android:visibility="gone"
+                        style="@style/TimeoutMessage" />
+
                     <androidx.recyclerview.widget.RecyclerView
                         android:id="@+id/device_list"
                         android:layout_width="match_parent"
-                        android:layout_height="200dp"
+                        android:layout_height="wrap_content"
+                        app:layout_constraintHeight_max="220dp"
+                        app:layout_constraintHeight_min="200dp"
                         android:scrollbars="vertical"
                         android:visibility="gone" />
 
@@ -87,9 +96,9 @@
                         app:layout_constraintHeight_max="220dp"
                         android:visibility="gone" />
 
-                    <View
-                        android:id="@+id/border_top"
-                        style="@style/DeviceListBorder" />
+                    <ProgressBar
+                        android:id="@+id/progress_bar"
+                        style="@style/HorizontalProgressBar" />
 
                     <View
                         android:id="@+id/border_bottom"
@@ -98,10 +107,6 @@
 
                 </androidx.constraintlayout.widget.ConstraintLayout>
 
-                <ProgressBar
-                    android:id="@+id/spinner_multiple_device"
-                    android:visibility="gone"
-                    style="@style/Spinner"  />
 
             </RelativeLayout>
 
@@ -135,7 +140,8 @@
                 android:layout_marginEnd="16dp"
                 android:layout_marginBottom="16dp">
 
-                <!-- Do NOT change the IDs of the buttons: they are referenced in CTS tests. -->
+                <!-- Do NOT change the IDs of the buttons: they are referenced in CTS tests.
+                     Legacy name before the change that added single-device dialog.-->
                 <LinearLayout
                     android:id="@+id/negative_multiple_devices_layout"
                     android:layout_width="wrap_content"
@@ -159,18 +165,6 @@
 
         </LinearLayout>
 
-        <RelativeLayout
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:layout_weight="1"
-            android:importantForAccessibility="noHideDescendants">
-
-            <ProgressBar
-                android:id="@+id/spinner_single_device"
-                android:visibility="gone"
-                style="@style/Spinner" />
-        </RelativeLayout>>
-
     </LinearLayout>
 
 </ScrollView>
\ No newline at end of file
diff --git a/packages/CompanionDeviceManager/res/values/colors.xml b/packages/CompanionDeviceManager/res/values/colors.xml
new file mode 100644
index 0000000..8782250
--- /dev/null
+++ b/packages/CompanionDeviceManager/res/values/colors.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.
+  -->
+<resources>
+  <color name="border">@android:color/system_neutral1_200</color>
+  <color name="progress_bg">@android:color/system_neutral1_600</color>
+  <color name="progress_fg">@android:color/system_neutral1_0</color>
+</resources>
diff --git a/packages/CompanionDeviceManager/res/values/strings.xml b/packages/CompanionDeviceManager/res/values/strings.xml
index 2a6d68d..20ede5f 100644
--- a/packages/CompanionDeviceManager/res/values/strings.xml
+++ b/packages/CompanionDeviceManager/res/values/strings.xml
@@ -22,6 +22,21 @@
     <!-- Title of the device association confirmation dialog. -->
     <string name="confirmation_title">Allow the app &lt;strong&gt;<xliff:g id="app_name" example="Android Wear">%1$s</xliff:g>&lt;/strong&gt; to access &lt;strong&gt;<xliff:g id="device_name" example="ASUS ZenWatch 2">%2$s</xliff:g>&lt;/strong&gt;?</string>
 
+    <!-- Description of soft discovery timeout. [CHAR LIMIT= NONE] -->
+    <string name="message_discovery_soft_timeout">Make sure this <xliff:g id="device_type" example="phone">%1$s</xliff:g> has <xliff:g id="discovery_method" example="Bluetooth">%2$s</xliff:g> turned on, and keep your <xliff:g id="profile_name" example="watch">%3$s</xliff:g> nearby.</string>
+
+    <!-- Description of hard discovery timeout. [CHAR LIMIT= NONE] -->
+    <string name="message_discovery_hard_timeout">No devices found. Please try again later.</string>
+
+    <!-- The discovery method name for Bluetooth [CHAR LIMIT=30] -->
+    <string name="discovery_bluetooth">Bluetooth</string>
+
+    <!-- The discovery method name for Wi-Fi [CHAR LIMIT=30] -->
+    <string name="discovery_wifi">Wi-Fi</string>
+
+    <!-- The discovery method name for both Bluetooth and Wi-Fi [CHAR LIMIT=50] -->
+    <string name="discovery_mixed">Bluetooth and Wi-Fi</string>
+
     <!-- ================= DEVICE_PROFILE_WATCH and null profile ================= -->
 
     <!-- The name of the "watch" device type [CHAR LIMIT=30] -->
@@ -30,9 +45,12 @@
     <!-- Title of the device selection dialog. -->
     <string name="chooser_title_non_profile">Choose a device to be managed by &lt;strong&gt;<xliff:g id="app_name" example="Android Wear">%1$s</xliff:g>&lt;/strong&gt;</string>
 
-    <!-- Tile of the multiple devices' dialog. -->
+    <!-- Title of the multiple devices' dialog. -->
     <string name="chooser_title">Choose a <xliff:g id="profile_name" example="watch">%1$s</xliff:g> to set up</string>
 
+    <!-- Title of the single device scan dialog. -->
+    <string name="single_device_title">Looking for a <xliff:g id="profile_name" example="watch">%1$s</xliff:g></string>
+
     <!-- Description of the privileges the application will get if associated with the companion device of WATCH profile [CHAR LIMIT=NONE] -->
     <string name="summary_watch">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" example="phone">%1$s</xliff:g></string>
 
diff --git a/packages/CompanionDeviceManager/res/values/styles.xml b/packages/CompanionDeviceManager/res/values/styles.xml
index a161a50..30813ba 100644
--- a/packages/CompanionDeviceManager/res/values/styles.xml
+++ b/packages/CompanionDeviceManager/res/values/styles.xml
@@ -59,6 +59,43 @@
         <item name="android:textColor">?android:attr/textColorSecondary</item>
     </style>
 
+    <style name="HorizontalProgressBar"
+        parent="@android:style/Widget.Material.ProgressBar.Horizontal">
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">1dp</item>
+        <item name="android:layout_marginStart">32dp</item>
+        <item name="android:layout_marginEnd">32dp</item>
+        <item name="android:progress">100</item>
+        <item name="android:indeterminate">true</item>
+        <item name="android:indeterminateOnly">false</item>
+        <item name="android:progressTint">@color/border</item>
+        <item name="android:indeterminateDrawable">@drawable/indeterminate_progress_drawable</item>
+    </style>
+
+    <style name="DeviceListBorder">
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">1dp</item>
+        <item name="android:layout_marginStart">32dp</item>
+        <item name="android:layout_marginEnd">32dp</item>
+        <item name="android:background">@color/border</item>
+    </style>
+
+    <style name="TimeoutMessage"
+           parent="@android:style/TextAppearance.DeviceDefault.Medium">
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">wrap_content</item>
+        <item name="android:layout_marginStart">32dp</item>
+        <item name="android:layout_marginEnd">32dp</item>
+        <item name="android:paddingEnd">8dp</item>
+        <item name="android:paddingStart">8dp</item>
+        <item name="android:paddingTop">18dp</item>
+        <item name="android:paddingBottom">18dp</item>
+        <item name="android:textDirection">locale</item>
+        <item name="android:textSize">14sp</item>
+        <item name="android:lineSpacingExtra">2dp</item>
+        <item name="android:textColor">?android:attr/textColorSecondary</item>
+    </style>
+
     <style name="VendorHelperBackButton"
            parent="@android:style/Widget.Material.Button.Borderless.Colored">
         <item name="android:layout_width">wrap_content</item>
@@ -111,22 +148,6 @@
         <item name="android:textAppearance">@android:style/TextAppearance.DeviceDefault.Medium</item>
     </style>
 
-    <style name="DeviceListBorder">
-        <item name="android:layout_width">match_parent</item>
-        <item name="android:layout_height">1dp</item>
-        <item name="android:layout_marginStart">32dp</item>
-        <item name="android:layout_marginEnd">32dp</item>
-        <item name="android:background">@android:color/system_neutral1_200</item>
-    </style>
-
-    <style name="Spinner"
-           parent="@android:style/Widget.Material.Light.ProgressBar.Large">
-        <item name="android:layout_width">56dp</item>
-        <item name="android:layout_height">56dp</item>
-        <item name="android:indeterminate">true</item>
-        <item name="android:layout_centerInParent">true</item>
-    </style>
-
     <style name="ScrollViewStyle">
         <item name="android:scrollbars">none</item>
         <item name="android:fillViewport">true</item>
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionAssociationActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionAssociationActivity.java
index 50419f7..ea40e13 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionAssociationActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionAssociationActivity.java
@@ -17,14 +17,12 @@
 package com.android.companiondevicemanager;
 
 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;
 
 import static com.android.companiondevicemanager.CompanionDeviceDiscoveryService.DiscoveryState;
-import static com.android.companiondevicemanager.CompanionDeviceDiscoveryService.DiscoveryState.FINISHED_TIMEOUT;
 import static com.android.companiondevicemanager.CompanionDeviceDiscoveryService.LOCK;
 import static com.android.companiondevicemanager.CompanionDeviceDiscoveryService.sDiscoveryStarted;
 import static com.android.companiondevicemanager.CompanionDeviceResources.PROFILE_ICONS;
@@ -48,11 +46,13 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.StringRes;
 import android.annotation.SuppressLint;
 import android.companion.AssociatedDevice;
 import android.companion.AssociationInfo;
 import android.companion.AssociationRequest;
 import android.companion.CompanionDeviceManager;
+import android.companion.DeviceFilter;
 import android.companion.Flags;
 import android.companion.IAssociationRequestCallback;
 import android.content.Intent;
@@ -139,22 +139,19 @@
     private TextView mVendorHeaderName;
     private ImageButton mVendorHeaderButton;
 
-    // Progress indicator is only shown while we are looking for the first suitable device for a
-    // multiple device association.
-    private ProgressBar mMultipleDeviceSpinner;
-    // Progress indicator is only shown while we are looking for the first suitable device for a
-    // single device association.
-    private ProgressBar mSingleDeviceSpinner;
+    // Message to be displayed when device hasn't been discovered for a certain duration
+    private TextView mTimeoutMessage;
+
+    // Horizontal progress indicator is always shown as long as the scanner is searching for devices
+    private ProgressBar mProgressBar;
 
     // Present for self-managed association requests and "single-device" regular association
     // regular.
     private Button mButtonAllow;
     private Button mButtonNotAllow;
-    // Present for multiple devices' association requests only.
-    private Button mButtonNotAllowMultipleDevices;
+    private Button mButtonCancelScan;
 
-    // Present for top and bottom borders for permissions list and device list.
-    private View mBorderTop;
+    // Bottom border for permissions list and device list. The progress bar acts as the top border.
     private View mBorderBottom;
 
     private LinearLayout mAssociationConfirmationDialog;
@@ -162,9 +159,9 @@
     private ConstraintLayout mConstraintList;
     // Only present for self-managed association requests.
     private RelativeLayout mVendorHeader;
-    // A linearLayout for mButtonNotAllowMultipleDevices, user will press this layout instead
+    // A linearLayout for mButtonCancelScan, user will press this layout instead
     // of the button for accessibility.
-    private LinearLayout mNotAllowMultipleDevicesLayout;
+    private LinearLayout mCancelScanLayout;
 
     // The recycler view is only shown for multiple-device regular association request, after
     // at least one matching device is found.
@@ -297,7 +294,6 @@
         mAssociationConfirmationDialog = findViewById(R.id.association_confirmation);
         mVendorHeader = findViewById(R.id.vendor_header);
 
-        mBorderTop = findViewById(R.id.border_top);
         mBorderBottom = findViewById(R.id.border_bottom);
 
         mTitle = findViewById(R.id.title);
@@ -311,42 +307,89 @@
 
         mDeviceIcon = findViewById(R.id.device_icon);
 
+        mTimeoutMessage = findViewById(R.id.timeout_message);
         mDeviceListRecyclerView = findViewById(R.id.device_list);
 
-        mMultipleDeviceSpinner = findViewById(R.id.spinner_multiple_device);
-        mSingleDeviceSpinner = findViewById(R.id.spinner_single_device);
+        mProgressBar = findViewById(R.id.progress_bar);
+        mProgressBar.getIndeterminateDrawable().clearColorFilter();
 
         mPermissionListRecyclerView = findViewById(R.id.permission_list);
         mPermissionListAdapter = new PermissionListAdapter(this);
 
         mButtonAllow = findViewById(R.id.btn_positive);
         mButtonNotAllow = findViewById(R.id.btn_negative);
-        mButtonNotAllowMultipleDevices = findViewById(R.id.btn_negative_multiple_devices);
-        mNotAllowMultipleDevicesLayout = findViewById(R.id.negative_multiple_devices_layout);
+        mButtonCancelScan = findViewById(R.id.btn_negative_multiple_devices);
+        mCancelScanLayout = findViewById(R.id.negative_multiple_devices_layout);
 
         mButtonAllow.setOnClickListener(this::onPositiveButtonClick);
         mButtonNotAllow.setOnClickListener(this::onNegativeButtonClick);
-        mNotAllowMultipleDevicesLayout.setOnClickListener(this::onNegativeButtonClick);
+        mCancelScanLayout.setOnClickListener(this::onNegativeButtonClick);
 
         mVendorHeaderButton.setOnClickListener(this::onShowHelperDialog);
 
         if (mRequest.isSelfManaged()) {
             initUiForSelfManagedAssociation();
-        } else if (mRequest.isSingleDevice()) {
-            initUiForSingleDevice();
         } else {
-            initUiForMultipleDevices();
+            initUiForDeviceDiscovery();
         }
     }
 
     private void onDiscoveryStateChanged(DiscoveryState newState) {
-        if (newState == FINISHED_TIMEOUT
-                && CompanionDeviceDiscoveryService.getScanResult().getValue().isEmpty()) {
-            synchronized (LOCK) {
-                if (sDiscoveryStarted) {
-                    cancel(RESULT_DISCOVERY_TIMEOUT, null);
-                }
+        switch (newState) {
+            case IN_PROGRESS: {
+                mTimeoutMessage.setText(null);
+                mProgressBar.setIndeterminate(true);
+                break;
             }
+            case IN_PROGRESS_EXTENDED: {
+                final String deviceType = getString(R.string.device_type);
+                final String discoveryType = getString(getDiscoveryMethod());
+                final String profile = getString(PROFILE_NAMES.get(mRequest.getDeviceProfile()));
+                final Spanned message = getHtmlFromResources(this,
+                        R.string.message_discovery_soft_timeout,
+                        deviceType, discoveryType, profile);
+                mTimeoutMessage.setText(message);
+                break;
+            }
+            case FINISHED_STOPPED: {
+                if (CompanionDeviceDiscoveryService.getScanResult().getValue().isEmpty()) {
+                    // If the scan times out, do NOT close the activity automatically and let the
+                    // user manually cancel the flow.
+                    synchronized (LOCK) {
+                        if (sDiscoveryStarted) {
+                            stopDiscovery();
+                        }
+                    }
+                    mTimeoutMessage.setText(getString(R.string.message_discovery_hard_timeout));
+                }
+                mProgressBar.setIndeterminate(false);
+                break;
+            }
+        }
+    }
+
+    @StringRes
+    private int getDiscoveryMethod() {
+        // If no filter was given or at least one bluetooth filter was provided, then
+        // display message for Bluetooth.
+        // If filter is _only_ for Wi-Fi devices, then display message for Wi-Fi.
+        // e.g. "Make sure Bluetooth is on" vs "Make sure Wi-Fi is on"
+        boolean hasBluetooth = false;
+        boolean hasWifi = false;
+        for (DeviceFilter<?> filter : mRequest.getDeviceFilters()) {
+            if (filter.getMediumType() == DeviceFilter.MEDIUM_TYPE_BLUETOOTH
+                    || filter.getMediumType() == DeviceFilter.MEDIUM_TYPE_BLUETOOTH_LE) {
+                hasBluetooth = true;
+            } else if (filter.getMediumType() == DeviceFilter.MEDIUM_TYPE_WIFI) {
+                hasWifi = true;
+            }
+        }
+        if (hasBluetooth == hasWifi) {
+            return R.string.discovery_mixed;
+        } else if (hasBluetooth) {
+            return R.string.discovery_bluetooth;
+        } else {
+            return R.string.discovery_wifi;
         }
     }
 
@@ -392,9 +435,7 @@
         mCancelled = true;
 
         // Stop discovery service if it was used.
-        if (!mRequest.isSelfManaged()) {
-            CompanionDeviceDiscoveryService.stop(this);
-        }
+        stopDiscovery();
 
         // First send callback to the app directly...
         try {
@@ -408,6 +449,12 @@
         setResultAndFinish(null, errorCode);
     }
 
+    private void stopDiscovery() {
+        if (!mRequest.isSelfManaged()) {
+            CompanionDeviceDiscoveryService.stop(this);
+        }
+    }
+
     private void setResultAndFinish(@Nullable AssociationInfo association, int resultCode) {
         Slog.i(TAG, "setResultAndFinish(), association="
                 + (association == null ? "null" : association)
@@ -479,41 +526,14 @@
         mVendorHeader.setVisibility(View.VISIBLE);
         mProfileIcon.setVisibility(View.GONE);
         mDeviceListRecyclerView.setVisibility(View.GONE);
-        // Top and bottom borders should be gone for selfManaged dialog.
-        mBorderTop.setVisibility(View.GONE);
+        mProgressBar.setVisibility(View.GONE);
         mBorderBottom.setVisibility(View.GONE);
     }
 
-    private void initUiForSingleDevice() {
-        Slog.d(TAG, "initUiForSingleDevice()");
-
-        final String deviceProfile = mRequest.getDeviceProfile();
-
-        if (!SUPPORTED_PROFILES.contains(deviceProfile)) {
-            throw new RuntimeException("Unsupported profile " + deviceProfile);
-        }
-
-        final Drawable profileIcon = getIcon(this, PROFILE_ICONS.get(deviceProfile));
-        mProfileIcon.setImageDrawable(profileIcon);
-
-        CompanionDeviceDiscoveryService.getScanResult().observe(this, deviceFilterPairs -> {
-            if (deviceFilterPairs.isEmpty()) {
-                return;
-            }
-            mSelectedDevice = requireNonNull(deviceFilterPairs.get(0));
-            updateSingleDeviceUi();
-        });
-
-        mSingleDeviceSpinner.setVisibility(View.VISIBLE);
-        // Hide permission list and confirmation dialog first before the
-        // first matched device is found.
-        mPermissionListRecyclerView.setVisibility(View.GONE);
-        mDeviceListRecyclerView.setVisibility(View.GONE);
-        mAssociationConfirmationDialog.setVisibility(View.GONE);
-    }
-
-    private void initUiForMultipleDevices() {
-        Slog.d(TAG, "initUiForMultipleDevices()");
+    private void initUiForDeviceDiscovery() {
+        Slog.d(TAG, "initUiForDeviceDiscovery() "
+                + "single-device=" + mRequest.isSingleDevice()
+                + ", profile=" + mRequest.getDeviceProfile());
 
         final Drawable profileIcon;
         final Spanned title;
@@ -525,41 +545,59 @@
 
         profileIcon = getIcon(this, PROFILE_ICONS.get(deviceProfile));
 
-        if (deviceProfile == null) {
+        if (mRequest.isSingleDevice()) {
+            title = getHtmlFromResources(this,
+                    R.string.single_device_title, getString(PROFILE_NAMES.get(deviceProfile)));
+        } else if (deviceProfile == null) {
             title = getHtmlFromResources(this, R.string.chooser_title_non_profile, mAppLabel);
-            mButtonNotAllowMultipleDevices.setText(R.string.consent_no);
         } else {
             title = getHtmlFromResources(this,
                     R.string.chooser_title, getString(PROFILE_NAMES.get(deviceProfile)));
         }
 
-        mDeviceAdapter = new DeviceListAdapter(this, this::onDeviceClicked);
-
         mTitle.setText(title);
         mProfileIcon.setImageDrawable(profileIcon);
 
-        mDeviceListRecyclerView.setAdapter(mDeviceAdapter);
-        mDeviceListRecyclerView.setLayoutManager(new LinearLayoutManager(this));
+        if (mRequest.isSingleDevice()) {
+            mBorderBottom.setVisibility(View.GONE);
+            CompanionDeviceDiscoveryService.getScanResult().observe(this, deviceFilterPairs -> {
+                if (deviceFilterPairs.isEmpty()) {
+                    return;
+                }
+                mSelectedDevice = requireNonNull(deviceFilterPairs.get(0));
+                updateUiForAssociationConsent();
+            });
+        } else {
+            mDeviceAdapter = new DeviceListAdapter(this, this::onDeviceClicked);
+            mDeviceListRecyclerView.setAdapter(mDeviceAdapter);
+            mDeviceListRecyclerView.setLayoutManager(new LinearLayoutManager(this));
 
-        CompanionDeviceDiscoveryService.getScanResult().observe(this,
-                deviceFilterPairs -> {
-                    // Dismiss the progress bar once there's one device found for multiple devices.
-                    if (deviceFilterPairs.size() >= 1) {
-                        mMultipleDeviceSpinner.setVisibility(View.GONE);
+            CompanionDeviceDiscoveryService.getScanResult().observe(this, deviceFilterPairs -> {
+                if (deviceFilterPairs.size() >= 1) {
+                    // Dismiss the timeout message once there's at least one device found.
+                    mTimeoutMessage.setText(null);
+
+                    // Update profile-less cancel scan button to read "Don't allow" to indicate
+                    // that selecting a device implies user consent.
+                    if (deviceProfile == null) {
+                        mButtonCancelScan.setText(R.string.consent_no);
                     }
+                }
 
-                    mDeviceAdapter.setDevices(deviceFilterPairs);
-                });
+                mDeviceAdapter.setDevices(deviceFilterPairs);
+            });
+
+            mDeviceListRecyclerView.setVisibility(View.VISIBLE);
+        }
 
         mSummary.setVisibility(View.GONE);
-        // "Remove" consent button: users would need to click on the list item.
         mButtonAllow.setVisibility(View.GONE);
         mButtonNotAllow.setVisibility(View.GONE);
-        mDeviceListRecyclerView.setVisibility(View.VISIBLE);
-        mButtonNotAllowMultipleDevices.setVisibility(View.VISIBLE);
-        mNotAllowMultipleDevicesLayout.setVisibility(View.VISIBLE);
+
+        mTimeoutMessage.setVisibility(View.VISIBLE);
+        mButtonCancelScan.setVisibility(View.VISIBLE);
+        mCancelScanLayout.setVisibility(View.VISIBLE);
         mConstraintList.setVisibility(View.VISIBLE);
-        mMultipleDeviceSpinner.setVisibility(View.VISIBLE);
     }
 
     private void onDeviceClicked(int position) {
@@ -586,15 +624,10 @@
         }
         // The permission consent dialog should be displayed for the multiple device
         // dialog if a device profile exists.
-        updateSingleDeviceUi();
-        mSummary.setVisibility(View.VISIBLE);
-        mButtonAllow.setVisibility(View.VISIBLE);
-        mButtonNotAllow.setVisibility(View.VISIBLE);
-        mDeviceListRecyclerView.setVisibility(View.GONE);
-        mNotAllowMultipleDevicesLayout.setVisibility(View.GONE);
+        updateUiForAssociationConsent();
     }
 
-    private void updateSingleDeviceUi() {
+    private void updateUiForAssociationConsent() {
         // No need to show permission consent dialog if it is a isSkipPrompt(true)
         // AssociationRequest. See AssociationRequestsProcessor#mayAssociateWithoutPrompt.
         if (mRequest.isSkipPrompt()) {
@@ -603,9 +636,14 @@
             return;
         }
 
-        mSingleDeviceSpinner.setVisibility(View.GONE);
         mAssociationConfirmationDialog.setVisibility(View.VISIBLE);
 
+        mProgressBar.setIndeterminate(false); // Keep as border but remove animation
+        mBorderBottom.setVisibility(View.VISIBLE);
+        mTimeoutMessage.setVisibility(View.GONE);
+        mDeviceListRecyclerView.setVisibility(View.GONE);
+        mCancelScanLayout.setVisibility(View.GONE);
+
         final String deviceProfile = mRequest.getDeviceProfile();
         final int summaryResourceId = PROFILE_SUMMARIES.get(deviceProfile);
         final String remoteDeviceName = mSelectedDevice.getDisplayName();
@@ -624,6 +662,10 @@
 
         mTitle.setText(title);
         mSummary.setText(summary);
+
+        mSummary.setVisibility(View.VISIBLE);
+        mButtonAllow.setVisibility(View.VISIBLE);
+        mButtonNotAllow.setVisibility(View.VISIBLE);
     }
 
     private void onPositiveButtonClick(View v) {
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java
index f586e3d..50a01b3 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceDiscoveryService.java
@@ -76,7 +76,8 @@
     private static final String TAG = "CDM_CompanionDeviceDiscoveryService";
 
     private static final String SYS_PROP_DEBUG_TIMEOUT = "debug.cdm.discovery_timeout";
-    private static final long TIMEOUT_DEFAULT = 20_000L; // 20 seconds
+    private static final long TIMEOUT_SOFT = 20_000L; // 20 seconds
+    private static final long TIMEOUT_HARD = 180_000L; // 3 minutes
     private static final long TIMEOUT_MIN = 1_000L; // 1 sec
     private static final long TIMEOUT_MAX = 60_000L; // 1 min
 
@@ -102,7 +103,8 @@
 
     private final List<DeviceFilterPair<?>> mDevicesFound = new ArrayList<>();
 
-    private final Runnable mTimeoutRunnable = this::timeout;
+    private final Runnable mSoftTimeoutRunnable = this::softTimeout;
+    private final Runnable mHardTimeoutRunnable = this::stopDiscoveryAndFinish;
 
     private boolean mStopAfterFirstMatch;
 
@@ -116,8 +118,8 @@
     enum DiscoveryState {
         NOT_STARTED,
         IN_PROGRESS,
+        IN_PROGRESS_EXTENDED,
         FINISHED_STOPPED,
-        FINISHED_TIMEOUT
     }
 
     static boolean startForRequest(
@@ -175,7 +177,7 @@
                 break;
 
             case ACTION_STOP_DISCOVERY:
-                stopDiscoveryAndFinish(/* timeout */ false);
+                stopDiscoveryAndFinish();
                 break;
         }
         return START_NOT_STICKY;
@@ -223,9 +225,17 @@
     }
 
     @MainThread
-    private void stopDiscoveryAndFinish(boolean timeout) {
-        Slog.d(TAG, "stopDiscoveryAndFinish(" + timeout + ")");
+    private void softTimeout() {
+        // If no device is found at this point, display a message and continue discovery
+        if (mDevicesFound.isEmpty()) {
+            sStateLiveData.setValue(DiscoveryState.IN_PROGRESS_EXTENDED);
+        } else {
+            stopDiscoveryAndFinish();
+        }
+    }
 
+    @MainThread
+    private void stopDiscoveryAndFinish() {
         synchronized (LOCK) {
             if (!sDiscoveryStarted) {
                 stopSelf();
@@ -260,13 +270,10 @@
             mBleScanner.stopScan(mBleScanCallback);
         }
 
-        Handler.getMain().removeCallbacks(mTimeoutRunnable);
+        Handler.getMain().removeCallbacks(mSoftTimeoutRunnable);
+        Handler.getMain().removeCallbacks(mHardTimeoutRunnable);
 
-        if (timeout) {
-            sStateLiveData.setValue(DiscoveryState.FINISHED_TIMEOUT);
-        } else {
-            sStateLiveData.setValue(DiscoveryState.FINISHED_STOPPED);
-        }
+        sStateLiveData.setValue(DiscoveryState.FINISHED_STOPPED);
 
         synchronized (LOCK) {
             sDiscoveryStarted = false;
@@ -379,7 +386,7 @@
             sScanResultsLiveData.setValue(mDevicesFound);
             // Stop discovery when there's one device found for singleDevice.
             if (mStopAfterFirstMatch) {
-                stopDiscoveryAndFinish(/* timeout */ false);
+                stopDiscoveryAndFinish();
             }
         });
     }
@@ -396,20 +403,17 @@
     }
 
     private void scheduleTimeout() {
-        long timeout = SystemProperties.getLong(SYS_PROP_DEBUG_TIMEOUT, -1);
-        if (timeout <= 0) {
+        long softTimeout = SystemProperties.getLong(SYS_PROP_DEBUG_TIMEOUT, -1);
+        if (softTimeout <= 0) {
             // 0 or negative values indicate that the sysprop was never set or should be ignored.
-            timeout = TIMEOUT_DEFAULT;
+            softTimeout = TIMEOUT_SOFT;
         } else {
-            timeout = min(timeout, TIMEOUT_MAX); // should be <= 1 min (TIMEOUT_MAX)
-            timeout = max(timeout, TIMEOUT_MIN); // should be >= 1 sec (TIMEOUT_MIN)
+            softTimeout = min(softTimeout, TIMEOUT_MAX); // should be <= 1 min (TIMEOUT_MAX)
+            softTimeout = max(softTimeout, TIMEOUT_MIN); // should be >= 1 sec (TIMEOUT_MIN)
         }
 
-        Handler.getMain().postDelayed(mTimeoutRunnable, timeout);
-    }
-
-    private void timeout() {
-        stopDiscoveryAndFinish(/* timeout */ true);
+        Handler.getMain().postDelayed(mSoftTimeoutRunnable, softTimeout);
+        Handler.getMain().postDelayed(mHardTimeoutRunnable, TIMEOUT_HARD);
     }
 
     @Override
diff --git a/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java b/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java
index ef46906..e4f07f9 100644
--- a/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java
+++ b/packages/CrashRecovery/services/module/java/com/android/server/PackageWatchdog.java
@@ -1364,8 +1364,6 @@
                 Slog.w(TAG, "Failed to save monitored packages, restoring backup", e);
                 mPolicyFile.failWrite(stream);
                 return false;
-            } finally {
-                IoUtils.closeQuietly(stream);
             }
         }
     }
diff --git a/packages/CrashRecovery/services/platform/java/com/android/server/PackageWatchdog.java b/packages/CrashRecovery/services/platform/java/com/android/server/PackageWatchdog.java
index 4a00ed3..5297c8b 100644
--- a/packages/CrashRecovery/services/platform/java/com/android/server/PackageWatchdog.java
+++ b/packages/CrashRecovery/services/platform/java/com/android/server/PackageWatchdog.java
@@ -1372,8 +1372,6 @@
                 Slog.w(TAG, "Failed to save monitored packages, restoring backup", e);
                 mPolicyFile.failWrite(stream);
                 return false;
-            } finally {
-                IoUtils.closeQuietly(stream);
             }
         }
     }
diff --git a/packages/CredentialManager/tests/robotests/Android.bp b/packages/CredentialManager/tests/robotests/Android.bp
index 27afaaa..01f403d 100644
--- a/packages/CredentialManager/tests/robotests/Android.bp
+++ b/packages/CredentialManager/tests/robotests/Android.bp
@@ -53,7 +53,6 @@
         "android.test.mock.stubs.system",
         "truth",
     ],
-    upstream: true,
     java_resource_dirs: ["config"],
     instrumentation_for: "CredentialManagerRobo",
 }
diff --git a/packages/CredentialManager/wear/robotests/Android.bp b/packages/CredentialManager/wear/robotests/Android.bp
index 589a3d6..db3c363 100644
--- a/packages/CredentialManager/wear/robotests/Android.bp
+++ b/packages/CredentialManager/wear/robotests/Android.bp
@@ -24,6 +24,5 @@
         "framework_graphics_flags_java_lib",
     ],
     java_resource_dirs: ["config"],
-    upstream: true,
     strict_mode: false,
 }
diff --git a/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java b/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java
index 068074a..8e52a00 100644
--- a/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java
+++ b/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java
@@ -38,6 +38,7 @@
 import android.location.provider.ProviderProperties;
 import android.location.provider.ProviderRequest;
 import android.os.Bundle;
+import android.util.Log;
 import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
@@ -301,8 +302,13 @@
                             .setWorkSource(mRequest.getWorkSource())
                             .setHiddenFromAppOps(true)
                             .build();
-                    mLocationManager.requestLocationUpdates(mProvider, request,
-                            mContext.getMainExecutor(), this);
+
+                    try {
+                        mLocationManager.requestLocationUpdates(
+                                mProvider, request, mContext.getMainExecutor(), this);
+                    } catch (IllegalArgumentException e) {
+                        Log.e(TAG, "Failed to request location updates");
+                    }
                 }
             }
         }
@@ -311,7 +317,11 @@
             synchronized (mLock) {
                 int requestCode = mNextFlushCode++;
                 mPendingFlushes.put(requestCode, callback);
-                mLocationManager.requestFlush(mProvider, this, requestCode);
+                try {
+                    mLocationManager.requestFlush(mProvider, this, requestCode);
+                } catch (IllegalArgumentException e) {
+                    Log.e(TAG, "Failed to request flush");
+                }
             }
         }
 
diff --git a/packages/InputDevices/res/raw/keyboard_layout_romanian.kcm b/packages/InputDevices/res/raw/keyboard_layout_romanian.kcm
new file mode 100644
index 0000000..b384a24
--- /dev/null
+++ b/packages/InputDevices/res/raw/keyboard_layout_romanian.kcm
@@ -0,0 +1,357 @@
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Romanian keyboard layout.
+#
+
+type OVERLAY
+
+map key 86 PLUS
+
+### ROW 1
+
+key GRAVE {
+    label:                              '\u201e'
+    base:                               '\u201e'
+    shift:                              '\u201d'
+    ralt:                               '`'
+    ralt+shift:                         '~'
+}
+
+key 1 {
+    label:                              '1'
+    base:                               '1'
+    shift:                              '!'
+    ralt:                               '\u0303'
+}
+
+key 2 {
+    label:                              '2'
+    base:                               '2'
+    shift:                              '@'
+    ralt:                               '\u030C'
+}
+
+key 3 {
+    label:                              '3'
+    base:                               '3'
+    shift:                              '#'
+    ralt:                               '\u0302'
+}
+
+key 4 {
+    label:                              '4'
+    base:                               '4'
+    shift:                              '$'
+    ralt:                               '\u0306'
+}
+
+key 5 {
+    label:                              '5'
+    base:                               '5'
+    shift:                              '%'
+    ralt:                               '\u030A'
+}
+
+key 6 {
+    label:                              '6'
+    base:                               '6'
+    shift:                              '^'
+    ralt:                               '\u0328'
+}
+
+key 7 {
+    label:                              '7'
+    base:                               '7'
+    shift:                              '&'
+    ralt:                               '\u0300'
+}
+
+key 8 {
+    label:                              '8'
+    base:                               '8'
+    shift:                              '*'
+    ralt:                               '\u0307'
+}
+
+key 9 {
+    label:                              '9'
+    base:                               '9'
+    shift:                              '('
+    ralt:                               '\u0301'
+}
+
+key 0 {
+    label:                              '0'
+    base:                               '0'
+    shift:                              ')'
+    ralt:                               '\u030B'
+}
+
+key MINUS {
+    label:                              '-'
+    base:                               '-'
+    shift:                              '_'
+    ralt:                               '\u0308'
+    ralt+shift:                         '\u2013'
+}
+
+key EQUALS {
+    label:                              '='
+    base:                               '='
+    shift:                              '+'
+    ralt:                               '\u0327'
+    ralt+shift:                         '\u00b1'
+}
+
+### ROW 2
+
+key Q {
+    label:                              'Q'
+    base, capslock+shift:               'q'
+    shift, capslock:                    'Q'
+}
+
+key W {
+    label:                              'W'
+    base, capslock+shift:               'w'
+    shift, capslock:                    'W'
+}
+
+key E {
+    label:                              'E'
+    base, capslock+shift:               'e'
+    shift, capslock:                    'E'
+    ralt:                               '\u20ac'
+}
+
+key R {
+    label:                              'R'
+    base, capslock+shift:               'r'
+    shift, capslock:                    'R'
+}
+
+key T {
+    label:                              'T'
+    base, capslock+shift:               't'
+    shift, capslock:                    'T'
+}
+
+key Y {
+    label:                              'Y'
+    base, capslock+shift:               'y'
+    shift, capslock:                    'Y'
+}
+
+key U {
+    label:                              'U'
+    base, capslock+shift:               'u'
+    shift, capslock:                    'U'
+}
+
+key I {
+    label:                              'I'
+    base, capslock+shift:               'i'
+    shift, capslock:                    'I'
+}
+
+key O {
+    label:                              'O'
+    base, capslock+shift:               'o'
+    shift, capslock:                    'O'
+}
+
+key P {
+    label:                              'P'
+    base, capslock+shift:               'p'
+    shift, capslock:                    'P'
+    ralt:                               '\u00a7'
+}
+
+key LEFT_BRACKET {
+    label:                              '\u0102'
+    base, capslock+shift:               '\u0103'
+    shift, capslock:                    '\u0102'
+    ralt:                               '['
+    ralt+shift:                         '{'
+}
+
+key RIGHT_BRACKET {
+    label:                              '\u00ce'
+    base, capslock+shift:               '\u00ee'
+    shift, capslock:                    '\u00ce'
+    ralt:                               ']'
+    ralt+shift:                         '}'
+}
+
+### ROW 3
+
+key A {
+    label:                              'A'
+    base, capslock+shift:               'a'
+    shift, capslock:                    'A'
+}
+
+key S {
+    label:                              'S'
+    base, capslock+shift:               's'
+    shift, capslock:                    'S'
+    ralt:                               '\u00df'
+}
+
+key D {
+    label:                              'D'
+    base, capslock+shift:               'd'
+    shift, capslock:                    'D'
+    ralt:                               '\u0111'
+    ralt+shift, ralt+capslock:          '\u0110'
+    ralt+shift+capslock:                '\u0111'
+}
+
+key F {
+    label:                              'F'
+    base, capslock+shift:               'f'
+    shift, capslock:                    'F'
+}
+
+key G {
+    label:                              'G'
+    base, capslock+shift:               'g'
+    shift, capslock:                    'G'
+}
+
+key H {
+    label:                              'H'
+    base, capslock+shift:               'h'
+    shift, capslock:                    'H'
+}
+
+key J {
+    label:                              'J'
+    base, capslock+shift:               'j'
+    shift, capslock:                    'J'
+}
+
+key K {
+    label:                              'K'
+    base, capslock+shift:               'k'
+    shift, capslock:                    'K'
+}
+
+key L {
+    label:                              'L'
+    base, capslock+shift:               'l'
+    shift, capslock:                    'L'
+    ralt:                               '\u0142'
+    ralt+shift, ralt+capslock:          '\u0141'
+    ralt+shift+capslock:                '\u0142'
+}
+
+key SEMICOLON {
+    label:                              '\u0218'
+    base, capslock+shift:               '\u0219'
+    shift, capslock:                    '\u0218'
+    ralt:                               ';'
+    ralt+shift:                         ':'
+}
+
+key APOSTROPHE {
+    label:                              '\u021a'
+    base, capslock+shift:               '\u021b'
+    shift, capslock:                    '\u021a'
+    ralt:                               '\''
+    ralt+shift:                         '\u0022'
+}
+
+key BACKSLASH {
+    label:                              '\u00c2'
+    base, capslock+shift:               '\u00e2'
+    shift, capslock:                    '\u00c2'
+    ralt:                               '\\'
+    ralt+shift:                         '|'
+}
+
+### ROW 4
+
+key PLUS {
+    label:                              '\\'
+    base:                               '\\'
+    shift:                              '|'
+}
+
+key Z {
+    label:                              'Z'
+    base, capslock+shift:               'z'
+    shift, capslock:                    'Z'
+}
+
+key X {
+    label:                              'X'
+    base, capslock+shift:               'x'
+    shift, capslock:                    'X'
+}
+
+key C {
+    label:                              'C'
+    base, capslock+shift:               'c'
+    shift, capslock:                    'C'
+    ralt:                               '\u00a9'
+}
+
+key V {
+    label:                              'V'
+    base, capslock+shift:               'v'
+    shift, capslock:                    'V'
+}
+
+key B {
+    label:                              'B'
+    base, capslock+shift:               'b'
+    shift, capslock:                    'B'
+}
+
+key N {
+    label:                              'N'
+    base, capslock+shift:               'n'
+    shift, capslock:                    'N'
+}
+
+key M {
+    label:                              'M'
+    base, capslock+shift:               'm'
+    shift, capslock:                    'M'
+}
+
+key COMMA {
+    label:                              ','
+    base:                               ','
+    shift:                              ';'
+    ralt:                               '<'
+    ralt+shift:                         '\u00ab'
+}
+
+key PERIOD {
+    label:                              '.'
+    base:                               '.'
+    shift:                              ':'
+    ralt:                               '>'
+    ralt+shift:                         '\u00bb'
+}
+
+key SLASH {
+    label:                              '/'
+    base:                               '/'
+    shift:                              '?'
+}
diff --git a/packages/InputDevices/res/values/strings.xml b/packages/InputDevices/res/values/strings.xml
index 5a91125..bd7cdc4 100644
--- a/packages/InputDevices/res/values/strings.xml
+++ b/packages/InputDevices/res/values/strings.xml
@@ -164,4 +164,7 @@
 
     <!-- Montenegrin (Cyrillic) keyboard layout label. [CHAR LIMIT=35] -->
     <string name="keyboard_layout_montenegrin_cyrillic">Montenegrin (Cyrillic)</string>
+
+    <!-- Romanian keyboard layout label. [CHAR LIMIT=35] -->
+    <string name="keyboard_layout_romanian">Romanian</string>
 </resources>
diff --git a/packages/InputDevices/res/xml/keyboard_layouts.xml b/packages/InputDevices/res/xml/keyboard_layouts.xml
index 9309489..9ce9a87 100644
--- a/packages/InputDevices/res/xml/keyboard_layouts.xml
+++ b/packages/InputDevices/res/xml/keyboard_layouts.xml
@@ -360,4 +360,11 @@
         android:keyboardLayout="@raw/keyboard_layout_serbian_and_montenegrin_cyrillic"
         android:keyboardLocale="cnr-Cyrl-ME"
         android:keyboardLayoutType="extended" />
+
+    <keyboard-layout
+        android:name="keyboard_layout_romanian"
+        android:label="@string/keyboard_layout_romanian"
+        android:keyboardLayout="@raw/keyboard_layout_romanian"
+        android:keyboardLocale="ro-Latn-RO"
+        android:keyboardLayoutType="qwerty" />
 </keyboard-layouts>
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveActivity.java
index b20117d..c99d37b 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveActivity.java
@@ -19,6 +19,7 @@
 import static android.Manifest.permission;
 import static android.content.pm.PackageManager.GET_PERMISSIONS;
 import static android.content.pm.PackageManager.MATCH_ARCHIVED_PACKAGES;
+import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
 
 import android.app.Activity;
 import android.app.DialogFragment;
@@ -53,6 +54,8 @@
 
     @Override
     public void onCreate(Bundle icicle) {
+        getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
+
         super.onCreate(null);
 
         int callingUid = getLaunchedFromUid();
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveFragment.java
index 42dd382..fbb0fa4 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveFragment.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/UnarchiveFragment.java
@@ -21,10 +21,14 @@
 import android.app.DialogFragment;
 import android.content.DialogInterface;
 import android.os.Bundle;
+import android.widget.Button;
 
 public class UnarchiveFragment extends DialogFragment implements
         DialogInterface.OnClickListener {
 
+    private Dialog mDialog;
+    private Button mRestoreButton;
+
     @Override
     public Dialog onCreateDialog(Bundle savedInstanceState) {
         String appTitle = getArguments().getString(UnarchiveActivity.APP_TITLE);
@@ -40,7 +44,32 @@
         dialogBuilder.setPositiveButton(R.string.restore, this);
         dialogBuilder.setNegativeButton(android.R.string.cancel, this);
 
-        return dialogBuilder.create();
+        mDialog = dialogBuilder.create();
+        return mDialog;
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        if (mDialog != null) {
+            mRestoreButton = ((AlertDialog) mDialog).getButton(DialogInterface.BUTTON_POSITIVE);
+        }
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        if (mRestoreButton != null) {
+            mRestoreButton.setEnabled(false);
+        }
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        if (mRestoreButton != null) {
+            mRestoreButton.setEnabled(true);
+        }
     }
 
     @Override
diff --git a/packages/SettingsLib/DataStore/OWNERS b/packages/SettingsLib/DataStore/OWNERS
new file mode 100644
index 0000000..1219dc4
--- /dev/null
+++ b/packages/SettingsLib/DataStore/OWNERS
@@ -0,0 +1 @@
+include ../OWNERS_catalyst
diff --git a/packages/SettingsLib/DataStore/tests/Android.bp b/packages/SettingsLib/DataStore/tests/Android.bp
index 2e3b42d..6044eab 100644
--- a/packages/SettingsLib/DataStore/tests/Android.bp
+++ b/packages/SettingsLib/DataStore/tests/Android.bp
@@ -25,6 +25,5 @@
     java_resource_dirs: ["config"],
     instrumentation_for: "SettingsLibDataStoreShell",
     coverage_libs: ["SettingsLibDataStore"],
-    upstream: true,
     strict_mode: false,
 }
diff --git a/packages/SettingsLib/Graph/OWNERS b/packages/SettingsLib/Graph/OWNERS
new file mode 100644
index 0000000..1219dc4
--- /dev/null
+++ b/packages/SettingsLib/Graph/OWNERS
@@ -0,0 +1 @@
+include ../OWNERS_catalyst
diff --git a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/GetPreferenceGraphApiHandler.kt b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/GetPreferenceGraphApiHandler.kt
index 3c8d6ed..51813a1c9 100644
--- a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/GetPreferenceGraphApiHandler.kt
+++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/GetPreferenceGraphApiHandler.kt
@@ -44,8 +44,8 @@
     ): PreferenceGraphProto {
         val builder = PreferenceGraphBuilder.of(application, callingPid, callingUid, request)
         if (request.screenKeys.isEmpty()) {
-            for (key in PreferenceScreenRegistry.preferenceScreens.keys) {
-                builder.addPreferenceScreenFromRegistry(key)
+            PreferenceScreenRegistry.preferenceScreenMetadataFactories.forEachKeyAsync {
+                builder.addPreferenceScreenFromRegistry(it)
             }
             for (provider in preferenceScreenProviders) {
                 builder.addPreferenceScreenProvider(provider)
@@ -69,7 +69,6 @@
     val visitedScreens: Set<String> = setOf(),
     val locale: Locale? = null,
     val flags: Int = PreferenceGetterFlags.ALL,
-    val includeValue: Boolean = true, // TODO: clean up
     val includeValueDescriptor: Boolean = true,
 )
 
diff --git a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGetterApi.kt b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGetterApi.kt
index de5731e..2fac545 100644
--- a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGetterApi.kt
+++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGetterApi.kt
@@ -98,7 +98,7 @@
         val preferences = mutableMapOf<PreferenceCoordinate, PreferenceProto>()
         val flags = request.flags
         for ((screenKey, coordinates) in request.preferences.groupBy { it.screenKey }) {
-            val screenMetadata = PreferenceScreenRegistry[screenKey]
+            val screenMetadata = PreferenceScreenRegistry.create(application, screenKey)
             if (screenMetadata == null) {
                 for (coordinate in coordinates) {
                     errors[coordinate] = PreferenceGetterErrorCode.NOT_FOUND
diff --git a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt
index 2cf32de..f001fad 100644
--- a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt
+++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceGraphBuilder.kt
@@ -141,7 +141,7 @@
         }
 
     suspend fun addPreferenceScreenFromRegistry(key: String): Boolean {
-        val metadata = PreferenceScreenRegistry[key] ?: return false
+        val metadata = PreferenceScreenRegistry.create(context, key) ?: return false
         return addPreferenceScreenMetadata(metadata)
     }
 
diff --git a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceSetterApi.kt b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceSetterApi.kt
index bef4bb2..3c870ac 100644
--- a/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceSetterApi.kt
+++ b/packages/SettingsLib/Graph/src/com/android/settingslib/graph/PreferenceSetterApi.kt
@@ -112,7 +112,8 @@
         request: PreferenceSetterRequest,
     ): Int {
         val screenMetadata =
-            PreferenceScreenRegistry[request.screenKey] ?: return PreferenceSetterResult.UNSUPPORTED
+            PreferenceScreenRegistry.create(application, request.screenKey)
+                ?: return PreferenceSetterResult.UNSUPPORTED
         val key = request.key
         val metadata =
             screenMetadata.getPreferenceHierarchy(application).find(key)
diff --git a/packages/SettingsLib/Ipc/Android.bp b/packages/SettingsLib/Ipc/Android.bp
index 2c7209a..bc5a936 100644
--- a/packages/SettingsLib/Ipc/Android.bp
+++ b/packages/SettingsLib/Ipc/Android.bp
@@ -25,7 +25,7 @@
     name: "SettingsLibIpc-testutils",
     srcs: ["testutils/**/*.kt"],
     static_libs: [
-        "Robolectric_all-target_upstream",
+        "Robolectric_all-target",
         "SettingsLibIpc",
         "androidx.test.core",
         "flag-junit",
diff --git a/packages/SettingsLib/Ipc/OWNERS b/packages/SettingsLib/Ipc/OWNERS
new file mode 100644
index 0000000..1219dc4
--- /dev/null
+++ b/packages/SettingsLib/Ipc/OWNERS
@@ -0,0 +1 @@
+include ../OWNERS_catalyst
diff --git a/packages/SettingsLib/Metadata/Android.bp b/packages/SettingsLib/Metadata/Android.bp
index 207637f..564c398 100644
--- a/packages/SettingsLib/Metadata/Android.bp
+++ b/packages/SettingsLib/Metadata/Android.bp
@@ -14,10 +14,9 @@
     ],
     srcs: [":SettingsLibMetadata-srcs"],
     static_libs: [
+        "SettingsLibDataStore",
         "androidx.annotation_annotation",
         "androidx.fragment_fragment",
-        "guava",
-        "SettingsLibDataStore",
     ],
     kotlincflags: ["-Xjvm-default=all"],
 }
diff --git a/packages/SettingsLib/Metadata/OWNERS b/packages/SettingsLib/Metadata/OWNERS
new file mode 100644
index 0000000..1219dc4
--- /dev/null
+++ b/packages/SettingsLib/Metadata/OWNERS
@@ -0,0 +1 @@
+include ../OWNERS_catalyst
diff --git a/packages/SettingsLib/Metadata/processor/src/com/android/settingslib/metadata/PreferenceScreenAnnotationProcessor.kt b/packages/SettingsLib/Metadata/processor/src/com/android/settingslib/metadata/PreferenceScreenAnnotationProcessor.kt
index 7432254..14e3b87 100644
--- a/packages/SettingsLib/Metadata/processor/src/com/android/settingslib/metadata/PreferenceScreenAnnotationProcessor.kt
+++ b/packages/SettingsLib/Metadata/processor/src/com/android/settingslib/metadata/PreferenceScreenAnnotationProcessor.kt
@@ -16,7 +16,6 @@
 
 package com.android.settingslib.metadata
 
-import java.util.TreeMap
 import javax.annotation.processing.AbstractProcessor
 import javax.annotation.processing.ProcessingEnvironment
 import javax.annotation.processing.RoundEnvironment
@@ -33,8 +32,7 @@
 
 /** Processor to gather preference screens annotated with `@ProvidePreferenceScreen`. */
 class PreferenceScreenAnnotationProcessor : AbstractProcessor() {
-    private val screens = TreeMap<String, ConstructorType>()
-    private val overlays = mutableMapOf<String, String>()
+    private val screens = mutableListOf<Screen>()
     private val contextType: TypeMirror by lazy {
         processingEnv.elementUtils.getTypeElement("android.content.Context").asType()
     }
@@ -94,15 +92,10 @@
             )
             return
         }
-        val screenQualifiedName = qualifiedName.toString()
-        screens[screenQualifiedName] = constructorType
         val annotation = annotationMirrors.single { it.isElement(annotationElement) }
-        val overlay = annotation.getOverlay()
-        if (overlay != null) {
-            overlays.put(overlay, screenQualifiedName)?.let {
-                error("$overlay has been overlaid by $it", this)
-            }
-        }
+        val key = annotation.fieldValue<String>("value")!!
+        val overlay = annotation.fieldValue<Boolean>("overlay") == true
+        screens.add(Screen(key, overlay, qualifiedName.toString(), constructorType))
     }
 
     private fun codegen() {
@@ -119,16 +112,8 @@
     }
 
     private fun generateCode(outputPkg: String, outputClass: String, outputFun: String) {
-        for ((overlay, screen) in overlays) {
-            if (screens.remove(overlay) == null) {
-                warn("$overlay is overlaid by $screen but not annotated with @$ANNOTATION_NAME")
-            } else {
-                processingEnv.messager.printMessage(
-                    Diagnostic.Kind.NOTE,
-                    "$overlay is overlaid by $screen",
-                )
-            }
-        }
+        // sort by screen keys to make the output deterministic and naturally fit to FixedArrayMap
+        screens.sort()
         val javaFileObject =
             try {
                 processingEnv.filer.createSourceFile("$outputPkg.$outputClass")
@@ -139,56 +124,64 @@
             }
         javaFileObject.openWriter().use {
             it.write("package $outputPkg;\n\n")
-            it.write("import $PACKAGE.$PREFERENCE_SCREEN_METADATA;\n\n")
+            it.write("import $PACKAGE.FixedArrayMap;\n")
+            it.write("import $PACKAGE.FixedArrayMap.OrderedInitializer;\n")
+            it.write("import $PACKAGE.$FACTORY;\n\n")
             it.write("// Generated by annotation processor for @$ANNOTATION_NAME\n")
             it.write("public final class $outputClass {\n")
             it.write("  private $outputClass() {}\n\n")
-            it.write(
-                "  public static java.util.List<$PREFERENCE_SCREEN_METADATA> " +
-                    "$outputFun(android.content.Context context) {\n"
-            )
-            it.write(
-                "    java.util.ArrayList<$PREFERENCE_SCREEN_METADATA> screens = " +
-                    "new java.util.ArrayList<>(${screens.size});\n"
-            )
-            for ((screen, constructorType) in screens) {
+            it.write("  public static FixedArrayMap<String, $FACTORY> $outputFun() {\n")
+            val size = screens.size
+            it.write("    return new FixedArrayMap<>($size, $outputClass::init);\n")
+            it.write("  }\n\n")
+            fun Screen.write() {
+                it.write("    screens.put(\"$key\", context -> new $klass(")
                 when (constructorType) {
-                    ConstructorType.DEFAULT -> it.write("    screens.add(new $screen());\n")
-                    ConstructorType.CONTEXT -> it.write("    screens.add(new $screen(context));\n")
-                    ConstructorType.SINGLETON -> it.write("    screens.add($screen.INSTANCE);\n")
+                    ConstructorType.DEFAULT -> it.write("));")
+                    ConstructorType.CONTEXT -> it.write("context));")
                 }
+                if (overlay) it.write(" // overlay")
+                it.write("\n")
             }
-            for ((overlay, screen) in overlays) {
-                it.write("    // $overlay is overlaid by $screen\n")
+            it.write("  private static void init(OrderedInitializer<String, $FACTORY> screens) {\n")
+            var index = 0
+            while (index < size) {
+                val screen = screens[index]
+                var next = index + 1
+                while (next < size && screen.key == screens[next].key) next++
+                val n = next - index
+                if (n == 1) {
+                    screen.write()
+                } else if (n == 2 && screen.overlay && !screens[index + 1].overlay) {
+                    it.write("    // ${screen.klass} overlays ${screens[index + 1].klass}\n")
+                    screen.write()
+                } else {
+                    val msg = StringBuilder("${screen.key} is associated to")
+                    for (i in index until next) msg.append(" ${screens[i]}")
+                    processingEnv.messager.printMessage(Diagnostic.Kind.ERROR, msg)
+                }
+                index = next
             }
-            it.write("    return screens;\n")
-            it.write("  }\n")
-            it.write("}")
+            it.write("  }\n}")
         }
     }
 
     private fun AnnotationMirror.isElement(element: TypeElement) =
         processingEnv.typeUtils.isSameType(annotationType.asElement().asType(), element.asType())
 
-    private fun AnnotationMirror.getOverlay(): String? {
+    @Suppress("UNCHECKED_CAST")
+    private fun <T> AnnotationMirror.fieldValue(name: String): T? = field(name)?.value as? T
+
+    private fun AnnotationMirror.field(name: String): AnnotationValue? {
         for ((key, value) in elementValues) {
-            if (key.simpleName.contentEquals("overlay")) {
-                return if (value.isDefaultClassValue(key)) null else value.value.toString()
-            }
+            if (key.simpleName.contentEquals(name)) return value
         }
         return null
     }
 
-    private fun AnnotationValue.isDefaultClassValue(key: ExecutableElement) =
-        processingEnv.typeUtils.isSameType(
-            value as TypeMirror,
-            key.defaultValue.value as TypeMirror,
-        )
-
     private fun TypeElement.getConstructorType(): ConstructorType? {
         var constructor: ExecutableElement? = null
         for (element in enclosedElements) {
-            if (element.isKotlinObject()) return ConstructorType.SINGLETON
             if (element.kind != ElementKind.CONSTRUCTOR) continue
             if (!element.modifiers.contains(Modifier.PUBLIC)) continue
             if (constructor != null) return null
@@ -204,21 +197,27 @@
         }
     }
 
-    private fun Element.isKotlinObject() =
-        kind == ElementKind.FIELD &&
-            modifiers.run { contains(Modifier.PUBLIC) && contains(Modifier.STATIC) } &&
-            simpleName.toString() == "INSTANCE"
-
     private fun warn(msg: CharSequence) =
         processingEnv.messager.printMessage(Diagnostic.Kind.WARNING, msg)
 
     private fun error(msg: CharSequence, element: Element) =
         processingEnv.messager.printMessage(Diagnostic.Kind.ERROR, msg, element)
 
+    private data class Screen(
+        val key: String,
+        val overlay: Boolean,
+        val klass: String,
+        val constructorType: ConstructorType,
+    ) : Comparable<Screen> {
+        override fun compareTo(other: Screen): Int {
+            val diff = key.compareTo(other.key)
+            return if (diff != 0) diff else other.overlay.compareTo(overlay)
+        }
+    }
+
     private enum class ConstructorType {
         DEFAULT, // default constructor with no parameter
         CONTEXT, // constructor with a Context parameter
-        SINGLETON, // Kotlin object class
     }
 
     companion object {
@@ -226,6 +225,7 @@
         private const val ANNOTATION_NAME = "ProvidePreferenceScreen"
         private const val ANNOTATION = "$PACKAGE.$ANNOTATION_NAME"
         private const val PREFERENCE_SCREEN_METADATA = "PreferenceScreenMetadata"
+        private const val FACTORY = "PreferenceScreenMetadataFactory"
 
         private const val OPTIONS_NAME = "ProvidePreferenceScreenOptions"
         private const val OPTIONS = "$PACKAGE.$OPTIONS_NAME"
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/Annotations.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/Annotations.kt
index ea20a74..4bed795 100644
--- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/Annotations.kt
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/Annotations.kt
@@ -16,24 +16,20 @@
 
 package com.android.settingslib.metadata
 
-import kotlin.reflect.KClass
-
 /**
  * Annotation to provide preference screen.
  *
  * The annotated class must satisfy either condition:
  * - the primary constructor has no parameter
  * - the primary constructor has a single [android.content.Context] parameter
- * - it is a Kotlin object class
  *
- * @param overlay if specified, current annotated screen will overlay the given screen
+ * @param value unique preference screen key
+ * @param overlay if true, current annotated screen will overlay the screen that has identical key
  */
 @Retention(AnnotationRetention.SOURCE)
 @Target(AnnotationTarget.CLASS)
 @MustBeDocumented
-annotation class ProvidePreferenceScreen(
-    val overlay: KClass<out PreferenceScreenMetadata> = PreferenceScreenMetadata::class,
-)
+annotation class ProvidePreferenceScreen(val value: String, val overlay: Boolean = false)
 
 /**
  * Provides options for [ProvidePreferenceScreen] annotation processor.
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/FixedArrayMap.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/FixedArrayMap.kt
new file mode 100644
index 0000000..149331a
--- /dev/null
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/FixedArrayMap.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.settingslib.metadata
+
+import java.util.function.Consumer
+
+/**
+ * A compact and immutable data structure provides [get] and for-each operations on ordered
+ * key-value entries.
+ *
+ * The implementation uses fixed-size array and no API is offered to modify entries. Actually,
+ * elements are provided in sorted order during constructor to simplify data management. As a
+ * result, this class is more lightweight compared with `ArrayMap`.
+ */
+@Suppress("UNCHECKED_CAST")
+class FixedArrayMap<K : Comparable<K>, V> {
+    private val array: Array<Any?>
+
+    /** Constructors with empty element. */
+    constructor() {
+        array = emptyArray()
+    }
+
+    /**
+     * Constructors.
+     *
+     * @param size the number of elements
+     * @param consumer initializer to provide exactly [size] elements *in sorted order*
+     */
+    constructor(size: Int, consumer: Consumer<OrderedInitializer<K, V>>) {
+        array = arrayOfNulls(size * 2)
+        val orderedInitializer = OrderedInitializer<K, V>(array)
+        consumer.accept(orderedInitializer)
+        orderedInitializer.verify()
+    }
+
+    /** Returns the number of elements. */
+    val size: Int
+        get() = array.size / 2
+
+    /**
+     * Returns a new [FixedArrayMap] that merged from current and given [FixedArrayMap] instance.
+     *
+     * [other] takes precedence for identical keys.
+     */
+    fun merge(other: FixedArrayMap<K, V>): FixedArrayMap<K, V> {
+        var newKeys = 0
+        other.forEachKey { if (get(it) == null) newKeys++ }
+        return FixedArrayMap(size + newKeys) { initializer ->
+            var index1 = 0
+            var index2 = 0
+            while (!initializer.isDone()) {
+                val key1 = if (index1 < array.size) array[index1] as K else null
+                val key2 = if (index2 < other.array.size) other.array[index2] as K else null
+                val diff =
+                    when {
+                        key1 == null -> 1
+                        key2 == null -> -1
+                        else -> key1.compareTo(key2)
+                    }
+                if (diff < 0) {
+                    initializer.put(key1!!, array[index1 + 1] as V)
+                    index1 += 2
+                } else {
+                    initializer.put(key2!!, other.array[index2 + 1] as V)
+                    index2 += 2
+                    if (diff == 0) index1 += 2
+                }
+            }
+        }
+    }
+
+    /** Traversals keys *in sorted order* and applies given action. */
+    fun forEachKey(action: (key: K) -> Unit) {
+        for (index in array.indices step 2) {
+            action(array[index] as K)
+        }
+    }
+
+    /** Traversals keys *in sorted order* and applies given action. */
+    suspend fun forEachKeyAsync(action: suspend (key: K) -> Unit) {
+        for (index in array.indices step 2) {
+            action(array[index] as K)
+        }
+    }
+
+    /** Traversals key-value entries *in sorted order* and applies given action. */
+    fun forEach(action: (key: K, value: V) -> Unit) {
+        for (index in array.indices step 2) {
+            action(array[index] as K, array[index + 1] as V)
+        }
+    }
+
+    /** Traversals key-value entries in sorted order and applies given action. */
+    suspend fun forEachAsync(action: suspend (key: K, value: V) -> Unit) {
+        for (index in array.indices step 2) {
+            action(array[index] as K, array[index + 1] as V)
+        }
+    }
+
+    /**
+     * Returns the value associated with given key.
+     *
+     * Binary-search algorithm is applied, so this operation takes O(log2(N)) at worst case.
+     */
+    operator fun get(key: K): V? {
+        var low = 0
+        var high = array.size / 2
+        while (low < high) {
+            val mid = (low + high).ushr(1) // safe from overflows
+            val diff = (array[mid * 2] as K).compareTo(key)
+            when {
+                diff < 0 -> low = mid + 1
+                diff > 0 -> high = mid
+                else -> return array[mid * 2 + 1] as V
+            }
+        }
+        return null
+    }
+
+    /** Initializer to provide key-value pairs *in sorted order*. */
+    class OrderedInitializer<K : Comparable<K>, V>
+    internal constructor(private val array: Array<Any?>) {
+        private var index = 0
+
+        internal val size: Int
+            get() = array.size
+
+        /** Returns whether all elements are added. */
+        fun isDone() = index == array.size
+
+        /** Adds a new key-value entry. The key must be provided in sorted order. */
+        fun put(key: K, value: V) {
+            array[index++] = key
+            array[index++] = value
+        }
+
+        internal fun verify() {
+            if (!isDone()) throw IllegalStateException("Missing items: ${index / 2} / ${size / 2}")
+            for (index in 2 until size step 2) {
+                if ((array[index - 2] as K) >= (array[index] as K)) {
+                    throw IllegalStateException("${array[index - 2]} >= ${array[index]}")
+                }
+            }
+        }
+    }
+}
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceHierarchy.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceHierarchy.kt
index 9179f8f..876f615 100644
--- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceHierarchy.kt
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceHierarchy.kt
@@ -16,6 +16,8 @@
 
 package com.android.settingslib.metadata
 
+import android.content.Context
+
 /** A node in preference hierarchy that is associated with [PreferenceMetadata]. */
 open class PreferenceHierarchyNode internal constructor(val metadata: PreferenceMetadata) {
     /**
@@ -32,7 +34,8 @@
  *
  * A root hierarchy represents a preference screen. A sub-hierarchy represents a preference group.
  */
-class PreferenceHierarchy internal constructor(metadata: PreferenceMetadata) :
+class PreferenceHierarchy
+internal constructor(private val context: Context, metadata: PreferenceMetadata) :
     PreferenceHierarchyNode(metadata) {
 
     private val children = mutableListOf<PreferenceHierarchyNode>()
@@ -51,7 +54,8 @@
      *
      * @throws NullPointerException if screen is not registered to [PreferenceScreenRegistry]
      */
-    operator fun String.unaryPlus() = +PreferenceHierarchyNode(PreferenceScreenRegistry[this]!!)
+    operator fun String.unaryPlus() =
+        +PreferenceHierarchyNode(PreferenceScreenRegistry.create(context, this)!!)
 
     operator fun PreferenceHierarchyNode.unaryPlus() = also { children.add(it) }
 
@@ -79,7 +83,7 @@
     /** Adds a preference group to the hierarchy before given key. */
     fun addGroupBefore(key: String, metadata: PreferenceMetadata): PreferenceHierarchy {
         val (list, index) = findPreference(key) ?: (children to children.size)
-        return PreferenceHierarchy(metadata).also { list.add(index, it) }
+        return PreferenceHierarchy(context, metadata).also { list.add(index, it) }
     }
 
     /** Adds a preference to the hierarchy after given key. */
@@ -91,7 +95,7 @@
     /** Adds a preference group to the hierarchy after given key. */
     fun addGroupAfter(key: String, metadata: PreferenceMetadata): PreferenceHierarchy {
         val (list, index) = findPreference(key) ?: (children to children.size - 1)
-        return PreferenceHierarchy(metadata).also { list.add(index + 1, it) }
+        return PreferenceHierarchy(context, metadata).also { list.add(index + 1, it) }
     }
 
     private fun findPreference(key: String): Pair<MutableList<PreferenceHierarchyNode>, Int>? {
@@ -106,12 +110,13 @@
     }
 
     /** Adds a preference group to the hierarchy. */
-    operator fun PreferenceGroup.unaryPlus() = PreferenceHierarchy(this).also { children.add(it) }
+    operator fun PreferenceGroup.unaryPlus() =
+        PreferenceHierarchy(context, this).also { children.add(it) }
 
     /** Adds a preference group and returns its preference hierarchy. */
     @JvmOverloads
     fun addGroup(metadata: PreferenceGroup, order: Int? = null): PreferenceHierarchy =
-        PreferenceHierarchy(metadata).also {
+        PreferenceHierarchy(context, metadata).also {
             this.order = order
             children.add(it)
         }
@@ -128,7 +133,9 @@
      * @throws NullPointerException if screen is not registered to [PreferenceScreenRegistry]
      */
     fun addPreferenceScreen(screenKey: String) {
-        children.add(PreferenceHierarchy(PreferenceScreenRegistry[screenKey]!!))
+        children.add(
+            PreferenceHierarchy(context, PreferenceScreenRegistry.create(context, screenKey)!!)
+        )
     }
 
     /** Extensions to add more preferences to the hierarchy. */
@@ -175,5 +182,8 @@
  * Builder function to create [PreferenceHierarchy] in
  * [DSL](https://kotlinlang.org/docs/type-safe-builders.html) manner.
  */
-fun preferenceHierarchy(metadata: PreferenceMetadata, init: PreferenceHierarchy.() -> Unit) =
-    PreferenceHierarchy(metadata).also(init)
+fun preferenceHierarchy(
+    context: Context,
+    metadata: PreferenceMetadata,
+    init: PreferenceHierarchy.() -> Unit,
+) = PreferenceHierarchy(context, metadata).also(init)
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenMetadata.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenMetadata.kt
index fc68ea7..850d452 100644
--- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenMetadata.kt
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenMetadata.kt
@@ -64,3 +64,14 @@
      */
     fun getLaunchIntent(context: Context, metadata: PreferenceMetadata?): Intent? = null
 }
+
+/** Factory of [PreferenceScreenMetadata]. */
+fun interface PreferenceScreenMetadataFactory {
+
+    /**
+     * Creates a new [PreferenceScreenMetadata].
+     *
+     * @param context application context to create the PreferenceScreenMetadata
+     */
+    fun create(context: Context): PreferenceScreenMetadata
+}
diff --git a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenRegistry.kt b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenRegistry.kt
index ff09910..9fc2134 100644
--- a/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenRegistry.kt
+++ b/packages/SettingsLib/Metadata/src/com/android/settingslib/metadata/PreferenceScreenRegistry.kt
@@ -18,11 +18,6 @@
 
 import android.content.Context
 import com.android.settingslib.datastore.KeyValueStore
-import com.google.common.base.Supplier
-import com.google.common.base.Suppliers
-import com.google.common.collect.ImmutableMap
-
-private typealias PreferenceScreenMap = ImmutableMap<String, PreferenceScreenMetadata>
 
 /** Registry of all available preference screens in the app. */
 object PreferenceScreenRegistry : ReadWritePermitProvider {
@@ -30,12 +25,12 @@
     /** Provider of key-value store. */
     private lateinit var keyValueStoreProvider: KeyValueStoreProvider
 
-    private var preferenceScreensSupplier: Supplier<PreferenceScreenMap> = Supplier {
-        ImmutableMap.of()
-    }
-
-    val preferenceScreens: PreferenceScreenMap
-        get() = preferenceScreensSupplier.get()
+    /**
+     * Factories of all available [PreferenceScreenMetadata]s.
+     *
+     * The map key is preference screen key.
+     */
+    var preferenceScreenMetadataFactories = FixedArrayMap<String, PreferenceScreenMetadataFactory>()
 
     private var readWritePermitProvider: ReadWritePermitProvider =
         object : ReadWritePermitProvider {}
@@ -54,26 +49,9 @@
     fun getKeyValueStore(context: Context, preference: PreferenceMetadata): KeyValueStore? =
         keyValueStoreProvider.getKeyValueStore(context, preference)
 
-    /** Sets supplier to provide available preference screens. */
-    fun setPreferenceScreensSupplier(supplier: Supplier<List<PreferenceScreenMetadata>>) {
-        preferenceScreensSupplier =
-            Suppliers.memoize {
-                val screensBuilder = ImmutableMap.builder<String, PreferenceScreenMetadata>()
-                for (screen in supplier.get()) screensBuilder.put(screen.key, screen)
-                screensBuilder.buildOrThrow()
-            }
-    }
-
-    /** Sets available preference screens. */
-    fun setPreferenceScreens(vararg screens: PreferenceScreenMetadata) {
-        val screensBuilder = ImmutableMap.builder<String, PreferenceScreenMetadata>()
-        for (screen in screens) screensBuilder.put(screen.key, screen)
-        preferenceScreensSupplier = Suppliers.ofInstance(screensBuilder.buildOrThrow())
-    }
-
-    /** Returns [PreferenceScreenMetadata] of particular key. */
-    operator fun get(key: String?): PreferenceScreenMetadata? =
-        if (key != null) preferenceScreens[key] else null
+    /** Creates [PreferenceScreenMetadata] of particular screen key. */
+    fun create(context: Context, screenKey: String?): PreferenceScreenMetadata? =
+        screenKey?.let { preferenceScreenMetadataFactories[it]?.create(context.applicationContext) }
 
     /**
      * Sets the provider to check read write permit. Read and write requests are denied by default.
diff --git a/packages/SettingsLib/OWNERS_catalyst b/packages/SettingsLib/OWNERS_catalyst
new file mode 100644
index 0000000..d44ac68
--- /dev/null
+++ b/packages/SettingsLib/OWNERS_catalyst
@@ -0,0 +1,9 @@
+# OWNERS of Catalyst libraries (DataStore, Metadata, etc.)
+
+# Main developers
+jiannan@google.com
+cechkahn@google.com
+sunnyshao@google.com
+
+# Emergency only
+cipson@google.com
diff --git a/packages/SettingsLib/Preference/OWNERS b/packages/SettingsLib/Preference/OWNERS
new file mode 100644
index 0000000..1219dc4
--- /dev/null
+++ b/packages/SettingsLib/Preference/OWNERS
@@ -0,0 +1 @@
+include ../OWNERS_catalyst
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceFragment.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceFragment.kt
index 991d5b7..e237a6a 100644
--- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceFragment.kt
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceFragment.kt
@@ -83,7 +83,7 @@
     @XmlRes protected open fun getPreferenceScreenResId(context: Context): Int = 0
 
     protected fun getPreferenceScreenCreator(context: Context): PreferenceScreenCreator? =
-        (PreferenceScreenRegistry[getPreferenceScreenBindingKey(context)]
+        (PreferenceScreenRegistry.create(context, getPreferenceScreenBindingKey(context))
                 as? PreferenceScreenCreator)
             ?.run { if (isFlagEnabled(context)) this else null }
 
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt
index a9e20f28..91abd8b 100644
--- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenBindingHelper.kt
@@ -220,7 +220,7 @@
         /** Updates preference screen that has incomplete hierarchy. */
         @JvmStatic
         fun bind(preferenceScreen: PreferenceScreen) {
-            PreferenceScreenRegistry[preferenceScreen.key]?.run {
+            PreferenceScreenRegistry.create(preferenceScreen.context, preferenceScreen.key)?.run {
                 if (!hasCompleteHierarchy()) {
                     val preferenceBindingFactory =
                         (this as? PreferenceScreenCreator)?.preferenceBindingFactory ?: return
diff --git a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenFactory.kt b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenFactory.kt
index 7f99d7a..211b3bd 100644
--- a/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenFactory.kt
+++ b/packages/SettingsLib/Preference/src/com/android/settingslib/preference/PreferenceScreenFactory.kt
@@ -81,8 +81,8 @@
      *
      * The screen must be registered in [PreferenceScreenFactory] and provide a complete hierarchy.
      */
-    fun createBindingScreen(screenKey: String?): PreferenceScreen? {
-        val metadata = PreferenceScreenRegistry[screenKey] ?: return null
+    fun createBindingScreen(context: Context, screenKey: String?): PreferenceScreen? {
+        val metadata = PreferenceScreenRegistry.create(context, screenKey) ?: return null
         if (metadata is PreferenceScreenCreator && metadata.hasCompleteHierarchy()) {
             return metadata.createPreferenceScreen(this)
         }
@@ -93,11 +93,12 @@
         /** Creates [PreferenceScreen] from [PreferenceScreenRegistry]. */
         @JvmStatic
         fun createBindingScreen(preference: Preference): PreferenceScreen? {
+            val context = preference.context
             val preferenceScreenCreator =
-                (PreferenceScreenRegistry[preference.key] as? PreferenceScreenCreator)
-                    ?: return null
+                (PreferenceScreenRegistry.create(context, preference.key)
+                    as? PreferenceScreenCreator) ?: return null
             if (!preferenceScreenCreator.hasCompleteHierarchy()) return null
-            val factory = PreferenceScreenFactory(preference.context)
+            val factory = PreferenceScreenFactory(context)
             val preferenceScreen = preferenceScreenCreator.createPreferenceScreen(factory)
             factory.preferenceManager.setPreferences(preferenceScreen)
             return preferenceScreen
diff --git a/packages/SettingsLib/SearchWidget/res/values-mn/strings.xml b/packages/SettingsLib/SearchWidget/res/values-mn/strings.xml
index 01a7797..bb9cf49 100644
--- a/packages/SettingsLib/SearchWidget/res/values-mn/strings.xml
+++ b/packages/SettingsLib/SearchWidget/res/values-mn/strings.xml
@@ -17,5 +17,5 @@
 
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="search_menu" msgid="1914043873178389845">"Тохиргоог хайх"</string>
+    <string name="search_menu" msgid="1914043873178389845">"Тохиргооноос хайх"</string>
 </resources>
diff --git a/packages/SettingsLib/Service/OWNERS b/packages/SettingsLib/Service/OWNERS
new file mode 100644
index 0000000..1219dc4
--- /dev/null
+++ b/packages/SettingsLib/Service/OWNERS
@@ -0,0 +1 @@
+include ../OWNERS_catalyst
diff --git a/packages/SettingsLib/Spa/screenshot/robotests/Android.bp b/packages/SettingsLib/Spa/screenshot/robotests/Android.bp
index f6477e2..dd6743b 100644
--- a/packages/SettingsLib/Spa/screenshot/robotests/Android.bp
+++ b/packages/SettingsLib/Spa/screenshot/robotests/Android.bp
@@ -68,7 +68,6 @@
         "android.test.mock.stubs.system",
         "truth",
     ],
-    upstream: true,
     java_resource_dirs: ["config"],
     instrumentation_for: "SpaRoboApp",
 
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/Flows.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/Flows.kt
index 61b8b7f..41b1319 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/Flows.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/util/Flows.kt
@@ -54,9 +54,11 @@
 fun <T1, T2> Flow<T1>.waitFirst(otherFlow: Flow<T2>): Flow<T1> =
     combine(otherFlow.take(1)) { value, _ -> value }
 
-
 /**
  * Collects the latest value of given flow with a provided action with [LifecycleOwner].
+ *
+ * This helper function is designed to work with non Compose code. For Compose, please collect the
+ * flow in a `LaunchedEffect` instead to ensure disposable and re-enter safe.
  */
 fun <T> Flow<T>.collectLatestWithLifecycle(
     lifecycleOwner: LifecycleOwner,
diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/BytesFormatter.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/BytesFormatter.kt
new file mode 100644
index 0000000..5b7e2a8
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/framework/common/BytesFormatter.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.spaprivileged.framework.common
+
+import android.content.Context
+import android.content.res.Resources
+import android.icu.text.DecimalFormat
+import android.icu.text.MeasureFormat
+import android.icu.text.NumberFormat
+import android.icu.text.UnicodeSet
+import android.icu.text.UnicodeSetSpanner
+import android.icu.util.Measure
+import android.text.format.Formatter
+import android.text.format.Formatter.RoundedBytesResult
+import java.math.BigDecimal
+
+class BytesFormatter(resources: Resources) {
+
+    enum class UseCase(val flag: Int) {
+        FileSize(Formatter.FLAG_SI_UNITS),
+        DataUsage(Formatter.FLAG_IEC_UNITS),
+    }
+
+    data class Result(val number: String, val units: String)
+
+    constructor(context: Context) : this(context.resources)
+
+    private val locale = resources.configuration.locales[0]
+
+    fun format(bytes: Long, useCase: UseCase): String {
+        val rounded = RoundedBytesResult.roundBytes(bytes, useCase.flag)
+        val numberFormatter = getNumberFormatter(rounded.fractionDigits)
+        return numberFormatter.formatRoundedBytesResult(rounded)
+    }
+
+    fun formatWithUnits(bytes: Long, useCase: UseCase): Result {
+        val rounded = RoundedBytesResult.roundBytes(bytes, useCase.flag)
+        val numberFormatter = getNumberFormatter(rounded.fractionDigits)
+        val formattedString = numberFormatter.formatRoundedBytesResult(rounded)
+        val formattedNumber = numberFormatter.format(rounded.value)
+        return Result(
+            number = formattedNumber,
+            units = formattedString.removeFirst(formattedNumber),
+        )
+    }
+
+    private fun NumberFormat.formatRoundedBytesResult(rounded: RoundedBytesResult): String {
+        val measureFormatter =
+            MeasureFormat.getInstance(locale, MeasureFormat.FormatWidth.SHORT, this)
+        return measureFormatter.format(Measure(rounded.value, rounded.units))
+    }
+
+    private fun getNumberFormatter(fractionDigits: Int) =
+        NumberFormat.getInstance(locale).apply {
+            minimumFractionDigits = fractionDigits
+            maximumFractionDigits = fractionDigits
+            isGroupingUsed = false
+            if (this is DecimalFormat) {
+                setRoundingMode(BigDecimal.ROUND_HALF_UP)
+            }
+        }
+
+    private companion object {
+        fun String.removeFirst(removed: String): String =
+            SPACES_AND_CONTROLS.trim(replaceFirst(removed, "")).toString()
+
+        val SPACES_AND_CONTROLS = UnicodeSetSpanner(UnicodeSet("[[:Zs:][:Cf:]]").freeze())
+    }
+}
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/framework/common/BytesFormatterTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/framework/common/BytesFormatterTest.kt
new file mode 100644
index 0000000..7220848
--- /dev/null
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/framework/common/BytesFormatterTest.kt
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.spaprivileged.framework.common
+
+import android.content.Context
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class BytesFormatterTest {
+
+    private val context: Context = ApplicationProvider.getApplicationContext()
+
+    private val formatter = BytesFormatter(context)
+
+    @Test
+    fun `Zero bytes`() {
+        // Given a byte value of 0, the formatted output should be "0 byte" for both FileSize
+        // and DataUsage UseCases. This verifies special handling of zero values.
+
+        val fileSizeResult = formatter.format(0, BytesFormatter.UseCase.FileSize)
+        assertThat(fileSizeResult).isEqualTo("0 byte")
+
+        val dataUsageResult = formatter.format(0, BytesFormatter.UseCase.DataUsage)
+        assertThat(dataUsageResult).isEqualTo("0 byte")
+    }
+
+    @Test
+    fun `Positive bytes`() {
+        // Given a positive byte value (e.g., 1000), the formatted output should be correctly
+        // displayed with appropriate units (e.g., '1.00 kB') for both UseCases.
+
+        val fileSizeResult = formatter.format(1000, BytesFormatter.UseCase.FileSize)
+        assertThat(fileSizeResult).isEqualTo("1.00 kB")
+
+        val dataUsageResult = formatter.format(1024, BytesFormatter.UseCase.DataUsage)
+        assertThat(dataUsageResult).isEqualTo("1.00 kB")
+    }
+
+    @Test
+    fun `Large bytes`() {
+        // Given a very large byte value (e.g., Long.MAX_VALUE), the formatted output should be
+        // correctly displayed with the largest unit (e.g., 'PB') for both UseCases.
+
+        val fileSizeResult = formatter.format(Long.MAX_VALUE, BytesFormatter.UseCase.FileSize)
+        assertThat(fileSizeResult).isEqualTo("9223 PB")
+
+        val dataUsageResult = formatter.format(Long.MAX_VALUE, BytesFormatter.UseCase.DataUsage)
+        assertThat(dataUsageResult).isEqualTo("8192 PB")
+    }
+
+    @Test
+    fun `Bytes requiring rounding`() {
+        // Given byte values that require rounding (e.g., 1512), the formatted output should be
+        // rounded to the appropriate number of decimal places (e.g., '1.51 kB').
+
+        val fileSizeResult = formatter.format(1512, BytesFormatter.UseCase.FileSize)
+        assertThat(fileSizeResult).isEqualTo("1.51 kB")
+
+        val dataUsageResult = formatter.format(1512, BytesFormatter.UseCase.DataUsage)
+        assertThat(dataUsageResult).isEqualTo("1.48 kB")
+    }
+
+    @Test
+    fun `FileSize UseCase`() {
+        // When the UseCase is FileSize, the correct units (byte, KB, kB, GB, TB, PB) should
+        // be used.
+        val values =
+            listOf(
+                1L,
+                1024L,
+                1024L * 1024L,
+                1024L * 1024L * 1024L,
+                1024L * 1024L * 1024L * 1024L,
+                1024L * 1024L * 1024L * 1024L * 1024L,
+                1024L * 1024L * 1024L * 1024L * 1024L * 1024L,
+            )
+        val expectedUnits = listOf("byte", "kB", "MB", "GB", "TB", "PB", "PB")
+
+        values.zip(expectedUnits).forEach { (value, expectedUnit) ->
+            val result = formatter.format(value, BytesFormatter.UseCase.FileSize)
+            assertThat(result).contains(expectedUnit)
+        }
+    }
+
+    @Test
+    fun `DataUsage UseCase`() {
+        // When the UseCase is DataUsage, the correct units (byte, kB, MB, GB, TB, PB) should
+        // be used.
+        val values =
+            listOf(
+                1L,
+                1024L,
+                1024L * 1024L,
+                1024L * 1024L * 1024L,
+                1024L * 1024L * 1024L * 1024L,
+                1024L * 1024L * 1024L * 1024L * 1024L,
+                1024L * 1024L * 1024L * 1024L * 1024L * 1024L,
+            )
+        val expectedUnits = listOf("byte", "kB", "MB", "GB", "TB", "PB", "PB")
+
+        values.zip(expectedUnits).forEach { (value, expectedUnit) ->
+            val result = formatter.format(value, BytesFormatter.UseCase.DataUsage)
+            assertThat(result).contains(expectedUnit)
+        }
+    }
+
+    @Test
+    fun `Fraction digits`() {
+        // The number of fraction digits in the output should be correctly determined based on
+        // the rounded byte value.
+
+        assertThat(formatter.format(1500, BytesFormatter.UseCase.FileSize)).isEqualTo("1.50 kB")
+        assertThat(formatter.format(1050, BytesFormatter.UseCase.FileSize)).isEqualTo("1.05 kB")
+        assertThat(formatter.format(999, BytesFormatter.UseCase.FileSize)).isEqualTo("1.00 kB")
+    }
+
+    @Test
+    fun `Rounding mode`() {
+        // The rounding mode used for formatting should be ROUND_HALF_UP.
+
+        val result = formatter.format(1006, BytesFormatter.UseCase.FileSize)
+
+        assertThat(result).isEqualTo("1.01 kB") // Ensure rounding mode is effective
+    }
+
+    @Test
+    fun `Grouping separator`() {
+        // Grouping separators should not be used in the formatted output.
+
+        val result = formatter.format(Long.MAX_VALUE, BytesFormatter.UseCase.FileSize)
+
+        assertThat(result).isEqualTo("9223 PB")
+    }
+
+    @Test
+    fun `Format with units`() {
+        // Verify that the `formatWithUnits` method correctly formats the given bytes with the
+        // specified units.
+
+        val resultByte = formatter.formatWithUnits(0, BytesFormatter.UseCase.FileSize)
+        assertThat(resultByte).isEqualTo(BytesFormatter.Result("0", "byte"))
+
+        val resultKb = formatter.formatWithUnits(1000, BytesFormatter.UseCase.FileSize)
+        assertThat(resultKb).isEqualTo(BytesFormatter.Result("1.00", "kB"))
+
+        val resultMb = formatter.formatWithUnits(479_999_999, BytesFormatter.UseCase.FileSize)
+        assertThat(resultMb).isEqualTo(BytesFormatter.Result("480", "MB"))
+
+        val resultGb = formatter.formatWithUnits(20_100_000_000, BytesFormatter.UseCase.FileSize)
+        assertThat(resultGb).isEqualTo(BytesFormatter.Result("20.10", "GB"))
+
+        val resultTb =
+            formatter.formatWithUnits(300_100_000_000_000, BytesFormatter.UseCase.FileSize)
+        assertThat(resultTb).isEqualTo(BytesFormatter.Result("300", "TB"))
+
+        val resultPb =
+            formatter.formatWithUnits(1000_000_000_000_000, BytesFormatter.UseCase.FileSize)
+        assertThat(resultPb).isEqualTo(BytesFormatter.Result("1.00", "PB"))
+    }
+}
diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPageTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPageTest.kt
index 0d73cb3..798e2d4 100644
--- a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPageTest.kt
+++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/template/app/TogglePermissionAppInfoPageTest.kt
@@ -160,23 +160,25 @@
     @Test
     fun infoPage_whenChangeableAndClick() {
         val listModel = TestTogglePermissionAppListModel(isAllowed = false, isChangeable = true)
+        val switchTitle = context.getString(listModel.switchTitleResId)
 
         setTogglePermissionAppInfoPage(listModel)
-        composeTestRule.onNodeWithText(context.getString(listModel.switchTitleResId)).performClick()
+        composeTestRule.waitUntilExists(hasText(switchTitle))
+        composeTestRule.onNodeWithText(switchTitle).performClick()
 
-        composeTestRule.waitUntilExists(
-            hasText(context.getString(listModel.switchTitleResId)) and isOn())
+        composeTestRule.waitUntilExists(hasText(switchTitle) and isOn())
     }
 
     @Test
     fun infoPage_whenNotChangeableAndClick() {
         val listModel = TestTogglePermissionAppListModel(isAllowed = false, isChangeable = false)
+        val switchTitle = context.getString(listModel.switchTitleResId)
 
         setTogglePermissionAppInfoPage(listModel)
-        composeTestRule.onNodeWithText(context.getString(listModel.switchTitleResId)).performClick()
+        composeTestRule.waitUntilExists(hasText(switchTitle))
+        composeTestRule.onNodeWithText(switchTitle).performClick()
 
-        composeTestRule.waitUntilExists(
-            hasText(context.getString(listModel.switchTitleResId)) and isOff())
+        composeTestRule.waitUntilExists(hasText(switchTitle) and isOff())
     }
 
     @Test
diff --git a/packages/SettingsLib/aconfig/settingslib.aconfig b/packages/SettingsLib/aconfig/settingslib.aconfig
index cc996c5..bbe08f2 100644
--- a/packages/SettingsLib/aconfig/settingslib.aconfig
+++ b/packages/SettingsLib/aconfig/settingslib.aconfig
@@ -9,10 +9,10 @@
 }
 
 flag {
-   name: "bluetooth_qs_tile_dialog_auto_on_toggle"
-   namespace: "bluetooth"
-   description: "Displays the auto on toggle in the bluetooth QS tile dialog"
-   bug: "316985153"
+    name: "bluetooth_qs_tile_dialog_auto_on_toggle"
+    namespace: "bluetooth"
+    description: "Displays the auto on toggle in the bluetooth QS tile dialog"
+    bug: "316985153"
 }
 
 flag {
@@ -23,24 +23,24 @@
 }
 
 flag {
-  name: "enable_le_audio_sharing"
-  namespace: "pixel_cross_device_control"
-  description: "Gates whether to enable LE audio sharing"
-  bug: "323125723"
+    name: "enable_le_audio_sharing"
+    namespace: "pixel_cross_device_control"
+    description: "Gates whether to enable LE audio sharing"
+    bug: "323125723"
 }
 
 flag {
-  name: "enable_le_audio_qr_code_private_broadcast_sharing"
-  namespace: "pixel_cross_device_control"
-  description: "Gates whether to enable LE audio private broadcast sharing via QR code"
-  bug: "323125723"
+    name: "enable_le_audio_qr_code_private_broadcast_sharing"
+    namespace: "pixel_cross_device_control"
+    description: "Gates whether to enable LE audio private broadcast sharing via QR code"
+    bug: "323125723"
 }
 
 flag {
-  name: "enable_hide_exclusively_managed_bluetooth_device"
-  namespace: "dck_framework"
-  description: "Hide exclusively managed Bluetooth devices in BT settings menu."
-  bug: "324475542"
+    name: "enable_hide_exclusively_managed_bluetooth_device"
+    namespace: "dck_framework"
+    description: "Hide exclusively managed Bluetooth devices in BT settings menu."
+    bug: "324475542"
 }
 
 flag {
@@ -89,7 +89,7 @@
     description: "the battery saver can pause all non-essential apps and their corresponding notification when device is in locked state to introduce the security vulnerability"
     bug: "346513692"
     metadata {
-      purpose: PURPOSE_BUGFIX
+        purpose: PURPOSE_BUGFIX
     }
 }
 
@@ -113,13 +113,13 @@
 }
 
 flag {
-  name: "asha_profile_access_profile_enabled_true"
-  namespace: "accessibility"
-  description: "Changes the return value of HearingAidProfile.accessProfileEnabled() to true"
-  bug: "356530795"
-  metadata {
-    purpose: PURPOSE_BUGFIX
-  }
+    name: "asha_profile_access_profile_enabled_true"
+    namespace: "accessibility"
+    description: "Changes the return value of HearingAidProfile.accessProfileEnabled() to true"
+    bug: "356530795"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
 }
 
 flag {
@@ -209,3 +209,13 @@
         purpose: PURPOSE_BUGFIX
     }
 }
+
+flag {
+    name: "enable_temporary_bond_devices_ui"
+    namespace: "cross_device_experiences"
+    description: "UI changes for temporary bond devices in audio sharing."
+    bug: "362859132"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/packages/SettingsLib/aconfig/settingslib_media_flag_declarations.aconfig b/packages/SettingsLib/aconfig/settingslib_media_flag_declarations.aconfig
index 6f614b3..065a61c 100644
--- a/packages/SettingsLib/aconfig/settingslib_media_flag_declarations.aconfig
+++ b/packages/SettingsLib/aconfig/settingslib_media_flag_declarations.aconfig
@@ -2,17 +2,17 @@
 container: "system"
 
 flag {
-  name: "use_media_router2_for_info_media_manager"
-  namespace: "media_solutions"
-  description: "Gates whether to use a MediaRouter2-based implementation of InfoMediaManager, instead of the legacy MediaRouter2Manager-based implementation."
-  bug: "192657812"
+    name: "use_media_router2_for_info_media_manager"
+    namespace: "media_solutions"
+    description: "Gates whether to use a MediaRouter2-based implementation of InfoMediaManager, instead of the legacy MediaRouter2Manager-based implementation."
+    bug: "192657812"
 }
 
 flag {
-  name: "enable_tv_media_output_dialog"
-  namespace: "tv_system_ui"
-  description: "Gates all the changes for the tv specific media output dialog"
-  bug: "303205631"
+    name: "enable_tv_media_output_dialog"
+    namespace: "tv_system_ui"
+    description: "Gates all the changes for the tv specific media output dialog"
+    bug: "303205631"
 }
 
 flag {
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 7e9e369..6a169e9 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -110,8 +110,7 @@
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Aktief (net links)"</string>
     <string name="bluetooth_hearing_aid_right_active" msgid="2244728507170385397">"Aktief (net regs)"</string>
     <string name="bluetooth_hearing_aid_left_and_right_active" msgid="4294571497939983181">"Aktief (links en regs)"</string>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"Kon nie omgewing opdateer nie"</string>
     <string name="bluetooth_active_media_only_battery_level" msgid="7772517511061834073">"Aktief (net media). <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> battery."</string>
     <string name="bluetooth_active_media_only_battery_level_untethered" msgid="7444753133664620926">"Aktiewe (net media). 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> battery"</string>
     <string name="bluetooth_battery_level_lea_support" msgid="5968584103507988820">"Gekoppel (steun oudiodeling). <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> battery"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index 6161873..be2daa2 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -110,8 +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>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"በዙሪያ ያሉትን ማዘመን አልተቻለም"</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>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index a298294..f334f99 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -110,8 +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>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"تعذَّر تعديل حالة الأصوات المحيطة"</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>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index addf12b..6e67645 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -110,8 +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>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"আশ-পাশ আপডে’ট কৰিব পৰা নগ’ল"</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>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index 6f97c9c..b8e7582 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -110,8 +110,7 @@
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Aktiv (yalnız sol)"</string>
     <string name="bluetooth_hearing_aid_right_active" msgid="2244728507170385397">"Aktiv (yalnız sağ)"</string>
     <string name="bluetooth_hearing_aid_left_and_right_active" msgid="4294571497939983181">"Aktiv (sol və sağ)"</string>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"Ətraf mühit güncəllənmədi"</string>
     <string name="bluetooth_active_media_only_battery_level" msgid="7772517511061834073">"Aktiv (yalnız media). <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> batareya."</string>
     <string name="bluetooth_active_media_only_battery_level_untethered" msgid="7444753133664620926">"Aktiv (yalnız media). 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> batareya."</string>
     <string name="bluetooth_battery_level_lea_support" msgid="5968584103507988820">"Qoşulub (audio paylaşma dəstəklənir). <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> batareya."</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index fc55fff..295dd83 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -110,8 +110,7 @@
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Aktivno (samo levo)"</string>
     <string name="bluetooth_hearing_aid_right_active" msgid="2244728507170385397">"Aktivno (samo desno)"</string>
     <string name="bluetooth_hearing_aid_left_and_right_active" msgid="4294571497939983181">"Aktivno (levo i desno)"</string>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"Ažuriranje okruženja nije uspelo"</string>
     <string name="bluetooth_active_media_only_battery_level" msgid="7772517511061834073">"Aktivno (samo za medije). <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> baterije."</string>
     <string name="bluetooth_active_media_only_battery_level_untethered" msgid="7444753133664620926">"Aktivno (samo za medije). Levo: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, desno: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> baterije."</string>
     <string name="bluetooth_battery_level_lea_support" msgid="5968584103507988820">"Povezano (podržava deljenje zvuka), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> baterije."</string>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index be86aca..90ed686 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -110,8 +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>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"Не ўдалося абнавіць стан навакольных гукаў"</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>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index 624c55b..d450687 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -110,8 +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>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"Данните за околните звуци не бяха актуализирани"</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>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index fcc99ca..8cb1542 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -110,8 +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>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"সারাউন্ডিং আপডেট করা যায়নি"</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>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index fea1fa5..1847a46 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -110,8 +110,7 @@
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Aktivno (samo lijevo)"</string>
     <string name="bluetooth_hearing_aid_right_active" msgid="2244728507170385397">"Aktivno (samo desno)"</string>
     <string name="bluetooth_hearing_aid_left_and_right_active" msgid="4294571497939983181">"Aktivno (lijevo i desno)"</string>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"Ažuriranje okruženja nije uspjelo"</string>
     <string name="bluetooth_active_media_only_battery_level" msgid="7772517511061834073">"Aktivno (samo za medijski sadržaj). <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> baterije."</string>
     <string name="bluetooth_active_media_only_battery_level_untethered" msgid="7444753133664620926">"Aktivno (samo za medijski sadržaj). L: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> baterije, D: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> baterije."</string>
     <string name="bluetooth_battery_level_lea_support" msgid="5968584103507988820">"Povezano (podržava dijeljenje zvuka). <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> baterije."</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 3f73bcc..ad9cc35 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -110,8 +110,7 @@
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Actiu (només l\'esquerre)"</string>
     <string name="bluetooth_hearing_aid_right_active" msgid="2244728507170385397">"Actiu (només el dret)"</string>
     <string name="bluetooth_hearing_aid_left_and_right_active" msgid="4294571497939983181">"Actiu (esquerre i dret)"</string>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"No s\'ha pogut actualitzar l\'entorn"</string>
     <string name="bluetooth_active_media_only_battery_level" msgid="7772517511061834073">"Actiu (només contingut multimèdia). <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de bateria."</string>
     <string name="bluetooth_active_media_only_battery_level_untethered" msgid="7444753133664620926">"Actiu (només contingut multimèdia), 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_lea_support" msgid="5968584103507988820">"Connectat (admet compartició d\'àudio). <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de bateria."</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index 940e2f6..867ddfd 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -110,8 +110,7 @@
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Aktivní (pouze levé)"</string>
     <string name="bluetooth_hearing_aid_right_active" msgid="2244728507170385397">"Aktivní (pouze pravé)"</string>
     <string name="bluetooth_hearing_aid_left_and_right_active" msgid="4294571497939983181">"Aktivní (levé a pravé)"</string>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"Okolí se nepodařilo aktualizovat"</string>
     <string name="bluetooth_active_media_only_battery_level" msgid="7772517511061834073">"Aktivní (pouze média). Baterie: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
     <string name="bluetooth_active_media_only_battery_level_untethered" msgid="7444753133664620926">"Aktivní (pouze média), baterie: L <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, P <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>."</string>
     <string name="bluetooth_battery_level_lea_support" msgid="5968584103507988820">"Připojeno (podporuje sdílení zvuku), baterie: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index 2b30847..aa85c1a 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -110,8 +110,7 @@
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Aktiveret (kun venstre)"</string>
     <string name="bluetooth_hearing_aid_right_active" msgid="2244728507170385397">"Aktiveret (kun højre)"</string>
     <string name="bluetooth_hearing_aid_left_and_right_active" msgid="4294571497939983181">"Aktiveret (venstre og højre)"</string>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"Omgivelserne kunne ikke opdateres"</string>
     <string name="bluetooth_active_media_only_battery_level" msgid="7772517511061834073">"Aktiveret (kun for medier). <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> batteri."</string>
     <string name="bluetooth_active_media_only_battery_level_untethered" msgid="7444753133664620926">"Aktiveret (kun for medier), V: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, H: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> batteri."</string>
     <string name="bluetooth_battery_level_lea_support" msgid="5968584103507988820">"Forbundet (understøtter lyddeling). <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> batteri."</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index e9e9275f..62aa4a7 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -110,8 +110,7 @@
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Aktiv (nur links)"</string>
     <string name="bluetooth_hearing_aid_right_active" msgid="2244728507170385397">"Aktiv (nur rechts)"</string>
     <string name="bluetooth_hearing_aid_left_and_right_active" msgid="4294571497939983181">"Aktiv (links und rechts)"</string>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"Status der Umgebungsgeräusche konnte nicht aktualisiert werden"</string>
     <string name="bluetooth_active_media_only_battery_level" msgid="7772517511061834073">"Aktiv (nur Medien). Akku: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
     <string name="bluetooth_active_media_only_battery_level_untethered" msgid="7444753133664620926">"Aktiv (nur Medien). Akku links: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, Akku rechts: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>."</string>
     <string name="bluetooth_battery_level_lea_support" msgid="5968584103507988820">"Verbunden (unterstützt Audiofreigabe). Akku: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index 4662c3c..ab75a9b 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -110,8 +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>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"Δεν ήταν δυνατή η ενημέρωση των ήχων περιβάλλοντος"</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>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index b281f4c..7573543 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -110,8 +110,7 @@
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Active (left only)"</string>
     <string name="bluetooth_hearing_aid_right_active" msgid="2244728507170385397">"Active (right only)"</string>
     <string name="bluetooth_hearing_aid_left_and_right_active" msgid="4294571497939983181">"Active (left and right)"</string>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"Couldn\'t update surroundings"</string>
     <string name="bluetooth_active_media_only_battery_level" msgid="7772517511061834073">"Active (media only). <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> battery."</string>
     <string name="bluetooth_active_media_only_battery_level_untethered" msgid="7444753133664620926">"Active (media only). 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> battery."</string>
     <string name="bluetooth_battery_level_lea_support" msgid="5968584103507988820">"Connected (supports audio sharing). <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> battery."</string>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index b281f4c..7573543 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -110,8 +110,7 @@
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Active (left only)"</string>
     <string name="bluetooth_hearing_aid_right_active" msgid="2244728507170385397">"Active (right only)"</string>
     <string name="bluetooth_hearing_aid_left_and_right_active" msgid="4294571497939983181">"Active (left and right)"</string>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"Couldn\'t update surroundings"</string>
     <string name="bluetooth_active_media_only_battery_level" msgid="7772517511061834073">"Active (media only). <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> battery."</string>
     <string name="bluetooth_active_media_only_battery_level_untethered" msgid="7444753133664620926">"Active (media only). 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> battery."</string>
     <string name="bluetooth_battery_level_lea_support" msgid="5968584103507988820">"Connected (supports audio sharing). <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> battery."</string>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index b281f4c..7573543 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -110,8 +110,7 @@
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Active (left only)"</string>
     <string name="bluetooth_hearing_aid_right_active" msgid="2244728507170385397">"Active (right only)"</string>
     <string name="bluetooth_hearing_aid_left_and_right_active" msgid="4294571497939983181">"Active (left and right)"</string>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"Couldn\'t update surroundings"</string>
     <string name="bluetooth_active_media_only_battery_level" msgid="7772517511061834073">"Active (media only). <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> battery."</string>
     <string name="bluetooth_active_media_only_battery_level_untethered" msgid="7444753133664620926">"Active (media only). 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> battery."</string>
     <string name="bluetooth_battery_level_lea_support" msgid="5968584103507988820">"Connected (supports audio sharing). <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> battery."</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 6ad6008..ae95613 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -110,8 +110,7 @@
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Activo (solo izquierdo)"</string>
     <string name="bluetooth_hearing_aid_right_active" msgid="2244728507170385397">"Activo (solo derecho)"</string>
     <string name="bluetooth_hearing_aid_left_and_right_active" msgid="4294571497939983181">"Activos (izquierdo y derecho)"</string>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"No se pudo actualizar el sonido envolvente"</string>
     <string name="bluetooth_active_media_only_battery_level" msgid="7772517511061834073">"Activado (solo para contenido multimedia). <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de batería."</string>
     <string name="bluetooth_active_media_only_battery_level_untethered" msgid="7444753133664620926">"Activo (solo para contenido multimedia); I: <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> de batería."</string>
     <string name="bluetooth_battery_level_lea_support" msgid="5968584103507988820">"Conectado (admite el uso compartido de audio); <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de batería."</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index 6019751..eb03083 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -110,8 +110,7 @@
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Activo (solo izquierdo)"</string>
     <string name="bluetooth_hearing_aid_right_active" msgid="2244728507170385397">"Activo (solo derecho)"</string>
     <string name="bluetooth_hearing_aid_left_and_right_active" msgid="4294571497939983181">"Activo (izquierdo y derecho)"</string>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"No se han podido actualizar los alrededores"</string>
     <string name="bluetooth_active_media_only_battery_level" msgid="7772517511061834073">"Activo (solo multimedia). <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de batería."</string>
     <string name="bluetooth_active_media_only_battery_level_untethered" msgid="7444753133664620926">"Activo (solo multimedia). Izquierdo: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> de batería. Derecho: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> de batería."</string>
     <string name="bluetooth_battery_level_lea_support" msgid="5968584103507988820">"Conectado (permite compartir audio). <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de batería."</string>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index 773b1e6..9ed48b1 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -110,8 +110,7 @@
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Aktiivne (ainult vasak)"</string>
     <string name="bluetooth_hearing_aid_right_active" msgid="2244728507170385397">"Aktiivne (ainult parem)"</string>
     <string name="bluetooth_hearing_aid_left_and_right_active" msgid="4294571497939983181">"Aktiivne (vasak ja parem)"</string>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"Ümbritsevate helide seadeid ei saanud värskendada"</string>
     <string name="bluetooth_active_media_only_battery_level" msgid="7772517511061834073">"Aktiivne (ainult meedia). Aku <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
     <string name="bluetooth_active_media_only_battery_level_untethered" msgid="7444753133664620926">"Aktiivne (ainult meedia). Aku: V: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, P: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>."</string>
     <string name="bluetooth_battery_level_lea_support" msgid="5968584103507988820">"Ühendatud (toetab heli jagamist). Aku <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 274b9a1..24dafa9 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -110,8 +110,7 @@
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Aktibo (ezkerrekoa soilik)"</string>
     <string name="bluetooth_hearing_aid_right_active" msgid="2244728507170385397">"Aktibo (eskuinekoa soilik)"</string>
     <string name="bluetooth_hearing_aid_left_and_right_active" msgid="4294571497939983181">"Aktibo (ezkerrekoa eta eskuinekoa)"</string>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"Ezin izan da eguneratu ingurunea"</string>
     <string name="bluetooth_active_media_only_battery_level" msgid="7772517511061834073">"Aktibo (multimedia-edukia soilik). Bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
     <string name="bluetooth_active_media_only_battery_level_untethered" msgid="7444753133664620926">"Aktibo (multimedia-edukia soilik). L aldearen bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>. R aldearen bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>."</string>
     <string name="bluetooth_battery_level_lea_support" msgid="5968584103507988820">"Konektatuta (audioa partekatzeko eginbidea onartzen du). Bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 6154b8c..60121cc 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -110,8 +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>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"پیرامون به‌روز نشد"</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>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 7baf2a0..d0d9037 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -110,8 +110,7 @@
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Aktiivinen (vain vasen)"</string>
     <string name="bluetooth_hearing_aid_right_active" msgid="2244728507170385397">"Aktiivinen (vain oikea)"</string>
     <string name="bluetooth_hearing_aid_left_and_right_active" msgid="4294571497939983181">"Aktiivinen (vasen ja oikea)"</string>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"Ympäristön päivittäminen epäonnistui"</string>
     <string name="bluetooth_active_media_only_battery_level" msgid="7772517511061834073">"Aktiivinen (vain media). <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> virtaa."</string>
     <string name="bluetooth_active_media_only_battery_level_untethered" msgid="7444753133664620926">"Aktiivinen (vain media). V: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, O: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> virtaa."</string>
     <string name="bluetooth_battery_level_lea_support" msgid="5968584103507988820">"Yhdistetty (tukee audionjakoa). <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> virtaa."</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index 699fa7f..27e4906 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -110,8 +110,7 @@
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Actif (gauche seulement)"</string>
     <string name="bluetooth_hearing_aid_right_active" msgid="2244728507170385397">"Actif (droite seulement)"</string>
     <string name="bluetooth_hearing_aid_left_and_right_active" msgid="4294571497939983181">"Actif (gauche et droite)"</string>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"Impossible de mettre à jour les sons de l\'environnement"</string>
     <string name="bluetooth_active_media_only_battery_level" msgid="7772517511061834073">"Actif (contenu multimédia uniquement). Pile à <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
     <string name="bluetooth_active_media_only_battery_level_untethered" msgid="7444753133664620926">"Actif (contenu multimédia uniquement). G. : pile à <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, D. : pile à <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>."</string>
     <string name="bluetooth_battery_level_lea_support" msgid="5968584103507988820">"Connecté (prise en charge du partage audio). Pile à <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index 73dbdbf..87d768a 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -110,8 +110,7 @@
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Actif (gauche uniquement)"</string>
     <string name="bluetooth_hearing_aid_right_active" msgid="2244728507170385397">"Actif (droit uniquement)"</string>
     <string name="bluetooth_hearing_aid_left_and_right_active" msgid="4294571497939983181">"Actifs (gauche et droit)"</string>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"Impossible de mettre à jour le mode Sons environnants"</string>
     <string name="bluetooth_active_media_only_battery_level" msgid="7772517511061834073">"Actif (multimédia uniquement). <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de batterie."</string>
     <string name="bluetooth_active_media_only_battery_level_untethered" msgid="7444753133664620926">"Actif (multimédia uniquement). Gauche : <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> de batterie, droit : <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> de batterie."</string>
     <string name="bluetooth_battery_level_lea_support" msgid="5968584103507988820">"Connecté (compatible avec le partage audio). <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de batterie."</string>
@@ -256,8 +255,8 @@
     <string name="adb_wireless_error" msgid="721958772149779856">"Erreur"</string>
     <string name="adb_wireless_settings" msgid="2295017847215680229">"Débogage sans fil"</string>
     <string name="adb_wireless_list_empty_off" msgid="1713707973837255490">"Pour afficher et utiliser les appareils disponibles, activez le débogage sans fil"</string>
-    <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"Associer l\'appareil avec un code QR"</string>
-    <string name="adb_pair_method_qrcode_summary" msgid="7130694277228970888">"Associer les nouveaux appareils à l\'aide d\'un lecteur de code QR"</string>
+    <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"Associer l\'appareil avec un QR code"</string>
+    <string name="adb_pair_method_qrcode_summary" msgid="7130694277228970888">"Associer les nouveaux appareils à l\'aide d\'un lecteur de QR code"</string>
     <string name="adb_pair_method_code_title" msgid="1122590300445142904">"Associer l\'appareil avec un code d\'association"</string>
     <string name="adb_pair_method_code_summary" msgid="6370414511333685185">"Associer les nouveaux appareils à l\'aide d\'un code à six chiffres"</string>
     <string name="adb_paired_devices_title" msgid="5268997341526217362">"Appareils associés"</string>
@@ -271,12 +270,12 @@
     <string name="adb_pairing_device_dialog_pairing_code_label" msgid="3639239786669722731">"Code d\'association via le Wi-Fi"</string>
     <string name="adb_pairing_device_dialog_failed_title" msgid="3426758947882091735">"Échec de l\'association"</string>
     <string name="adb_pairing_device_dialog_failed_msg" msgid="6611097519661997148">"Assurez-vous que l\'appareil est connecté au même réseau."</string>
-    <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"Associer l\'appareil via le Wi‑Fi à l\'aide d\'un code QR"</string>
+    <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"Associer l\'appareil via le Wi‑Fi à l\'aide d\'un QR code"</string>
     <string name="adb_wireless_verifying_qrcode_text" msgid="6123192424916029207">"Association de l\'appareil…"</string>
-    <string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"Échec de l\'association à l\'appareil. Le code QR est incorrect, ou l\'appareil n\'est pas connecté au même réseau."</string>
+    <string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"Échec de l\'association à l\'appareil. Le QR code est incorrect, ou l\'appareil n\'est pas connecté au même réseau."</string>
     <string name="adb_wireless_ip_addr_preference_title" msgid="8335132107715311730">"Adresse IP et port"</string>
-    <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"Scanner un code QR"</string>
-    <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"Associez l\'appareil via le Wi‑Fi à l\'aide d\'un code QR"</string>
+    <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"Scanner un QR code"</string>
+    <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"Associez l\'appareil via le Wi‑Fi à l\'aide d\'un QR code"</string>
     <string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"Connectez-vous à un réseau Wi-Fi"</string>
     <string name="keywords_adb_wireless" msgid="6507505581882171240">"adb, débogage, dev"</string>
     <string name="bugreport_in_power" msgid="8664089072534638709">"Raccourci vers rapport de bug"</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index 9f14f7c..4e90b53 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -110,8 +110,7 @@
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Activo (só o esquerdo)"</string>
     <string name="bluetooth_hearing_aid_right_active" msgid="2244728507170385397">"Activo (só o dereito)"</string>
     <string name="bluetooth_hearing_aid_left_and_right_active" msgid="4294571497939983181">"Activos (o esquerdo e o dereito)"</string>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"Non se puido actualizar o ambiente"</string>
     <string name="bluetooth_active_media_only_battery_level" msgid="7772517511061834073">"Activo (só contido multimedia). <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de batería."</string>
     <string name="bluetooth_active_media_only_battery_level_untethered" msgid="7444753133664620926">"Activo (só contido multimedia). Esquerdo: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> de batería. Dereito: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> de batería."</string>
     <string name="bluetooth_battery_level_lea_support" msgid="5968584103507988820">"Conectado (compatible con audio compartido). <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de batería."</string>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index 74b19f6..bfe68fe 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -110,8 +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>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"આસપાસના અવાજો અપડેટ કરી શક્યા નથી"</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>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 8f9229a..5146ebc 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -110,8 +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>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"वॉल्यूम को मैनेज करने की सेटिंग नहीं बदली जा सकी"</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>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index 3a18da0..4c743a9 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -110,8 +110,7 @@
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Aktivno (samo lijevo)"</string>
     <string name="bluetooth_hearing_aid_right_active" msgid="2244728507170385397">"Aktivno (samo desno)"</string>
     <string name="bluetooth_hearing_aid_left_and_right_active" msgid="4294571497939983181">"Aktivno (lijevo i desno)"</string>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"Ažuriranje okruženja nije uspjelo"</string>
     <string name="bluetooth_active_media_only_battery_level" msgid="7772517511061834073">"Aktivno (samo medijski sadržaji). <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> baterije."</string>
     <string name="bluetooth_active_media_only_battery_level_untethered" msgid="7444753133664620926">"Aktivno (samo medijski sadržaji), 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> baterije."</string>
     <string name="bluetooth_battery_level_lea_support" msgid="5968584103507988820">"Povezano (podržava zajedničko slušanje). <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> baterije."</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index 00ca5c6..b17dfff 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -110,8 +110,7 @@
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Aktív (csak bal)"</string>
     <string name="bluetooth_hearing_aid_right_active" msgid="2244728507170385397">"Aktív (csak jobb)"</string>
     <string name="bluetooth_hearing_aid_left_and_right_active" msgid="4294571497939983181">"Aktív (bal és jobb)"</string>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"Nem sikerült módosítani a környezetet"</string>
     <string name="bluetooth_active_media_only_battery_level" msgid="7772517511061834073">"Aktív (csak médiatartalom lejátszása esetén). Akkumulátor töltöttségi szintje: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
     <string name="bluetooth_active_media_only_battery_level_untethered" msgid="7444753133664620926">"Aktív (csak médiatartalom lejátszása esetén). Akkumulátorok töltöttségi szintje: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> (bal) és <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> (jobb)."</string>
     <string name="bluetooth_battery_level_lea_support" msgid="5968584103507988820">"Csatlakoztatva (támogatja a hang megosztását). Akkumulátor töltöttségi szintje: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index 58eca5c..abca57c 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -110,8 +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>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"Չհաջողվեց թարմացնել շրջակայքի կարգավիճակը"</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>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index 1652ebf..784a758 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -110,8 +110,7 @@
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Aktif (hanya kiri)"</string>
     <string name="bluetooth_hearing_aid_right_active" msgid="2244728507170385397">"Aktif (hanya kanan)"</string>
     <string name="bluetooth_hearing_aid_left_and_right_active" msgid="4294571497939983181">"Aktif (kiri dan kanan)"</string>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"Tidak dapat memperbarui suara sekitar"</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>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index bc766325..88f657a 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -110,8 +110,7 @@
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Kveikt (eingöngu vinstra)"</string>
     <string name="bluetooth_hearing_aid_right_active" msgid="2244728507170385397">"Kveikt (eingöngu hægra)"</string>
     <string name="bluetooth_hearing_aid_left_and_right_active" msgid="4294571497939983181">"Kveikt (vinstra og hægra)"</string>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"Ekki var hægt að uppfæra umhverfi"</string>
     <string name="bluetooth_active_media_only_battery_level" msgid="7772517511061834073">"Virkt (eingöngu margmiðlunarefni). <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> rafhlöðuhleðsla."</string>
     <string name="bluetooth_active_media_only_battery_level_untethered" msgid="7444753133664620926">"Virkt (eingöngu margmiðlunarefni), V: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, H: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> rafhlöðuhleðsla."</string>
     <string name="bluetooth_battery_level_lea_support" msgid="5968584103507988820">"Tengt (styður hljóðdeilingu), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> rafhlöðuhleðsla."</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index f51c916..0c71a219 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -110,8 +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>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"לא ניתן לעדכן את עוצמת הרעשים בסביבה"</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>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index 923054d..10f1427 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -110,8 +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>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"周囲の音を更新できませんでした"</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>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index e8f1b07..4473216 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -110,8 +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>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"გარემოცვის განახლება ვერ მოხერხდა"</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>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index a88517d..a0a91c8 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -110,8 +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>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"Айналаны жаңарту мүмкін болмады."</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>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index 41b427a..bee7772 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -110,8 +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>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"មិនអាចប្ដូរមជ្ឈដ្ឋានជុំវិញបានទេ"</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>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index 20b4c93..f11dff5 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -110,8 +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>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"ಆ್ಯಂಬಿಯೆಂಟ್ ಸ್ಥಿತಿಯನ್ನು ಅಪ್‌ಡೇಟ್ ಮಾಡಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ"</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>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index 084ab17..672ad8c 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -110,8 +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>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"주변 소리를 업데이트할 수 없음"</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>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index 2d1ea867..a9b4a1e 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -110,8 +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>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"Айланадагы абал жаңыртылган жок"</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>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index ec06e5e..cc8e42c 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -110,8 +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>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"ບໍ່ສາມາດອັບເດດສຽງແວດລ້ອມໄດ້"</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>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index f0a82e0..f81d527 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -110,8 +110,7 @@
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Aktyvus (tik kairiojoje pusėje)"</string>
     <string name="bluetooth_hearing_aid_right_active" msgid="2244728507170385397">"Aktyvus (tik dešiniojoje pusėje)"</string>
     <string name="bluetooth_hearing_aid_left_and_right_active" msgid="4294571497939983181">"Aktyvus (kairiojoje ir dešiniojoje pusėse)"</string>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"Nepavyko atnaujinti aplinkos"</string>
     <string name="bluetooth_active_media_only_battery_level" msgid="7772517511061834073">"Aktyvus (tik medija). Akumuliatorius lygis: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
     <string name="bluetooth_active_media_only_battery_level_untethered" msgid="7444753133664620926">"Aktyvus (tik medija), akumuliatoriaus lygis kairėje: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, dešinėje: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>."</string>
     <string name="bluetooth_battery_level_lea_support" msgid="5968584103507988820">"Prijungta (palaikomas garso įrašų bendrinimas). Akumuliatoriaus lygis: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index 47fe2d9..2738a40 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -110,8 +110,7 @@
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Ierīce aktīva (tikai kreisā auss)"</string>
     <string name="bluetooth_hearing_aid_right_active" msgid="2244728507170385397">"Ierīce aktīva (tikai labā auss)"</string>
     <string name="bluetooth_hearing_aid_left_and_right_active" msgid="4294571497939983181">"Ierīces aktīvas (kreisā un labā auss)"</string>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"Nevarēja atjaunināt apkārtnes skaņas"</string>
     <string name="bluetooth_active_media_only_battery_level" msgid="7772517511061834073">"Aktīvs (tikai multividei). Akumulatora uzlādes līmenis: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
     <string name="bluetooth_active_media_only_battery_level_untethered" msgid="7444753133664620926">"Aktīvs (tikai multividei). Akumulatora uzlādes līmenis kreisajā austiņā: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, labajā austiņā: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>."</string>
     <string name="bluetooth_battery_level_lea_support" msgid="5968584103507988820">"Izveidots savienojums (atbalsta audio kopīgošanu). Akumulatora uzlādes līmenis: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index a8c8d83..9ec3bac 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -110,8 +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>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"Не можеше да се ажурира опкружувањето"</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>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index 859c67e..fd8861f 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -110,8 +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>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"സറൗണ്ടിംഗ്‌സ് അപ്ഡേറ്റ് ചെയ്യാനായില്ല"</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>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index c61bcde..08dfa7d 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -110,8 +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>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"Орчин тойрныг шинэчилж чадсангүй"</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>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index 654b447..3f88af6 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -110,8 +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>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"जवळपासचे आवाज अपडेट करता आले नाहीत"</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>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index fcb5d6e..97a7b43 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -110,8 +110,7 @@
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Aktif (kiri sahaja)"</string>
     <string name="bluetooth_hearing_aid_right_active" msgid="2244728507170385397">"Aktif (kanan sahaja)"</string>
     <string name="bluetooth_hearing_aid_left_and_right_active" msgid="4294571497939983181">"Aktif (kiri dan kanan)"</string>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"Tidak dapat mengemaskinikan persekitaran"</string>
     <string name="bluetooth_active_media_only_battery_level" msgid="7772517511061834073">"Aktif (media sahaja). Bateri <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
     <string name="bluetooth_active_media_only_battery_level_untethered" msgid="7444753133664620926">"Aktif (media sahaja), L: Bateri <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, R: Bateri <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>."</string>
     <string name="bluetooth_battery_level_lea_support" msgid="5968584103507988820">"Disambungkan (menyokong perkongsian audio). Bateri <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index 4826c49..9525884 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -110,8 +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>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"ဝန်းကျင်အသံ အပ်ဒိတ်လုပ်၍ မရလိုက်ပါ"</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>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index e7be3c9..94a701b 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -110,8 +110,7 @@
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Aktiv (bare venstre)"</string>
     <string name="bluetooth_hearing_aid_right_active" msgid="2244728507170385397">"Aktiv (bare høyre)"</string>
     <string name="bluetooth_hearing_aid_left_and_right_active" msgid="4294571497939983181">"Aktiv (venstre og høyre)"</string>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"Kunne ikke oppdatere omgivelsene"</string>
     <string name="bluetooth_active_media_only_battery_level" msgid="7772517511061834073">"Aktiv (bare medieinnhold) <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> batteri."</string>
     <string name="bluetooth_active_media_only_battery_level_untethered" msgid="7444753133664620926">"Aktiv (bare medieinnhold). V: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> batteri, H: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> batteri."</string>
     <string name="bluetooth_battery_level_lea_support" msgid="5968584103507988820">"Tilkoblet (støtter lyddeling). <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> batteri."</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index 0478d9c..a2ee640 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -110,8 +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>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"वरपरका आवाजसम्बन्धी सेटिङ अपडेट गर्न सकिएन"</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>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index bae7bb9..63014b6 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -110,8 +110,7 @@
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Actief (alleen links)"</string>
     <string name="bluetooth_hearing_aid_right_active" msgid="2244728507170385397">"Actief (alleen rechts)"</string>
     <string name="bluetooth_hearing_aid_left_and_right_active" msgid="4294571497939983181">"Actief (links en rechts)"</string>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"Kan omgeving niet updaten"</string>
     <string name="bluetooth_active_media_only_battery_level" msgid="7772517511061834073">"Actief (alleen media). Batterijniveau <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
     <string name="bluetooth_active_media_only_battery_level_untethered" msgid="7444753133664620926">"Actief (alleen media), L: batterijniveau <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, R: batterijniveau <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>."</string>
     <string name="bluetooth_battery_level_lea_support" msgid="5968584103507988820">"Verbonden (ondersteunt audio delen), batterijniveau <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index 7b8f3cc..803309c 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -110,8 +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>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"ପରିପାର୍ଶ୍ୱକୁ ଅପଡେଟ କରାଯାଇପାରିଲା ନାହିଁ"</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>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index 7e2a50f..d8726db 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -110,8 +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>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"ਆਲੇ-ਦੁਆਲੇ ਨੂੰ ਅੱਪਡੇਟ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ"</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>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index 397cb82..d789c64 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -110,8 +110,7 @@
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Aktywne (tylko lewa strona)"</string>
     <string name="bluetooth_hearing_aid_right_active" msgid="2244728507170385397">"Aktywne (tylko prawa strona)"</string>
     <string name="bluetooth_hearing_aid_left_and_right_active" msgid="4294571497939983181">"Aktywne (lewa i prawa strona)"</string>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"Nie udało się zaktualizować otoczenia"</string>
     <string name="bluetooth_active_media_only_battery_level" msgid="7772517511061834073">"Aktywne (tylko multimedia). <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> naładowania baterii."</string>
     <string name="bluetooth_active_media_only_battery_level_untethered" msgid="7444753133664620926">"Aktywne (tylko multimedia), lewa: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, prawa: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> naładowania baterii."</string>
     <string name="bluetooth_battery_level_lea_support" msgid="5968584103507988820">"Połączone (obsługa udostępniania dźwięku), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> naładowania baterii."</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index 273c84d..bd08415 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -110,8 +110,7 @@
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Ativo (apenas o esquerdo)"</string>
     <string name="bluetooth_hearing_aid_right_active" msgid="2244728507170385397">"Ativo (apenas o direito)"</string>
     <string name="bluetooth_hearing_aid_left_and_right_active" msgid="4294571497939983181">"Ativo (esquerdo e direito)"</string>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"Não foi possível atualizar o som ambiente"</string>
     <string name="bluetooth_active_media_only_battery_level" msgid="7772517511061834073">"Ativo (apenas mídia). <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de bateria."</string>
     <string name="bluetooth_active_media_only_battery_level_untethered" msgid="7444753133664620926">"Ativo (apenas mídia). Lado esquerdo: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> de bateria. Lado direito: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> de bateria."</string>
     <string name="bluetooth_battery_level_lea_support" msgid="5968584103507988820">"Conectado (aceita compartilhamento de áudio). <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de bateria."</string>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index 273c84d..bd08415 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -110,8 +110,7 @@
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Ativo (apenas o esquerdo)"</string>
     <string name="bluetooth_hearing_aid_right_active" msgid="2244728507170385397">"Ativo (apenas o direito)"</string>
     <string name="bluetooth_hearing_aid_left_and_right_active" msgid="4294571497939983181">"Ativo (esquerdo e direito)"</string>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"Não foi possível atualizar o som ambiente"</string>
     <string name="bluetooth_active_media_only_battery_level" msgid="7772517511061834073">"Ativo (apenas mídia). <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de bateria."</string>
     <string name="bluetooth_active_media_only_battery_level_untethered" msgid="7444753133664620926">"Ativo (apenas mídia). Lado esquerdo: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> de bateria. Lado direito: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> de bateria."</string>
     <string name="bluetooth_battery_level_lea_support" msgid="5968584103507988820">"Conectado (aceita compartilhamento de áudio). <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> de bateria."</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 1d77583..1b66145 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -110,8 +110,7 @@
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Activ (numai stânga)"</string>
     <string name="bluetooth_hearing_aid_right_active" msgid="2244728507170385397">"Activ (numai dreapta)"</string>
     <string name="bluetooth_hearing_aid_left_and_right_active" msgid="4294571497939983181">"Activ (stânga și dreapta)"</string>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"Nu s-a putut actualiza zona din jur"</string>
     <string name="bluetooth_active_media_only_battery_level" msgid="7772517511061834073">"Activ (numai pentru conținut media). Nivelul bateriei <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
     <string name="bluetooth_active_media_only_battery_level_untethered" msgid="7444753133664620926">"Activ (numai pentru conținut media): nivelul bateriei din stânga: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, nivelul bateriei din dreapta: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g>"</string>
     <string name="bluetooth_battery_level_lea_support" msgid="5968584103507988820">"Conectat (acceptă permiterea accesului la audio). Nivelul bateriei: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index d697780..56748fb 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -110,8 +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>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"Не удалось отрегулировать окружающие звуки."</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>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index df22ba7..5030450 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -110,8 +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>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"වටපිටාව යාවත්කාලීන කළ නොහැකි විය"</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>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index 7af3825..b4787f2 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -110,8 +110,7 @@
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Aktívne (iba ľavé)"</string>
     <string name="bluetooth_hearing_aid_right_active" msgid="2244728507170385397">"Aktívne (iba pravé)"</string>
     <string name="bluetooth_hearing_aid_left_and_right_active" msgid="4294571497939983181">"Aktívne (ľavé aj pravé)"</string>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"Okolie sa nepodarilo aktualizovať"</string>
     <string name="bluetooth_active_media_only_battery_level" msgid="7772517511061834073">"Aktívne (iba médiá). <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> batérie."</string>
     <string name="bluetooth_active_media_only_battery_level_untethered" msgid="7444753133664620926">"Aktívne (iba médiá). Ľ: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> batérie, P: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> batérie."</string>
     <string name="bluetooth_battery_level_lea_support" msgid="5968584103507988820">"Pripojené (podporuje zdieľanie zvuku). <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> batérie."</string>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index e82feb4..b6eacf8 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -110,8 +110,7 @@
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Aktivno (samo levo)"</string>
     <string name="bluetooth_hearing_aid_right_active" msgid="2244728507170385397">"Aktivno (samo desno)"</string>
     <string name="bluetooth_hearing_aid_left_and_right_active" msgid="4294571497939983181">"Aktivno (levo in desno)"</string>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"Okolice ni bilo mogoče posodobiti"</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>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index f857309..ee31c29 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -110,8 +110,7 @@
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Aktive (vetëm majtas)"</string>
     <string name="bluetooth_hearing_aid_right_active" msgid="2244728507170385397">"Aktive (vetëm djathtas)"</string>
     <string name="bluetooth_hearing_aid_left_and_right_active" msgid="4294571497939983181">"Aktive (majtas dhe djathtas)"</string>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"Ambienti rrethues nuk mund të përditësohej"</string>
     <string name="bluetooth_active_media_only_battery_level" msgid="7772517511061834073">"Aktiv (vetëm për media). <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> bateri."</string>
     <string name="bluetooth_active_media_only_battery_level_untethered" msgid="7444753133664620926">"Aktiv (vetëm për media). Majtas: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> bateri, djathtas: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> bateri."</string>
     <string name="bluetooth_battery_level_lea_support" msgid="5968584103507988820">"Lidhur (mbështet ndarjen e audios). <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> bateri."</string>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index ad15dd4..fcf1e4f 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -110,8 +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>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"Ажурирање окружења није успело"</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>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index 7f172db..c66242b 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -110,8 +110,7 @@
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Aktiv (endast vänster)"</string>
     <string name="bluetooth_hearing_aid_right_active" msgid="2244728507170385397">"Aktiv (endast höger)"</string>
     <string name="bluetooth_hearing_aid_left_and_right_active" msgid="4294571497939983181">"Aktiv (vänster och höger)"</string>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"Det gick inte att uppdatera omgivningsläget"</string>
     <string name="bluetooth_active_media_only_battery_level" msgid="7772517511061834073">"Aktiv (endast media). <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> batteri."</string>
     <string name="bluetooth_active_media_only_battery_level_untethered" msgid="7444753133664620926">"Aktiv (endast media). V: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g>, H: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> batteri."</string>
     <string name="bluetooth_battery_level_lea_support" msgid="5968584103507988820">"Ansluten (ljuddelning stöds). <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> batteri."</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index bf95f4e..d84d355 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -110,8 +110,7 @@
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Inatumika (kushoto pekee)"</string>
     <string name="bluetooth_hearing_aid_right_active" msgid="2244728507170385397">"Inatumika (kulia pekee)"</string>
     <string name="bluetooth_hearing_aid_left_and_right_active" msgid="4294571497939983181">"Inatumika (kushoto na kulia)"</string>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"Imeshindwa kusasisha mazingira"</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 imefika <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index 24c2244..b0c25d0 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -110,8 +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>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"சுற்றுப்புறங்களைப் புதுப்பிக்க முடியவில்லை"</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>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index abee836..aae20b2 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -110,8 +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>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"పరిసరాలను అప్‌డేట్ చేయడం సాధ్యం కాలేదు"</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>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index f8f6af2..a228e6b 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -110,8 +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>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"อัปเดตเสียงแวดล้อมไม่ได้"</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>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index 8536219..6553479 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -110,8 +110,7 @@
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Aktibo (kaliwa lang)"</string>
     <string name="bluetooth_hearing_aid_right_active" msgid="2244728507170385397">"Aktibo (kanan lang)"</string>
     <string name="bluetooth_hearing_aid_left_and_right_active" msgid="4294571497939983181">"Aktibo (kaliwa at kanan)"</string>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"Hindi ma-update ang paligid"</string>
     <string name="bluetooth_active_media_only_battery_level" msgid="7772517511061834073">"Aktibo (media lang). <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> baterya."</string>
     <string name="bluetooth_active_media_only_battery_level_untethered" msgid="7444753133664620926">"Aktibo (media lang). 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> baterya."</string>
     <string name="bluetooth_battery_level_lea_support" msgid="5968584103507988820">"Nakakonekta (sinusuportahan ang pag-share ng audio), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> baterya."</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index 8168d53..6a8158b 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -110,8 +110,7 @@
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Etkin (yalnızca sol taraf)"</string>
     <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>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"Çevredeki sesler güncellenemedi"</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>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index 3167290..d599fb4 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -110,8 +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>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"Не вдалось оновити стан оточення"</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>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index fe3dc05..f0588ef 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -110,8 +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>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"اطراف کو اپ ڈیٹ نہیں کیا جا سکا"</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>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index d046eff..6930278 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -110,8 +110,7 @@
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Faol (faqat chap)"</string>
     <string name="bluetooth_hearing_aid_right_active" msgid="2244728507170385397">"Faol (faqat oʻng)"</string>
     <string name="bluetooth_hearing_aid_left_and_right_active" msgid="4294571497939983181">"Faol (chap va oʻng)"</string>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"Atrof-muhit yangilanmadi"</string>
     <string name="bluetooth_active_media_only_battery_level" msgid="7772517511061834073">"Faol (faqat media uchun) Quvvat: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>."</string>
     <string name="bluetooth_active_media_only_battery_level_untethered" msgid="7444753133664620926">"Faol (faqat media uchun), quvvat: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> (L), <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> (R)"</string>
     <string name="bluetooth_battery_level_lea_support" msgid="5968584103507988820">"Ulangan (audio yuborish mumkin), quvvat: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index b9617f7..ffdd479 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -110,8 +110,7 @@
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Đang hoạt động (chỉ tai trái)"</string>
     <string name="bluetooth_hearing_aid_right_active" msgid="2244728507170385397">"Đang hoạt động (chỉ tai phải)"</string>
     <string name="bluetooth_hearing_aid_left_and_right_active" msgid="4294571497939983181">"Đang hoạt động (cả tai phải và tai trái)"</string>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"Không cập nhật được âm lượng xung quanh"</string>
     <string name="bluetooth_active_media_only_battery_level" msgid="7772517511061834073">"Đang hoạt động (chỉ phát nội dung đa phương tiện). Còn <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> pin."</string>
     <string name="bluetooth_active_media_only_battery_level_untethered" msgid="7444753133664620926">"Đang hoạt động (chỉ phát nội dung đa phương tiện). Bên trái: Còn <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_0">%1$s</xliff:g> pin. Bên phải: Còn <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE_1">%2$s</xliff:g> pin."</string>
     <string name="bluetooth_battery_level_lea_support" msgid="5968584103507988820">"Đã kết nối (có hỗ trợ tính năng chia sẻ âm thanh). Còn <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> pin."</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 905b221..3b42efd 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -110,8 +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>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"无法更新周围声音"</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>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index ccac068..a6de0d9 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -110,8 +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>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"無法更新環境聲音"</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>
diff --git a/packages/SettingsLib/res/values-zh-rTW/arrays.xml b/packages/SettingsLib/res/values-zh-rTW/arrays.xml
index b851f46..c4d820c 100644
--- a/packages/SettingsLib/res/values-zh-rTW/arrays.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/arrays.xml
@@ -296,7 +296,7 @@
   <string-array name="shade_display_awareness_summaries">
     <item msgid="2964753205732912921">"只在裝置螢幕顯示通知欄"</item>
     <item msgid="7795034287069726554">"在單一外接螢幕顯示通知欄"</item>
-    <item msgid="5280431949814340475">"在上一次使用的螢幕顯示通知欄"</item>
+    <item msgid="5280431949814340475">"在最新使用的螢幕顯示通知欄"</item>
   </string-array>
   <string-array name="shade_display_awareness_values">
     <item msgid="3055776101992426514">"預設螢幕"</item>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index 3bd3564..e4d95e8 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -110,8 +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>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"無法更新環境狀態"</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>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index 7d96423..1f9eca4 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -110,8 +110,7 @@
     <string name="bluetooth_hearing_aid_left_active" msgid="8330226430756799572">"Iyasebenza (ngakwesokunxele kuphela)"</string>
     <string name="bluetooth_hearing_aid_right_active" msgid="2244728507170385397">"Iyasebenza (ngakwesokudla kuphela)"</string>
     <string name="bluetooth_hearing_aid_left_and_right_active" msgid="4294571497939983181">"Iyasebenza (ngakwesokunxele nakwesokudla)"</string>
-    <!-- no translation found for bluetooth_hearing_device_ambient_error (6035857289108813878) -->
-    <skip />
+    <string name="bluetooth_hearing_device_ambient_error" msgid="6035857289108813878">"Ayikwazanga ukubuyekeza izindawo ezizungezile"</string>
     <string name="bluetooth_active_media_only_battery_level" msgid="7772517511061834073">"Iyasebenza (imidiya kuphela). <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> ibhethri."</string>
     <string name="bluetooth_active_media_only_battery_level_untethered" msgid="7444753133664620926">"Iyasebenza (imidiya kuphela). 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> ibhethri."</string>
     <string name="bluetooth_battery_level_lea_support" msgid="5968584103507988820">"Ixhunyiwe (isekela ukwabelana ngokuqoshiwe). <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g> ibhethri."</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
index 3c36c44..68e9fe7 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java
@@ -73,6 +73,13 @@
     private static final Set<Integer> SA_PROFILES =
             ImmutableSet.of(
                     BluetoothProfile.A2DP, BluetoothProfile.LE_AUDIO, BluetoothProfile.HEARING_AID);
+    private static final List<Integer> BLUETOOTH_DEVICE_CLASS_HEADSET =
+            List.of(
+                    BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES,
+                    BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET);
+
+    private static final String TEMP_BOND_TYPE = "TEMP_BOND_TYPE";
+    private static final String TEMP_BOND_DEVICE_METADATA_VALUE = "le_audio_sharing";
 
     private static ErrorListener sErrorListener;
 
@@ -387,6 +394,19 @@
         return false;
     }
 
+    /** Checks whether the bluetooth device is a headset. */
+    public static boolean isHeadset(@NonNull BluetoothDevice bluetoothDevice) {
+        String deviceType =
+                BluetoothUtils.getStringMetaData(
+                        bluetoothDevice, BluetoothDevice.METADATA_DEVICE_TYPE);
+        if (!TextUtils.isEmpty(deviceType)) {
+            return BluetoothDevice.DEVICE_TYPE_HEADSET.equals(deviceType)
+                    || BluetoothDevice.DEVICE_TYPE_UNTETHERED_HEADSET.equals(deviceType);
+        }
+        BluetoothClass btClass = bluetoothDevice.getBluetoothClass();
+        return btClass != null && BLUETOOTH_DEVICE_CLASS_HEADSET.contains(btClass.getDeviceClass());
+    }
+
     /** Create an Icon pointing to a drawable. */
     public static IconCompat createIconWithDrawable(Drawable drawable) {
         Bitmap bitmap;
@@ -1138,4 +1158,15 @@
         }
         return saDevice;
     }
+
+    /**
+     * Verifies if the device is temporary bond in audio sharing.
+     *
+     * @param bluetoothDevice the BluetoothDevice to verify
+     * @return if the device is temporary bond
+     */
+    public static boolean isTemporaryBondDevice(@Nullable BluetoothDevice bluetoothDevice) {
+        String metadataValue = getFastPairCustomizedField(bluetoothDevice, TEMP_BOND_TYPE);
+        return Objects.equals(metadataValue, TEMP_BOND_DEVICE_METADATA_VALUE);
+    }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index b4afb7d..7374f80 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -444,12 +444,23 @@
     }
 
     /**
-     * @return {@code true} if {@code cachedBluetoothDevice} is hearing aid device
+     * @return {@code true} if {@code cachedBluetoothDevice} is hearing aid device.
+     * @deprecated use {@link #isHearingDevice() }
+     * // TODO: b/385679160 - Target to deprecate it and replace  with #isHearingDevice()
      */
+    @Deprecated
     public boolean isHearingAidDevice() {
         return mHearingAidInfo != null;
     }
 
+    /**
+     * @return {@code true} if {@code cachedBluetoothDevice} support any of hearing device profile.
+     */
+    public boolean isHearingDevice() {
+        return getProfiles().stream().anyMatch(
+                p -> (p instanceof HearingAidProfile || p instanceof HapClientProfile));
+    }
+
     public int getDeviceSide() {
         return mHearingAidInfo != null
                 ? mHearingAidInfo.getSide() : HearingAidInfo.DeviceSide.SIDE_INVALID;
@@ -910,12 +921,33 @@
         }
     }
 
+    /**
+     * Checks if the device is connected to the specified Bluetooth profile.
+     *
+     * @param profile The Bluetooth profile to check.
+     * @return {@code true} if the device is connected to the profile.
+     */
     public boolean isConnectedProfile(LocalBluetoothProfile profile) {
         int status = getProfileConnectionState(profile);
         return status == BluetoothProfile.STATE_CONNECTED;
 
     }
 
+    /**
+     * Checks if the device is connected to the Bluetooth profile with the given ID.
+     *
+     * @param profileId The ID of the Bluetooth profile to check.
+     * @return {@code true} if the device is connected to the profile.
+     */
+    public boolean isConnectedProfile(int profileId) {
+        for (LocalBluetoothProfile profile : getProfiles()) {
+            if (profile.getProfileId() == profileId) {
+                return isConnectedProfile(profile);
+            }
+        }
+        return false;
+    }
+
     public boolean isBusy() {
         synchronized (mProfileLock) {
             for (LocalBluetoothProfile profile : mProfiles) {
@@ -1891,13 +1923,6 @@
     }
 
     /**
-     * @return {@code true} if {@code cachedBluetoothDevice} is LeAudio hearing aid device
-     */
-    public boolean isConnectedLeAudioHearingAidDevice() {
-        return isConnectedHapClientDevice() && isConnectedLeAudioDevice();
-    }
-
-    /**
      * @return {@code true} if {@code cachedBluetoothDevice} is hearing aid device
      *
      * The device may be an ASHA hearing aid that supports {@link HearingAidProfile} or a LeAudio
@@ -1908,6 +1933,13 @@
     }
 
     /**
+     * @return {@code true} if {@code cachedBluetoothDevice} is LeAudio hearing aid device
+     */
+    public boolean isConnectedLeAudioHearingAidDevice() {
+        return isConnectedHapClientDevice() && isConnectedLeAudioDevice();
+    }
+
+    /**
      * @return {@code true} if {@code cachedBluetoothDevice} is LeAudio device
      */
     public boolean isConnectedLeAudioDevice() {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
index b754706..313013c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceManager.java
@@ -24,7 +24,10 @@
 import android.content.Context;
 import android.util.Log;
 
+import androidx.annotation.NonNull;
+
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.settingslib.flags.Flags;
 
 import java.sql.Timestamp;
 import java.util.ArrayList;
@@ -46,7 +49,7 @@
     private final LocalBluetoothManager mBtManager;
 
     @VisibleForTesting
-    final List<CachedBluetoothDevice> mCachedDevices = new ArrayList<CachedBluetoothDevice>();
+    final List<CachedBluetoothDevice> mCachedDevices = new ArrayList<>();
     @VisibleForTesting
     HearingAidDeviceManager mHearingAidDeviceManager;
     @VisibleForTesting
@@ -192,6 +195,20 @@
     }
 
     /**
+     * Notifies the connection status if device is hearing device.
+     *
+     * @param device The {@link CachedBluetoothDevice} need to be hearing device
+     */
+    public synchronized void notifyHearingDevicesConnectionStatusChangedIfNeeded(
+            @NonNull CachedBluetoothDevice device) {
+        if (!device.isHearingDevice()) {
+            return;
+        }
+
+        mHearingAidDeviceManager.notifyDevicesConnectionStatusChanged();
+    }
+
+    /**
      * Search for existing sub device {@link CachedBluetoothDevice}.
      *
      * @param device the address of the Bluetooth device
@@ -388,8 +405,14 @@
 
     /** Handles when the device been set as active/inactive. */
     public synchronized void onActiveDeviceChanged(CachedBluetoothDevice cachedBluetoothDevice) {
-        if (cachedBluetoothDevice.isHearingAidDevice()) {
+        if (cachedBluetoothDevice == null) {
+            return;
+        }
+        if (cachedBluetoothDevice.isHearingDevice()) {
             mHearingAidDeviceManager.onActiveDeviceChanged(cachedBluetoothDevice);
+            if (Flags.hearingDeviceSetConnectionStatusReport()) {
+                mHearingAidDeviceManager.notifyDevicesConnectionStatusChanged();
+            }
         }
     }
 
@@ -421,6 +444,14 @@
             mainDevice.unpair();
             mainDevice.setSubDevice(null);
         }
+
+        // TODO: b/386121967 - Should change to use isHearingDevice but mProfile get clear here.
+        //  Need to consider where to put this logic when using isHearingDevice()
+        if (device.isHearingAidDevice()) {
+            if (Flags.hearingDeviceSetConnectionStatusReport()) {
+                mHearingAidDeviceManager.notifyDevicesConnectionStatusChanged();
+            }
+        }
     }
 
     /**
@@ -579,6 +610,11 @@
         return mOngoingSetMemberPair != null && mOngoingSetMemberPair.equals(device);
     }
 
+    @NonNull
+    public HearingAidDeviceManager getHearingAidDeviceManager() {
+        return mHearingAidDeviceManager;
+    }
+
     private void log(String msg) {
         if (DEBUG) {
             Log.d(TAG, msg);
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java
index ad34e83..b2c2794 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java
@@ -15,7 +15,11 @@
  */
 package com.android.settingslib.bluetooth;
 
+import static android.bluetooth.BluetoothDevice.BOND_BONDED;
+
+import android.annotation.CallbackExecutor;
 import android.bluetooth.BluetoothCsipSetCoordinator;
+import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothHapClient;
 import android.bluetooth.BluetoothHearingAid;
 import android.bluetooth.BluetoothProfile;
@@ -30,15 +34,25 @@
 import android.util.FeatureFlagUtils;
 import android.util.Log;
 
+import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.collection.ArraySet;
+
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.settingslib.bluetooth.HearingAidAudioRoutingConstants.RoutingValue;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executor;
+import java.util.stream.Collectors;
 
 /**
- * HearingAidDeviceManager manages the set of remote HearingAid(ASHA) Bluetooth devices.
+ * HearingAidDeviceManager manages the set of remote bluetooth hearing devices.
  */
 public class HearingAidDeviceManager {
     private static final String TAG = "HearingAidDeviceManager";
@@ -49,6 +63,12 @@
     private final LocalBluetoothManager mBtManager;
     private final List<CachedBluetoothDevice> mCachedDevices;
     private final HearingAidAudioRoutingHelper mRoutingHelper;
+    private static final Map<ConnectionStatusListener, Executor>
+            mConnectionStatusListeners = new ConcurrentHashMap<>();
+    @ConnectionStatus
+    private int mDevicesConnectionStatus = ConnectionStatus.NO_DEVICE_BONDED;
+    private boolean mInitialDevicesConnectionStatusUpdate = false;
+
     HearingAidDeviceManager(Context context, LocalBluetoothManager localBtManager,
             List<CachedBluetoothDevice> CachedDevices) {
         mContext = context;
@@ -68,6 +88,191 @@
         mRoutingHelper = routingHelper;
     }
 
+    /**
+     * Defines the connection status for hearing devices.
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({
+            ConnectionStatus.NO_DEVICE_BONDED,
+            ConnectionStatus.DISCONNECTED,
+            ConnectionStatus.CONNECTED,
+            ConnectionStatus.CONNECTING_OR_DISCONNECTING,
+            ConnectionStatus.ACTIVE
+    })
+    public @interface ConnectionStatus {
+        int NO_DEVICE_BONDED = -1;
+        int DISCONNECTED = 0;
+        int CONNECTED = 1;
+        int CONNECTING_OR_DISCONNECTING = 2;
+        int ACTIVE = 3;
+    }
+    /**
+     * Interface for connection status listener.
+     */
+    public interface ConnectionStatusListener {
+        /**
+         * Callback when hearing devices connection status change.
+         *
+         * <p>devices here means singular device or binaural device.
+         * E.g. One of hearing device is in CONNECTED status and another is in DISCONNECTED,
+         * it will callback CONNECTED status.
+         *
+         * @param status Updated {@link ConnectionStatus}
+         */
+        void onDevicesConnectionStatusChanged(@ConnectionStatus int status);
+    }
+
+    /**
+     * Registers a listener to be notified of connection status changes.
+     *
+     * @param listener The listener to register.
+     * @param executor The executor on which the listener's callback will be run.
+     */
+    public void registerConnectionStatusListener(
+            @NonNull ConnectionStatusListener listener,
+            @NonNull @CallbackExecutor Executor executor) {
+        mConnectionStatusListeners.put(listener, executor);
+    }
+
+    /**
+     * Unregisters a listener previously registered with
+     * {@link #registerConnectionStatusListener(ConnectionStatusListener, Executor)}.
+     *
+     * @param listener The listener to unregister.
+     */
+    public void unregisterConnectionStatusListener(
+            @NonNull ConnectionStatusListener listener) {
+        mConnectionStatusListeners.remove(listener);
+    }
+
+    private void notifyDevicesConnectionStatusChanged(int status) {
+        mConnectionStatusListeners.forEach((listener, executor) ->
+                executor.execute(() -> listener.onDevicesConnectionStatusChanged(status)));
+    }
+
+    /**
+     * Updates the connection status of the hearing devices based on the currently bonded
+     * hearing aid devices.
+     */
+    synchronized void notifyDevicesConnectionStatusChanged() {
+        final int prevVal = mDevicesConnectionStatus;
+        updateDevicesConnectionStatus();
+        if (mDevicesConnectionStatus != prevVal) {
+            notifyDevicesConnectionStatusChanged(mDevicesConnectionStatus);
+        }
+    }
+
+    private void updateDevicesConnectionStatus() {
+        mInitialDevicesConnectionStatusUpdate = true;
+        // Add all hearing devices including sub and member into a set.
+        Set<CachedBluetoothDevice> allHearingDevices = mCachedDevices.stream()
+                .filter(d -> d.getBondState() == BluetoothDevice.BOND_BONDED
+                        && d.isHearingDevice())
+                .flatMap(d -> getAssociatedCachedDevice(d).stream())
+                .collect(Collectors.toSet());
+
+        // Status sequence matters here. If one of the hearing devices is in previous
+        // ConnectionStatus, we will treat whole hearing devices is in this status.
+        // E.g. One of hearing device is in CONNECTED status and another is in DISCONNECTED
+        // status, the hearing devices connection status will notify CONNECTED status.
+        if (isConnectingOrDisconnectingConnectionStatus(allHearingDevices)) {
+            mDevicesConnectionStatus = ConnectionStatus.CONNECTING_OR_DISCONNECTING;
+        } else if (isActiveConnectionStatus(allHearingDevices)) {
+            mDevicesConnectionStatus = ConnectionStatus.ACTIVE;
+        } else if (isConnectedStatus(allHearingDevices)) {
+            mDevicesConnectionStatus = ConnectionStatus.CONNECTED;
+        } else if (isDisconnectedStatus(allHearingDevices)) {
+            mDevicesConnectionStatus = ConnectionStatus.DISCONNECTED;
+        } else {
+            mDevicesConnectionStatus = ConnectionStatus.NO_DEVICE_BONDED;
+        }
+
+        if (DEBUG) {
+            Log.d(TAG, "updateDevicesConnectionStatus: " + mDevicesConnectionStatus);
+        }
+    }
+
+    /**
+     * @return all the related CachedBluetoothDevices for this device.
+     */
+    @NonNull
+    public Set<CachedBluetoothDevice> getAssociatedCachedDevice(
+            @NonNull CachedBluetoothDevice device) {
+        ArraySet<CachedBluetoothDevice> cachedDeviceSet = new ArraySet<>();
+        cachedDeviceSet.add(device);
+        // Associated device should be added into memberDevice if it support CSIP profile.
+        Set<CachedBluetoothDevice> memberDevices = device.getMemberDevice();
+        if (!memberDevices.isEmpty()) {
+            cachedDeviceSet.addAll(memberDevices);
+            return cachedDeviceSet;
+        }
+        // If not support CSIP profile, it should be ASHA hearing device and added into subDevice.
+        CachedBluetoothDevice subDevice = device.getSubDevice();
+        if (subDevice != null) {
+            cachedDeviceSet.add(subDevice);
+            return cachedDeviceSet;
+        }
+
+        return cachedDeviceSet;
+    }
+
+    private boolean isConnectingOrDisconnectingConnectionStatus(
+            Set<CachedBluetoothDevice> devices) {
+        HearingAidProfile hearingAidProfile = mBtManager.getProfileManager().getHearingAidProfile();
+        HapClientProfile hapClientProfile = mBtManager.getProfileManager().getHapClientProfile();
+
+        for (CachedBluetoothDevice device : devices) {
+            if (hearingAidProfile != null) {
+                int status = device.getProfileConnectionState(hearingAidProfile);
+                if (status == BluetoothProfile.STATE_DISCONNECTING
+                        || status == BluetoothProfile.STATE_CONNECTING) {
+                    return true;
+                }
+            }
+            if (hapClientProfile != null) {
+                int status = device.getProfileConnectionState(hapClientProfile);
+                if (status == BluetoothProfile.STATE_DISCONNECTING
+                        || status == BluetoothProfile.STATE_CONNECTING) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private boolean isActiveConnectionStatus(Set<CachedBluetoothDevice> devices) {
+        for (CachedBluetoothDevice device : devices) {
+            if ((device.isActiveDevice(BluetoothProfile.HEARING_AID)
+                    && device.isConnectedProfile(BluetoothProfile.HEARING_AID))
+                    || (device.isActiveDevice(BluetoothProfile.LE_AUDIO)
+                    && device.isConnectedProfile(BluetoothProfile.LE_AUDIO))) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean isConnectedStatus(Set<CachedBluetoothDevice> devices) {
+        return devices.stream().anyMatch(CachedBluetoothDevice::isConnected);
+    }
+
+    private boolean isDisconnectedStatus(Set<CachedBluetoothDevice> devices) {
+        return devices.stream().anyMatch(
+                d -> (!d.isConnected() && d.getBondState() == BOND_BONDED));
+    }
+
+    /**
+     * Gets the connection status for hearing device set. Will update connection status first if
+     * never updated.
+     */
+    @ConnectionStatus
+    public int getDevicesConnectionStatus() {
+        if (!mInitialDevicesConnectionStatusUpdate) {
+            updateDevicesConnectionStatus();
+        }
+        return mDevicesConnectionStatus;
+    }
+
     void initHearingAidDeviceIfNeeded(CachedBluetoothDevice newDevice,
             List<ScanFilter> leScanFilters) {
         HearingAidInfo info = generateHearingAidInfo(newDevice);
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
index 8dfeb55..7c24df9 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
@@ -47,12 +47,14 @@
 
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.CollectionUtils;
+import com.android.settingslib.flags.Flags;
 
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.CopyOnWriteArrayList;
 
 
@@ -345,11 +347,17 @@
                     oldState == BluetoothProfile.STATE_CONNECTING) {
                 Log.i(TAG, "Failed to connect " + mProfile + " device");
             }
+            final boolean isAshaProfile = getHearingAidProfile() != null
+                    && mProfile instanceof HearingAidProfile;
+            final boolean isHapClientProfile = getHapClientProfile() != null
+                    && mProfile instanceof HapClientProfile;
+            final boolean isLeAudioProfile = getLeAudioProfile() != null
+                    && mProfile instanceof LeAudioProfile;
+            final boolean isHapClientOrLeAudioProfile = isHapClientProfile || isLeAudioProfile;
+            final boolean isCsipProfile = getCsipSetCoordinatorProfile() != null
+                    && mProfile instanceof CsipSetCoordinatorProfile;
 
-            if (getHearingAidProfile() != null
-                    && mProfile instanceof HearingAidProfile
-                    && (newState == BluetoothProfile.STATE_CONNECTED)) {
-
+            if (isAshaProfile && (newState == BluetoothProfile.STATE_CONNECTED)) {
                 // Check if the HiSyncID has being initialized
                 if (cachedDevice.getHiSyncId() == BluetoothHearingAid.HI_SYNC_ID_INVALID) {
                     long newHiSyncId = getHearingAidProfile().getHiSyncId(cachedDevice.getDevice());
@@ -366,11 +374,6 @@
                 HearingAidStatsLogUtils.logHearingAidInfo(cachedDevice);
             }
 
-            final boolean isHapClientProfile = getHapClientProfile() != null
-                    && mProfile instanceof HapClientProfile;
-            final boolean isLeAudioProfile = getLeAudioProfile() != null
-                    && mProfile instanceof LeAudioProfile;
-            final boolean isHapClientOrLeAudioProfile = isHapClientProfile || isLeAudioProfile;
             if (isHapClientOrLeAudioProfile && newState == BluetoothProfile.STATE_CONNECTED) {
 
                 // Checks if both profiles are connected to the device. Hearing aid info need
@@ -385,9 +388,7 @@
                 }
             }
 
-            if (getCsipSetCoordinatorProfile() != null
-                    && mProfile instanceof CsipSetCoordinatorProfile
-                    && newState == BluetoothProfile.STATE_CONNECTED) {
+            if (isCsipProfile && (newState == BluetoothProfile.STATE_CONNECTED)) {
                 // Check if the GroupID has being initialized
                 if (cachedDevice.getGroupId() == BluetoothCsipSetCoordinator.GROUP_ID_INVALID) {
                     final Map<Integer, ParcelUuid> groupIdMap = getCsipSetCoordinatorProfile()
@@ -403,6 +404,21 @@
                 }
             }
 
+            // LE_AUDIO, CSIP_SET_COORDINATOR profiles will also impact the connection status
+            // change, e.g. device need to active on LE_AUDIO to become active connection status.
+            final Set<Integer> hearingDeviceConnectionStatusProfileId = Set.of(
+                    BluetoothProfile.HEARING_AID,
+                    BluetoothProfile.HAP_CLIENT,
+                    BluetoothProfile.LE_AUDIO,
+                    BluetoothProfile.CSIP_SET_COORDINATOR
+            );
+            if (Flags.hearingDeviceSetConnectionStatusReport()) {
+                if (hearingDeviceConnectionStatusProfileId.contains(mProfile.getProfileId())) {
+                    mDeviceManager.notifyHearingDevicesConnectionStatusChangedIfNeeded(
+                            cachedDevice);
+                }
+            }
+
             cachedDevice.onProfileStateChanged(mProfile, newState);
             // Dispatch profile changed after device update
             boolean needDispatchProfileConnectionState = true;
diff --git a/packages/SettingsLib/src/com/android/settingslib/display/DisplayDensityUtils.java b/packages/SettingsLib/src/com/android/settingslib/display/DisplayDensityUtils.java
index d91c6bd..58e9355 100644
--- a/packages/SettingsLib/src/com/android/settingslib/display/DisplayDensityUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/display/DisplayDensityUtils.java
@@ -29,13 +29,16 @@
 import android.view.DisplayInfo;
 import android.view.IWindowManager;
 import android.view.WindowManagerGlobal;
+import android.window.ConfigurationChangeSetting;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
 import com.android.settingslib.R;
 
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 import java.util.function.Predicate;
 
 /**
@@ -340,4 +343,31 @@
             }
         });
     }
+
+    /**
+     * Returns a list of {@link ConfigurationChangeSetting} object representing the forced display
+     * density settings for the displays that satisfy the predicate.
+     *
+     * @param index the index of the density value in the available density values array.
+     * @return a list of {@link ConfigurationChangeSetting} objects.
+     * @see IWindowManager#setConfigurationChangeSettingsForUser
+     */
+    @NonNull
+    public List<ConfigurationChangeSetting> getForcedDisplayDensitySetting(final int index) {
+        final ArrayList<ConfigurationChangeSetting> settings = new ArrayList<>();
+        for (final Display display : mDisplayManager.getDisplays(
+                DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED)) {
+            final int displayId = display.getDisplayId();
+            final DisplayInfo info = new DisplayInfo();
+            if (!display.getDisplayInfo(info)) {
+                Log.w(LOG_TAG, "Unable to get display info for display " + displayId);
+                continue;
+            }
+            if (!mPredicate.test(info)) {
+                continue;
+            }
+            settings.add(new ConfigurationChangeSetting.DensitySetting(displayId, mValues[index]));
+        }
+        return settings;
+    }
 }
diff --git a/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminControllerFactory.java b/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminControllerFactory.java
index 7516d2e..e3d7902 100644
--- a/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminControllerFactory.java
+++ b/packages/SettingsLib/src/com/android/settingslib/enterprise/ActionDisabledByAdminControllerFactory.java
@@ -22,6 +22,7 @@
 import static com.android.settingslib.enterprise.ManagedDeviceActionDisabledByAdminController.DEFAULT_FOREGROUND_USER_CHECKER;
 
 import android.app.admin.DevicePolicyManager;
+import android.app.supervision.SupervisionManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.hardware.biometrics.BiometricAuthenticator;
@@ -59,12 +60,18 @@
     }
 
     private static boolean isSupervisedDevice(Context context) {
-        DevicePolicyManager devicePolicyManager =
-                context.getSystemService(DevicePolicyManager.class);
-        ComponentName supervisionComponent =
-                devicePolicyManager.getProfileOwnerOrDeviceOwnerSupervisionComponent(
-                        new UserHandle(UserHandle.myUserId()));
-        return supervisionComponent != null;
+        if (android.app.supervision.flags.Flags.deprecateDpmSupervisionApis()) {
+            SupervisionManager supervisionManager =
+                    context.getSystemService(SupervisionManager.class);
+            return supervisionManager.isSupervisionEnabledForUser(UserHandle.myUserId());
+        } else {
+            DevicePolicyManager devicePolicyManager =
+                    context.getSystemService(DevicePolicyManager.class);
+            ComponentName supervisionComponent =
+                    devicePolicyManager.getProfileOwnerOrDeviceOwnerSupervisionComponent(
+                            new UserHandle(UserHandle.myUserId()));
+            return supervisionComponent != null;
+        }
     }
 
     /**
diff --git a/packages/SettingsLib/tests/robotests/Android.bp b/packages/SettingsLib/tests/robotests/Android.bp
index 81358ca..117ca85 100644
--- a/packages/SettingsLib/tests/robotests/Android.bp
+++ b/packages/SettingsLib/tests/robotests/Android.bp
@@ -65,7 +65,6 @@
     test_options: {
         timeout: 36000,
     },
-    upstream: true,
 
     strict_mode: false,
 }
@@ -100,10 +99,10 @@
     plugins: [
         "auto_value_plugin_1.9",
         "auto_value_builder_plugin_1.9",
-        "Robolectric_processor_upstream",
+        "Robolectric_processor",
     ],
     libs: [
-        "Robolectric_all-target_upstream",
+        "Robolectric_all-target",
         "mockito-robolectric-prebuilt",
         "truth",
     ],
diff --git a/packages/SettingsLib/tests/robotests/fragment/Android.bp b/packages/SettingsLib/tests/robotests/fragment/Android.bp
index 3e67156..0214874 100644
--- a/packages/SettingsLib/tests/robotests/fragment/Android.bp
+++ b/packages/SettingsLib/tests/robotests/fragment/Android.bp
@@ -28,13 +28,13 @@
         //"-J-verbose",
     ],
     libs: [
-        "Robolectric_all-target_upstream",
+        "Robolectric_all-target",
         "androidx.fragment_fragment",
     ],
     plugins: [
         "auto_value_plugin_1.9",
         "auto_value_builder_plugin_1.9",
-        "Robolectric_processor_upstream",
+        "Robolectric_processor",
     ],
 
 }
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 ab9f871..cafe19f 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
@@ -80,7 +80,9 @@
     @Mock(answer = Answers.RETURNS_DEEP_STUBS)
     private CachedBluetoothDevice mCachedBluetoothDevice;
 
-    @Mock private BluetoothDevice mBluetoothDevice;
+    @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+    private BluetoothDevice mBluetoothDevice;
+
     @Mock private AudioManager mAudioManager;
     @Mock private PackageManager mPackageManager;
     @Mock private LeAudioProfile mA2dpProfile;
@@ -96,6 +98,7 @@
     private Context mContext;
     private ShadowBluetoothAdapter mShadowBluetoothAdapter;
     private static final String STRING_METADATA = "string_metadata";
+    private static final String LE_AUDIO_SHARING_METADATA = "le_audio_sharing";
     private static final String BOOL_METADATA = "true";
     private static final String INT_METADATA = "25";
     private static final int METADATA_FAST_PAIR_CUSTOMIZED_FIELDS = 25;
@@ -104,6 +107,8 @@
             "<HEARABLE_CONTROL_SLICE_WITH_WIDTH>"
                     + STRING_METADATA
                     + "</HEARABLE_CONTROL_SLICE_WITH_WIDTH>";
+    private static final String TEMP_BOND_METADATA =
+            "<TEMP_BOND_TYPE>" + LE_AUDIO_SHARING_METADATA + "</TEMP_BOND_TYPE>";
     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;
@@ -396,6 +401,38 @@
     }
 
     @Test
+    public void isHeadset_metadataMatched_returnTrue() {
+        when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_DEVICE_TYPE))
+                .thenReturn(BluetoothDevice.DEVICE_TYPE_UNTETHERED_HEADSET.getBytes());
+
+        assertThat(BluetoothUtils.isHeadset(mBluetoothDevice)).isTrue();
+    }
+
+    @Test
+    public void isHeadset_metadataNotMatched_returnFalse() {
+        when(mBluetoothDevice.getMetadata(BluetoothDevice.METADATA_DEVICE_TYPE))
+                .thenReturn(BluetoothDevice.DEVICE_TYPE_CARKIT.getBytes());
+
+        assertThat(BluetoothUtils.isHeadset(mBluetoothDevice)).isFalse();
+    }
+
+    @Test
+    public void isHeadset_btClassMatched_returnTrue() {
+        when(mBluetoothDevice.getBluetoothClass().getDeviceClass())
+                .thenReturn(BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES);
+
+        assertThat(BluetoothUtils.isHeadset(mBluetoothDevice)).isTrue();
+    }
+
+    @Test
+    public void isHeadset_btClassNotMatched_returnFalse() {
+        when(mBluetoothDevice.getBluetoothClass().getDeviceClass())
+                .thenReturn(BluetoothClass.Device.AUDIO_VIDEO_LOUDSPEAKER);
+
+        assertThat(BluetoothUtils.isHeadset(mBluetoothDevice)).isFalse();
+    }
+
+    @Test
     public void isAvailableMediaBluetoothDevice_isConnectedLeAudioDevice_returnTrue() {
         when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
         when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
@@ -1303,4 +1340,12 @@
 
         assertThat(BluetoothUtils.isAudioSharingHysteresisModeFixAvailable(mContext)).isTrue();
     }
+
+    @Test
+    public void isTemporaryBondDevice_hasMetadata_returnsTrue() {
+        when(mBluetoothDevice.getMetadata(METADATA_FAST_PAIR_CUSTOMIZED_FIELDS))
+                .thenReturn(TEMP_BOND_METADATA.getBytes());
+
+        assertThat(BluetoothUtils.isTemporaryBondDevice(mBluetoothDevice)).isTrue();
+    }
 }
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 05f471f..69e99c6 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
@@ -33,21 +33,37 @@
 import android.content.Context;
 import android.os.Parcel;
 import android.os.ParcelUuid;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.google.common.collect.ImmutableList;
 
 import org.junit.Before;
 import org.junit.Ignore;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
 import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
 
 import java.util.Collection;
+import java.util.List;
 import java.util.Map;
 
 @RunWith(RobolectricTestRunner.class)
 public class CachedBluetoothDeviceManagerTest {
+    @Rule
+    public MockitoRule mMockitoRule = MockitoJUnit.rule();
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
+    private final Context mContext = ApplicationProvider.getApplicationContext();
+
     private final static String DEVICE_NAME_1 = "TestName_1";
     private final static String DEVICE_NAME_2 = "TestName_2";
     private final static String DEVICE_NAME_3 = "TestName_3";
@@ -82,6 +98,8 @@
     @Mock
     private HearingAidProfile mHearingAidProfile;
     @Mock
+    private HapClientProfile mHapClientProfile;
+    @Mock
     private CsipSetCoordinatorProfile mCsipSetCoordinatorProfile;
     @Mock
     private BluetoothDevice mDevice1;
@@ -89,12 +107,11 @@
     private BluetoothDevice mDevice2;
     @Mock
     private BluetoothDevice mDevice3;
+    private HearingAidDeviceManager mHearingAidDeviceManager;
     private CachedBluetoothDevice mCachedDevice1;
     private CachedBluetoothDevice mCachedDevice2;
     private CachedBluetoothDevice mCachedDevice3;
     private CachedBluetoothDeviceManager mCachedDeviceManager;
-    private HearingAidDeviceManager mHearingAidDeviceManager;
-    private Context mContext;
 
     private BluetoothClass createBtClass(int deviceClass) {
         Parcel p = Parcel.obtain();
@@ -108,8 +125,6 @@
 
     @Before
     public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        mContext = RuntimeEnvironment.application;
         when(mDevice1.getAddress()).thenReturn(DEVICE_ADDRESS_1);
         when(mDevice2.getAddress()).thenReturn(DEVICE_ADDRESS_2);
         when(mDevice3.getAddress()).thenReturn(DEVICE_ADDRESS_3);
@@ -129,13 +144,15 @@
         when(mA2dpProfile.isProfileReady()).thenReturn(true);
         when(mPanProfile.isProfileReady()).thenReturn(true);
         when(mHearingAidProfile.isProfileReady()).thenReturn(true);
+        when(mHapClientProfile.isProfileReady()).thenReturn(true);
         when(mCsipSetCoordinatorProfile.isProfileReady())
                 .thenReturn(true);
         doAnswer((invocation) -> mHearingAidProfile).
                 when(mLocalProfileManager).getHearingAidProfile();
         doAnswer((invocation) -> mCsipSetCoordinatorProfile)
                 .when(mLocalProfileManager).getCsipSetCoordinatorProfile();
-        mCachedDeviceManager = new CachedBluetoothDeviceManager(mContext, mLocalBluetoothManager);
+        mCachedDeviceManager = spy(
+                new CachedBluetoothDeviceManager(mContext, mLocalBluetoothManager));
         mCachedDevice1 = spy(new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice1));
         mCachedDevice2 = spy(new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice2));
         mCachedDevice3 = spy(new CachedBluetoothDevice(mContext, mLocalProfileManager, mDevice3));
@@ -621,12 +638,55 @@
     public void onActiveDeviceChanged_validHiSyncId_callExpectedFunction() {
         doNothing().when(mHearingAidDeviceManager).onActiveDeviceChanged(any());
         when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
-        CachedBluetoothDevice cachedDevice1 = mCachedDeviceManager.addDevice(mDevice1);
-        cachedDevice1.setHearingAidInfo(
-                new HearingAidInfo.Builder().setHiSyncId(HISYNCID1).build());
+        when(mCachedDevice1.getProfiles()).thenReturn(
+                ImmutableList.of(mHapClientProfile, mHearingAidProfile));
 
-        mCachedDeviceManager.onActiveDeviceChanged(cachedDevice1);
+        mCachedDeviceManager.onActiveDeviceChanged(mCachedDevice1);
 
-        verify(mHearingAidDeviceManager).onActiveDeviceChanged(cachedDevice1);
+        verify(mHearingAidDeviceManager).onActiveDeviceChanged(mCachedDevice1);
+    }
+
+    @Test
+    @RequiresFlagsEnabled(
+            com.android.settingslib.flags.Flags.FLAG_HEARING_DEVICE_SET_CONNECTION_STATUS_REPORT)
+    public void onActiveDeviceChanged_hearingDevice_callReportConnectionStatus() {
+        when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+        when(mCachedDevice1.getProfiles()).thenReturn(
+                ImmutableList.of(mHapClientProfile, mHearingAidProfile));
+
+        mCachedDeviceManager.onActiveDeviceChanged(mCachedDevice1);
+
+        verify(mHearingAidDeviceManager).notifyDevicesConnectionStatusChanged();
+    }
+
+    @Test
+    @RequiresFlagsEnabled(
+            com.android.settingslib.flags.Flags.FLAG_HEARING_DEVICE_SET_CONNECTION_STATUS_REPORT)
+    public void onDeviceUnpaired_hearingDevice_callReportConnectionStatus() {
+        when(mDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+        when(mCachedDevice1.getProfiles()).thenReturn(
+                ImmutableList.of(mHapClientProfile, mHearingAidProfile));
+
+        mCachedDeviceManager.onDeviceUnpaired(mCachedDevice1);
+
+        verify(mHearingAidDeviceManager).notifyDevicesConnectionStatusChanged();
+    }
+
+    @Test
+    public void notifyHearingDevicesConnectionStatusChanged_nonHearingDevice_notCallFunction() {
+        when(mCachedDevice1.getProfiles()).thenReturn(List.of(mA2dpProfile));
+
+        mCachedDeviceManager.notifyHearingDevicesConnectionStatusChangedIfNeeded(mCachedDevice1);
+
+        verify(mHearingAidDeviceManager, never()).notifyDevicesConnectionStatusChanged();
+    }
+
+    @Test
+    public void notifyHearingDevicesConnectionStatusChanged_hearingDeviceProfile_callFunction() {
+        when(mCachedDevice1.getProfiles()).thenReturn(List.of(mHapClientProfile));
+
+        mCachedDeviceManager.notifyHearingDevicesConnectionStatusChangedIfNeeded(mCachedDevice1);
+
+        verify(mHearingAidDeviceManager).notifyDevicesConnectionStatusChanged();
     }
 }
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
index 30f8a79..d933a1c 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
@@ -2074,6 +2074,21 @@
         assertThat(mCachedDevice.getConnectionSummary(false)).isNull();
     }
 
+    @Test
+    public void isHearingDevice_supportHearingRelatedProfiles_returnTrue() {
+        when(mCachedDevice.getProfiles()).thenReturn(
+                ImmutableList.of(mHapClientProfile, mHearingAidProfile));
+
+        assertThat(mCachedDevice.isHearingDevice()).isTrue();
+    }
+
+    @Test
+    public void isHearingDevice_supportOnlyLeAudioProfile_returnFalse() {
+        when(mCachedDevice.getProfiles()).thenReturn(ImmutableList.of(mLeAudioProfile));
+
+        assertThat(mCachedDevice.isHearingDevice()).isFalse();
+    }
+
     private void updateProfileStatus(LocalBluetoothProfile profile, int status) {
         doReturn(status).when(profile).getConnectionStatus(mDevice);
         mCachedDevice.onProfileStateChanged(profile, status);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java
index 2458c5b..21dde1f 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java
@@ -54,6 +54,8 @@
 
 import androidx.test.core.app.ApplicationProvider;
 
+import com.android.settingslib.bluetooth.HearingAidDeviceManager.ConnectionStatus;
+
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -112,7 +114,10 @@
     private BluetoothDevice mDevice1;
     @Mock
     private BluetoothDevice mDevice2;
-
+    @Mock
+    private HearingAidDeviceManager.ConnectionStatusListener mConnectionStatusListener;
+    @Mock
+    private HearingAidDeviceManager.ConnectionStatusListener mConnectionStatusListener2;
 
     private BluetoothClass createBtClass(int deviceClass) {
         Parcel p = Parcel.obtain();
@@ -140,6 +145,8 @@
         when(mLocalProfileManager.getHearingAidProfile()).thenReturn(mHearingAidProfile);
         when(mLocalProfileManager.getLeAudioProfile()).thenReturn(mLeAudioProfile);
         when(mLocalProfileManager.getHapClientProfile()).thenReturn(mHapClientProfile);
+        when(mHapClientProfile.getProfileId()).thenReturn(BluetoothProfile.HAP_CLIENT);
+        when(mLeAudioProfile.getProfileId()).thenReturn(BluetoothProfile.LE_AUDIO);
         when(mAudioStrategy.getAudioAttributesForLegacyStreamType(
                 AudioManager.STREAM_MUSIC))
                 .thenReturn((new AudioAttributes.Builder()).build());
@@ -826,6 +833,125 @@
         verify(mHapClientProfile).selectPreset(mDevice2, PRESET_INDEX_1);
     }
 
+    @Test
+    public void getAssociatedCachedDevice_existSubDevice_returnSize2() {
+        mCachedDevice1.setSubDevice(mCachedDevice2);
+
+        //including self device
+        assertThat(mHearingAidDeviceManager.getAssociatedCachedDevice(
+                mCachedDevice1).size()).isEqualTo(2);
+    }
+
+    @Test
+    public void getAssociatedCachedDevice_existMemberDevice_returnSize2() {
+        mCachedDevice1.addMemberDevice(mCachedDevice2);
+
+        //including self device
+        assertThat(mHearingAidDeviceManager.getAssociatedCachedDevice(
+                mCachedDevice1).size()).isEqualTo(2);
+    }
+
+    @Test
+    public void notifyDevicesConnectionStatusChanged_connecting_connectingStatus() {
+        when(mCachedDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+        when(mCachedDevice1.getProfiles()).thenReturn(List.of(mHapClientProfile));
+        when(mHapClientProfile.getConnectionStatus(mDevice1)).thenReturn(
+                BluetoothProfile.STATE_CONNECTING);
+
+        mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
+        mHearingAidDeviceManager.notifyDevicesConnectionStatusChanged();
+
+        assertThat(mHearingAidDeviceManager.getDevicesConnectionStatus()).isEqualTo(
+                ConnectionStatus.CONNECTING_OR_DISCONNECTING);
+    }
+
+    @Test
+    public void notifyDevicesConnectionStatusChanged_activeConnectedProfile_activeStatus() {
+        when(mCachedDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+        when(mCachedDevice1.getProfiles()).thenReturn(List.of(mHapClientProfile, mLeAudioProfile));
+        when(mLeAudioProfile.getConnectionStatus(mDevice1)).thenReturn(
+                BluetoothProfile.STATE_CONNECTED);
+        when(mCachedDevice1.isActiveDevice(BluetoothProfile.LE_AUDIO)).thenReturn(true);
+
+        mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
+        mHearingAidDeviceManager.notifyDevicesConnectionStatusChanged();
+
+        assertThat(mHearingAidDeviceManager.getDevicesConnectionStatus()).isEqualTo(
+                ConnectionStatus.ACTIVE);
+    }
+
+    @Test
+    public void notifyDevicesConnectionStatusChanged_isConnected_connectedStatus() {
+        when(mCachedDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+        when(mCachedDevice1.getProfiles()).thenReturn(List.of(mHapClientProfile, mLeAudioProfile));
+        when(mCachedDevice1.isConnected()).thenReturn(true);
+
+        mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
+        mHearingAidDeviceManager.notifyDevicesConnectionStatusChanged();
+
+        assertThat(mHearingAidDeviceManager.getDevicesConnectionStatus()).isEqualTo(
+                ConnectionStatus.CONNECTED);
+    }
+
+    @Test
+    public void notifyDevicesConnectionStatusChanged_bondedNotConnected_disconnectedStatus() {
+        when(mCachedDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+        when(mCachedDevice1.isConnected()).thenReturn(false);
+        when(mCachedDevice1.getProfiles()).thenReturn(List.of(mHapClientProfile));
+        mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
+
+        mHearingAidDeviceManager.notifyDevicesConnectionStatusChanged();
+
+        assertThat(mHearingAidDeviceManager.getDevicesConnectionStatus()).isEqualTo(
+                ConnectionStatus.DISCONNECTED);
+    }
+
+    @Test
+    public void notifyDevicesConnectionStatusChanged_bondNone_noDeviceBondedStatus() {
+        when(mCachedDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_NONE);
+        mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
+
+        mHearingAidDeviceManager.notifyDevicesConnectionStatusChanged();
+
+        assertThat(mHearingAidDeviceManager.getDevicesConnectionStatus()).isEqualTo(
+                ConnectionStatus.NO_DEVICE_BONDED);
+    }
+
+    @Test
+    public void notifyDevicesConnectionStatusChanged_noRegisteredListener_noCallback() {
+        when(mCachedDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+        when(mCachedDevice1.getProfiles()).thenReturn(List.of(mHapClientProfile, mLeAudioProfile));
+        when(mCachedDevice1.isConnected()).thenReturn(true);
+        mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
+
+        mHearingAidDeviceManager.registerConnectionStatusListener(
+                mConnectionStatusListener, mContext.getMainExecutor());
+        mHearingAidDeviceManager.unregisterConnectionStatusListener(
+                mConnectionStatusListener);
+        mHearingAidDeviceManager.notifyDevicesConnectionStatusChanged();
+
+        verify(mConnectionStatusListener, never()).onDevicesConnectionStatusChanged(anyInt());
+    }
+
+    @Test
+    public void notifyDevicesConnectionStatusChanged_twoRegisteredListener_callbackEachConnected() {
+        when(mCachedDevice1.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED);
+        when(mCachedDevice1.getProfiles()).thenReturn(List.of(mHapClientProfile, mLeAudioProfile));
+        when(mCachedDevice1.isConnected()).thenReturn(true);
+        mCachedDeviceManager.mCachedDevices.add(mCachedDevice1);
+
+        mHearingAidDeviceManager.registerConnectionStatusListener(
+                mConnectionStatusListener, mContext.getMainExecutor());
+        mHearingAidDeviceManager.registerConnectionStatusListener(
+                mConnectionStatusListener2, mContext.getMainExecutor());
+        mHearingAidDeviceManager.notifyDevicesConnectionStatusChanged();
+
+        verify(mConnectionStatusListener).onDevicesConnectionStatusChanged(
+                ConnectionStatus.CONNECTED);
+        verify(mConnectionStatusListener2).onDevicesConnectionStatusChanged(
+                ConnectionStatus.CONNECTED);
+    }
+
     private HearingAidInfo getLeftAshaHearingAidInfo(long hiSyncId) {
         return new HearingAidInfo.Builder()
                 .setAshaDeviceSide(HearingAidInfo.DeviceSide.SIDE_LEFT)
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java
index 6ff90ba..219bfe0 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java
@@ -37,10 +37,14 @@
 import android.content.Context;
 import android.content.Intent;
 import android.os.ParcelUuid;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 
 import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -56,6 +60,9 @@
 @RunWith(RobolectricTestRunner.class)
 @Config(shadows = {ShadowBluetoothAdapter.class})
 public class LocalBluetoothProfileManagerTest {
+    @Rule
+    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
     private static final long HISYNCID = 10;
 
     private static final int GROUP_ID = 1;
@@ -305,6 +312,25 @@
         verify(mCachedBluetoothDevice).refresh();
     }
 
+    @Test
+    @RequiresFlagsEnabled(
+            com.android.settingslib.flags.Flags.FLAG_HEARING_DEVICE_SET_CONNECTION_STATUS_REPORT)
+    public void stateChangedHandler_hapProfileStateChanged_notifyHearingDevicesConnectionStatus() {
+        mShadowBluetoothAdapter.setSupportedProfiles(generateList(
+                new int[] {BluetoothProfile.HAP_CLIENT}));
+        mProfileManager.updateLocalProfiles();
+
+        mIntent = new Intent(BluetoothHapClient.ACTION_HAP_CONNECTION_STATE_CHANGED);
+        mIntent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice);
+        mIntent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, BluetoothProfile.STATE_CONNECTING);
+        mIntent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_CONNECTED);
+
+        mContext.sendBroadcast(mIntent);
+
+        verify(mDeviceManager).notifyHearingDevicesConnectionStatusChangedIfNeeded(
+                mCachedBluetoothDevice);
+    }
+
     private List<Integer> generateList(int[] profiles) {
         if (profiles == null) {
             return null;
diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml
index 5ddf005..dafcc72 100644
--- a/packages/SettingsProvider/res/values/defaults.xml
+++ b/packages/SettingsProvider/res/values/defaults.xml
@@ -322,9 +322,6 @@
     <!-- Whether vibrate icon is shown in the status bar by default. -->
     <integer name="def_statusBarVibrateIconEnabled">0</integer>
 
-    <!-- Whether predictive back animation is enabled by default. -->
-    <bool name="def_enable_back_animation">false</bool>
-
     <!-- Whether wifi is always requested by default. -->
     <bool name="def_enable_wifi_always_requested">false</bool>
 
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
index 4125a81f..fc61b1e 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
@@ -46,6 +46,7 @@
         Settings.Global.APP_AUTO_RESTRICTION_ENABLED,
         Settings.Global.AUTO_TIME,
         Settings.Global.AUTO_TIME_ZONE,
+        Settings.Global.TIME_ZONE_NOTIFICATIONS,
         Settings.Global.POWER_SOUNDS_ENABLED,
         Settings.Global.DOCK_SOUNDS_ENABLED,
         Settings.Global.CHARGING_SOUNDS_ENABLED,
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index b9f8c71..dd28402 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -91,6 +91,7 @@
         Settings.Secure.KEY_REPEAT_TIMEOUT_MS,
         Settings.Secure.KEY_REPEAT_DELAY_MS,
         Settings.Secure.CAMERA_GESTURE_DISABLED,
+        Settings.Secure.ACCESSIBILITY_AUTOCLICK_CURSOR_AREA_SIZE,
         Settings.Secure.ACCESSIBILITY_AUTOCLICK_ENABLED,
         Settings.Secure.ACCESSIBILITY_AUTOCLICK_DELAY,
         Settings.Secure.ACCESSIBILITY_LARGE_POINTER_ICON,
@@ -186,11 +187,6 @@
         Settings.Secure.BACK_GESTURE_INSET_SCALE_LEFT,
         Settings.Secure.BACK_GESTURE_INSET_SCALE_RIGHT,
         Settings.Secure.NAVIGATION_MODE,
-        Settings.Secure.TRACKPAD_GESTURE_BACK_ENABLED,
-        Settings.Secure.TRACKPAD_GESTURE_HOME_ENABLED,
-        Settings.Secure.TRACKPAD_GESTURE_OVERVIEW_ENABLED,
-        Settings.Secure.TRACKPAD_GESTURE_NOTIFICATION_ENABLED,
-        Settings.Secure.TRACKPAD_GESTURE_QUICK_SWITCH_ENABLED,
         Settings.Secure.SKIP_GESTURE_COUNT,
         Settings.Secure.SKIP_TOUCH_COUNT,
         Settings.Secure.SILENCE_ALARMS_GESTURE_COUNT,
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
index f1bbfc6..1f56f10 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
@@ -109,7 +109,9 @@
                 Settings.System.LOCALE_PREFERENCES,
                 Settings.System.MOUSE_REVERSE_VERTICAL_SCROLLING,
                 Settings.System.MOUSE_SCROLLING_ACCELERATION,
+                Settings.System.MOUSE_SCROLLING_SPEED,
                 Settings.System.MOUSE_SWAP_PRIMARY_BUTTON,
+                Settings.System.MOUSE_POINTER_ACCELERATION_ENABLED,
                 Settings.System.TOUCHPAD_POINTER_SPEED,
                 Settings.System.TOUCHPAD_NATURAL_SCROLLING,
                 Settings.System.TOUCHPAD_TAP_TO_CLICK,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
index 32d4580..c0e266f 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
@@ -102,6 +102,7 @@
                 });
         VALIDATORS.put(Global.AUTO_TIME, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Global.AUTO_TIME_ZONE, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Global.TIME_ZONE_NOTIFICATIONS, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Global.POWER_SOUNDS_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Global.DOCK_SOUNDS_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Global.CHARGING_SOUNDS_ENABLED, BOOLEAN_VALIDATOR);
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index 7c5e577..b01f622 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -140,6 +140,8 @@
         VALIDATORS.put(Secure.KEY_REPEAT_TIMEOUT_MS, NON_NEGATIVE_INTEGER_VALIDATOR);
         VALIDATORS.put(Secure.KEY_REPEAT_DELAY_MS, NON_NEGATIVE_INTEGER_VALIDATOR);
         VALIDATORS.put(Secure.CAMERA_GESTURE_DISABLED, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(
+                Secure.ACCESSIBILITY_AUTOCLICK_CURSOR_AREA_SIZE, NON_NEGATIVE_INTEGER_VALIDATOR);
         VALIDATORS.put(Secure.ACCESSIBILITY_AUTOCLICK_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.ACCESSIBILITY_AUTOCLICK_DELAY, NON_NEGATIVE_INTEGER_VALIDATOR);
         VALIDATORS.put(Secure.ACCESSIBILITY_LARGE_POINTER_ICON, BOOLEAN_VALIDATOR);
@@ -284,11 +286,6 @@
                 new InclusiveFloatRangeValidator(0.0f, Float.MAX_VALUE));
         VALIDATORS.put(Secure.BACK_GESTURE_INSET_SCALE_RIGHT,
                 new InclusiveFloatRangeValidator(0.0f, Float.MAX_VALUE));
-        VALIDATORS.put(Secure.TRACKPAD_GESTURE_BACK_ENABLED, BOOLEAN_VALIDATOR);
-        VALIDATORS.put(Secure.TRACKPAD_GESTURE_HOME_ENABLED, BOOLEAN_VALIDATOR);
-        VALIDATORS.put(Secure.TRACKPAD_GESTURE_OVERVIEW_ENABLED, BOOLEAN_VALIDATOR);
-        VALIDATORS.put(Secure.TRACKPAD_GESTURE_NOTIFICATION_ENABLED, BOOLEAN_VALIDATOR);
-        VALIDATORS.put(Secure.TRACKPAD_GESTURE_QUICK_SWITCH_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.AWARE_ENABLED, BOOLEAN_VALIDATOR);
         VALIDATORS.put(Secure.SKIP_GESTURE_COUNT, NON_NEGATIVE_INTEGER_VALIDATOR);
         VALIDATORS.put(Secure.SKIP_TOUCH_COUNT, NON_NEGATIVE_INTEGER_VALIDATOR);
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
index 6abd9b7..4d98a11 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
@@ -226,6 +226,8 @@
         VALIDATORS.put(System.MOUSE_REVERSE_VERTICAL_SCROLLING, BOOLEAN_VALIDATOR);
         VALIDATORS.put(System.MOUSE_SWAP_PRIMARY_BUTTON, BOOLEAN_VALIDATOR);
         VALIDATORS.put(System.MOUSE_SCROLLING_ACCELERATION, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(System.MOUSE_POINTER_ACCELERATION_ENABLED, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(System.MOUSE_SCROLLING_SPEED, new InclusiveIntegerRangeValidator(-7, 7));
         VALIDATORS.put(System.TOUCHPAD_POINTER_SPEED, new InclusiveIntegerRangeValidator(-7, 7));
         VALIDATORS.put(System.TOUCHPAD_NATURAL_SCROLLING, BOOLEAN_VALIDATOR);
         VALIDATORS.put(System.TOUCHPAD_TAP_TO_CLICK, BOOLEAN_VALIDATOR);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index a2cc008..c1c3e04 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -193,6 +193,7 @@
             "power_button_instantly_locks";
     private static final String KEY_LOCK_SETTINGS_PIN_ENHANCED_PRIVACY =
             "pin_enhanced_privacy";
+    private static final int NUM_LOCK_SETTINGS = 5;
 
     // Error messages for logging metrics.
     private static final String ERROR_COULD_NOT_READ_FROM_CURSOR =
@@ -208,6 +209,11 @@
     private static final String ERROR_SKIPPED_DUE_TO_LARGE_SCREEN =
         "skipped_due_to_large_screen";
     private static final String ERROR_DID_NOT_PASS_VALIDATION = "did_not_pass_validation";
+    private static final String ERROR_IO_EXCEPTION = "io_exception";
+    private static final String ERROR_FAILED_TO_RESTORE_SOFTAP_CONFIG =
+        "failed_to_restore_softap_config";
+    private static final String ERROR_FAILED_TO_RESTORE_WIFI_CONFIG =
+        "failed_to_restore_wifi_config";
 
 
     // Name of the temporary file we use during full backup/restore.  This is
@@ -794,29 +800,44 @@
 
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         DataOutputStream out = new DataOutputStream(baos);
+        int backedUpSettingsCount = 0;
         try {
             out.writeUTF(KEY_LOCK_SETTINGS_OWNER_INFO_ENABLED);
             out.writeUTF(ownerInfoEnabled ? "1" : "0");
+            backedUpSettingsCount++;
             if (ownerInfo != null) {
                 out.writeUTF(KEY_LOCK_SETTINGS_OWNER_INFO);
                 out.writeUTF(ownerInfo != null ? ownerInfo : "");
+                backedUpSettingsCount++;
             }
             if (lockPatternUtils.isVisiblePatternEverChosen(userId)) {
                 out.writeUTF(KEY_LOCK_SETTINGS_VISIBLE_PATTERN_ENABLED);
                 out.writeUTF(visiblePatternEnabled ? "1" : "0");
+                backedUpSettingsCount++;
             }
             if (lockPatternUtils.isPowerButtonInstantlyLocksEverChosen(userId)) {
                 out.writeUTF(KEY_LOCK_SETTINGS_POWER_BUTTON_INSTANTLY_LOCKS);
                 out.writeUTF(powerButtonInstantlyLocks ? "1" : "0");
+                backedUpSettingsCount++;
             }
             if (lockPatternUtils.isPinEnhancedPrivacyEverChosen(userId)) {
                 out.writeUTF(KEY_LOCK_SETTINGS_PIN_ENHANCED_PRIVACY);
                 out.writeUTF(lockPatternUtils.isPinEnhancedPrivacyEnabled(userId) ? "1" : "0");
+                backedUpSettingsCount++;
             }
             // End marker
             out.writeUTF("");
             out.flush();
+            if (areAgentMetricsEnabled) {
+                numberOfSettingsPerKey.put(KEY_LOCK_SETTINGS, backedUpSettingsCount);
+            }
         } catch (IOException ioe) {
+            if (areAgentMetricsEnabled) {
+                mBackupRestoreEventLogger.logItemsBackupFailed(
+                    KEY_LOCK_SETTINGS,
+                    NUM_LOCK_SETTINGS - backedUpSettingsCount,
+                    ERROR_IO_EXCEPTION);
+            }
         }
         return baos.toByteArray();
     }
@@ -1162,6 +1183,7 @@
 
         ByteArrayInputStream bais = new ByteArrayInputStream(buffer, 0, nBytes);
         DataInputStream in = new DataInputStream(bais);
+        int restoredLockSettingsCount = 0;
         try {
             String key;
             // Read until empty string marker
@@ -1187,9 +1209,20 @@
                         lockPatternUtils.setPinEnhancedPrivacyEnabled("1".equals(value), userId);
                         break;
                 }
+                if (areAgentMetricsEnabled) {
+                    mBackupRestoreEventLogger.logItemsRestored(KEY_LOCK_SETTINGS, /* count= */ 1);
+                    restoredLockSettingsCount++;
+                }
+
             }
             in.close();
         } catch (IOException ioe) {
+            if (areAgentMetricsEnabled) {
+                mBackupRestoreEventLogger.logItemsRestoreFailed(
+                        KEY_LOCK_SETTINGS,
+                        NUM_LOCK_SETTINGS - restoredLockSettingsCount,
+                        ERROR_IO_EXCEPTION);
+            }
         }
     }
 
@@ -1309,12 +1342,31 @@
         mWifiManager.restoreSupplicantBackupData(supplicant_bytes, ipconfig_bytes);
     }
 
-    private byte[] getSoftAPConfiguration() {
-        return mWifiManager.retrieveSoftApBackupData();
+    @VisibleForTesting
+    byte[] getSoftAPConfiguration() {
+        byte[] data = mWifiManager.retrieveSoftApBackupData();
+        if (areAgentMetricsEnabled) {
+            // We're unable to determine how many settings this includes, so we'll just log 1.
+            numberOfSettingsPerKey.put(KEY_SOFTAP_CONFIG, 1);
+        }
+        return data;
     }
 
-    private void restoreSoftApConfiguration(byte[] data) {
-        SoftApConfiguration configInCloud = mWifiManager.restoreSoftApBackupData(data);
+    @VisibleForTesting
+    void restoreSoftApConfiguration(byte[] data) {
+        SoftApConfiguration configInCloud;
+        if (areAgentMetricsEnabled) {
+            try {
+                configInCloud = mWifiManager.restoreSoftApBackupData(data);
+                mBackupRestoreEventLogger.logItemsRestored(KEY_SOFTAP_CONFIG, /* count= */ 1);
+            } catch (Exception e) {
+                configInCloud = null;
+                mBackupRestoreEventLogger.logItemsRestoreFailed(
+                    KEY_SOFTAP_CONFIG, /* count= */ 1, ERROR_FAILED_TO_RESTORE_SOFTAP_CONFIG);
+            }
+        } else {
+            configInCloud = mWifiManager.restoreSoftApBackupData(data);
+        }
         if (configInCloud != null) {
             if (DEBUG) Log.d(TAG, "Successfully unMarshaled SoftApConfiguration ");
             // Depending on device hardware, we may need to notify the user of a setting change
@@ -1405,8 +1457,14 @@
         return baos.toByteArray();
     }
 
-    private byte[] getNewWifiConfigData() {
-        return mWifiManager.retrieveBackupData();
+    @VisibleForTesting
+    byte[] getNewWifiConfigData() {
+        byte[] data = mWifiManager.retrieveBackupData();
+        if (areAgentMetricsEnabled) {
+            // We're unable to determine how many settings this includes, so we'll just log 1.
+            numberOfSettingsPerKey.put(KEY_WIFI_NEW_CONFIG, 1);
+        }
+        return data;
     }
 
     private byte[] getLocaleSettings() {
@@ -1418,11 +1476,22 @@
         return localeList.toLanguageTags().getBytes();
     }
 
-    private void restoreNewWifiConfigData(byte[] bytes) {
+    @VisibleForTesting
+    void restoreNewWifiConfigData(byte[] bytes) {
         if (DEBUG_BACKUP) {
             Log.v(TAG, "Applying restored wifi data");
         }
-        mWifiManager.restoreBackupData(bytes);
+        if (areAgentMetricsEnabled) {
+            try {
+                mWifiManager.restoreBackupData(bytes);
+                mBackupRestoreEventLogger.logItemsRestored(KEY_WIFI_NEW_CONFIG, /* count= */ 1);
+            } catch (Exception e) {
+                mBackupRestoreEventLogger.logItemsRestoreFailed(
+                    KEY_WIFI_NEW_CONFIG, /* count= */ 1, ERROR_FAILED_TO_RESTORE_WIFI_CONFIG);
+            }
+        } else {
+            mWifiManager.restoreBackupData(bytes);
+        }
     }
 
     private void restoreNetworkPolicies(byte[] data) {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index dedd7eb..5ad4b8a 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1715,6 +1715,9 @@
                 Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
                 SecureSettingsProto.Accessibility.ENABLED_ACCESSIBILITY_SERVICES);
         dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_AUTOCLICK_CURSOR_AREA_SIZE,
+                SecureSettingsProto.Accessibility.AUTOCLICK_CURSOR_AREA_SIZE);
+        dumpSetting(s, p,
                 Settings.Secure.ACCESSIBILITY_AUTOCLICK_ENABLED,
                 SecureSettingsProto.Accessibility.AUTOCLICK_ENABLED);
         dumpSetting(s, p,
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index f1f03c3..cb656bd 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -2470,7 +2470,7 @@
         boolean isRestrictedShell = android.security.Flags.protectDeviceConfigFlags()
                 && hasAllowlistPermission;
 
-        if (!isRestrictedShell && hasWritePermission) {
+        if (hasWritePermission) {
             assertCallingUserDenyList(flags);
         } else if (hasAllowlistPermission) {
             Set<String> allowlistedDeviceConfigNamespaces = null;
@@ -2500,7 +2500,7 @@
                 }
 
                 if (!namespaceAllowed && !DeviceConfig.getAdbWritableFlags().contains(flag)) {
-                    Slog.wtf(LOG_TAG, "Permission denial for flag '" + flag
+                    throw new SecurityException("Permission denial for flag '" + flag
                             + "'; allowlist permission granted, but must add flag to the "
                             + "allowlist");
                 }
@@ -6122,17 +6122,7 @@
                 }
 
                 if (currentVersion == 220) {
-                    final SettingsState globalSettings = getGlobalSettingsLocked();
-                    final Setting enableBackAnimation =
-                            globalSettings.getSettingLocked(Global.ENABLE_BACK_ANIMATION);
-                    if (enableBackAnimation.isNull()) {
-                        final boolean defEnableBackAnimation =
-                                getContext()
-                                        .getResources()
-                                        .getBoolean(R.bool.def_enable_back_animation);
-                        initGlobalSettingsDefaultValLocked(
-                                Settings.Global.ENABLE_BACK_ANIMATION, defEnableBackAnimation);
-                    }
+                    // Version 221: Removed
                     currentVersion = 221;
                 }
 
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index c88a7fd..cbdb36f 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -564,7 +564,6 @@
                     Settings.Global.WATCHDOG_TIMEOUT_MILLIS,
                     Settings.Global.MANAGED_PROVISIONING_DEFER_PROVISIONING_TO_ROLE_HOLDER,
                     Settings.Global.REVIEW_PERMISSIONS_NOTIFICATION_STATE,
-                    Settings.Global.ENABLE_BACK_ANIMATION, // Temporary for T, dev option only
                     Settings.Global.HEARING_DEVICE_LOCAL_AMBIENT_VOLUME, // cache per hearing device
                     Settings.Global.HEARING_DEVICE_LOCAL_NOTIFICATION, // cache per hearing device
                     Settings.Global.Wearable.COMBINED_LOCATION_ENABLE,
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 18c43a7..6e5b602c 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java
@@ -16,6 +16,9 @@
 
 package com.android.providers.settings;
 
+import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_WIFI_NEW_CONFIG;
+import static com.android.providers.settings.SettingsBackupRestoreKeys.KEY_SOFTAP_CONFIG;
+
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.assertNull;
@@ -26,8 +29,11 @@
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.when;
 
+import android.annotation.Nullable;
 import android.app.backup.BackupAnnotations.BackupDestination;
 import android.app.backup.BackupAnnotations.OperationType;
 import android.app.backup.BackupDataInput;
@@ -42,6 +48,8 @@
 import android.database.Cursor;
 import android.database.MatrixCursor;
 import android.net.Uri;
+import android.net.wifi.SoftApConfiguration;
+import android.net.wifi.WifiManager;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.UserHandle;
@@ -64,6 +72,7 @@
 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;
 
@@ -126,6 +135,7 @@
 
     @Mock private BackupDataInput mBackupDataInput;
     @Mock private BackupDataOutput mBackupDataOutput;
+    @Mock private static WifiManager mWifiManager;
 
     private TestFriendlySettingsBackupAgent mAgentUnderTest;
     private Context mContext;
@@ -754,6 +764,148 @@
         assertNull(getLoggingResultForDatatype(TEST_KEY, mAgentUnderTest));
     }
 
+    @Test
+    @EnableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS)
+    public void getSoftAPConfiguration_flagIsEnabled_numberOfSettingsInKeyAreRecorded() {
+        mAgentUnderTest.onCreate(
+            UserHandle.SYSTEM, BackupDestination.CLOUD, OperationType.BACKUP);
+        when(mWifiManager.retrieveSoftApBackupData()).thenReturn(null);
+
+        mAgentUnderTest.getSoftAPConfiguration();
+
+        assertEquals(mAgentUnderTest.getNumberOfSettingsPerKey(KEY_SOFTAP_CONFIG), 1);
+    }
+
+    @Test
+    @DisableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS)
+    public void getSoftAPConfiguration_flagIsNotEnabled_numberOfSettingsInKeyAreNotRecorded() {
+        mAgentUnderTest.onCreate(
+            UserHandle.SYSTEM, BackupDestination.CLOUD, OperationType.BACKUP);
+        when(mWifiManager.retrieveSoftApBackupData()).thenReturn(null);
+
+        mAgentUnderTest.getSoftAPConfiguration();
+
+        assertEquals(mAgentUnderTest.getNumberOfSettingsPerKey(KEY_SOFTAP_CONFIG), 0);
+    }
+
+    @Test
+    @EnableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS)
+    public void
+        restoreSoftApConfiguration_flagIsEnabled_restoreIsSuccessful_successMetricsAreLogged() {
+        mAgentUnderTest.onCreate(
+            UserHandle.SYSTEM, BackupDestination.CLOUD, OperationType.RESTORE);
+        SoftApConfiguration config = new SoftApConfiguration.Builder().setSsid("test").build();
+        byte[] data = config.toString().getBytes();
+        when(mWifiManager.restoreSoftApBackupData(any())).thenReturn(null);
+
+        mAgentUnderTest.restoreSoftApConfiguration(data);
+
+        DataTypeResult loggingResult =
+            getLoggingResultForDatatype(KEY_SOFTAP_CONFIG, mAgentUnderTest);
+        assertNotNull(loggingResult);
+        assertEquals(loggingResult.getSuccessCount(), 1);
+    }
+
+    @Test
+    @EnableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS)
+    public void
+        restoreSoftApConfiguration_flagIsEnabled_restoreIsNotSuccessful_failureMetricsAreLogged() {
+        mAgentUnderTest.onCreate(
+            UserHandle.SYSTEM, BackupDestination.CLOUD, OperationType.RESTORE);
+        SoftApConfiguration config = new SoftApConfiguration.Builder().setSsid("test").build();
+        byte[] data = config.toString().getBytes();
+        when(mWifiManager.restoreSoftApBackupData(any())).thenThrow(new RuntimeException());
+
+        mAgentUnderTest.restoreSoftApConfiguration(data);
+
+        DataTypeResult loggingResult =
+            getLoggingResultForDatatype(KEY_SOFTAP_CONFIG, mAgentUnderTest);
+        assertNotNull(loggingResult);
+        assertEquals(loggingResult.getFailCount(), 1);
+    }
+
+    @Test
+    @DisableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS)
+    public void restoreSoftApConfiguration_flagIsNotEnabled_metricsAreNotLogged() {
+        mAgentUnderTest.onCreate(
+            UserHandle.SYSTEM, BackupDestination.CLOUD, OperationType.RESTORE);
+        SoftApConfiguration config = new SoftApConfiguration.Builder().setSsid("test").build();
+        byte[] data = config.toString().getBytes();
+        when(mWifiManager.restoreSoftApBackupData(any())).thenReturn(null);
+
+        mAgentUnderTest.restoreSoftApConfiguration(data);
+
+        assertNull(getLoggingResultForDatatype(KEY_SOFTAP_CONFIG, mAgentUnderTest));
+    }
+
+    @Test
+    @EnableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS)
+    public void getNewWifiConfigData_flagIsEnabled_numberOfSettingsInKeyAreRecorded() {
+        mAgentUnderTest.onCreate(
+            UserHandle.SYSTEM, BackupDestination.CLOUD, OperationType.BACKUP);
+        when(mWifiManager.retrieveBackupData()).thenReturn(null);
+
+        mAgentUnderTest.getNewWifiConfigData();
+
+        assertEquals(mAgentUnderTest.getNumberOfSettingsPerKey(KEY_WIFI_NEW_CONFIG), 1);
+    }
+
+    @Test
+    @DisableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS)
+    public void getNewWifiConfigData_flagIsNotEnabled_numberOfSettingsInKeyAreNotRecorded() {
+        mAgentUnderTest.onCreate(
+            UserHandle.SYSTEM, BackupDestination.CLOUD, OperationType.BACKUP);
+        when(mWifiManager.retrieveBackupData()).thenReturn(null);
+
+        mAgentUnderTest.getNewWifiConfigData();
+
+        assertEquals(mAgentUnderTest.getNumberOfSettingsPerKey(KEY_WIFI_NEW_CONFIG), 0);
+    }
+
+    @Test
+    @EnableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS)
+    public void
+        restoreNewWifiConfigData_flagIsEnabled_restoreIsSuccessful_successMetricsAreLogged() {
+        mAgentUnderTest.onCreate(
+            UserHandle.SYSTEM, BackupDestination.CLOUD, OperationType.RESTORE);
+        doNothing().when(mWifiManager).restoreBackupData(any());
+
+        mAgentUnderTest.restoreNewWifiConfigData(new byte[] {});
+
+        DataTypeResult loggingResult =
+            getLoggingResultForDatatype(KEY_WIFI_NEW_CONFIG, mAgentUnderTest);
+        assertNotNull(loggingResult);
+        assertEquals(loggingResult.getSuccessCount(), 1);
+    }
+
+    @Test
+    @EnableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS)
+    public void
+        restoreNewWifiConfigData_flagIsEnabled_restoreIsNotSuccessful_failureMetricsAreLogged() {
+        mAgentUnderTest.onCreate(
+            UserHandle.SYSTEM, BackupDestination.CLOUD, OperationType.RESTORE);
+        doThrow(new RuntimeException()).when(mWifiManager).restoreBackupData(any());
+
+        mAgentUnderTest.restoreNewWifiConfigData(new byte[] {});
+
+        DataTypeResult loggingResult =
+            getLoggingResultForDatatype(KEY_WIFI_NEW_CONFIG, mAgentUnderTest);
+        assertNotNull(loggingResult);
+        assertEquals(loggingResult.getFailCount(), 1);
+    }
+
+    @Test
+    @DisableFlags(com.android.server.backup.Flags.FLAG_ENABLE_METRICS_SETTINGS_BACKUP_AGENTS)
+    public void restoreNewWifiConfigData_flagIsNotEnabled_metricsAreNotLogged() {
+        mAgentUnderTest.onCreate(
+            UserHandle.SYSTEM, BackupDestination.CLOUD, OperationType.RESTORE);
+        doNothing().when(mWifiManager).restoreBackupData(any());
+
+        mAgentUnderTest.restoreNewWifiConfigData(new byte[] {});
+
+        assertNull(getLoggingResultForDatatype(KEY_WIFI_NEW_CONFIG, mAgentUnderTest));
+    }
+
     private byte[] generateBackupData(Map<String, String> keyValueData) {
         int totalBytes = 0;
         for (String key : keyValueData.keySet()) {
@@ -890,6 +1042,13 @@
                 this.numberOfSettingsPerKey.put(key, numberOfSettings);
             }
         }
+
+        int getNumberOfSettingsPerKey(String key) {
+            if (numberOfSettingsPerKey == null || !numberOfSettingsPerKey.containsKey(key)) {
+                return 0;
+            }
+            return numberOfSettingsPerKey.get(key);
+        }
     }
 
     /** The TestSettingsHelper tracks which values have been backed up and/or restored. */
@@ -944,6 +1103,14 @@
         public ContentResolver getContentResolver() {
             return mContentResolver;
         }
+
+        @Override
+        public Object getSystemService(String name) {
+            if (name.equals(Context.WIFI_SERVICE)) {
+                return mWifiManager;
+            }
+            return super.getSystemService(name);
+        }
     }
 
     /** ContentProvider which returns a set of known test values. */
diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java
index a945c33..9355bf8 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsProviderTest.java
@@ -634,7 +634,7 @@
             throws Exception {
         setSettingAndAssertSuccessfulChange(() -> {
             insertStringViaProviderApi(type, name, value, withTableRowUri);
-        }, type, name, value, UserHandle.USER_SYSTEM);
+        }, type, name, value, getContext().getUserId());
     }
 
     private void setSettingAndAssertSuccessfulChange(Runnable setCommand, final int type,
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index c655504..61f49db 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -2419,8 +2419,8 @@
             bugreportLocationInfo = new BugreportLocationInfo(readFile(in));
 
             int screenshotSize = in.readInt();
+            screenshotLocationInfo = new ScreenshotLocationInfo(null);
             for (int i = 1; i <= screenshotSize; i++) {
-                screenshotLocationInfo = new ScreenshotLocationInfo(null);
                 screenshotLocationInfo.mScreenshotFiles.add(readFile(in));
             }
 
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 3ee2db1..9adc95a 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -85,6 +85,9 @@
 filegroup {
     name: "SystemUI-tests-broken-robofiles-run",
     srcs: [
+        "tests/src/**/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt",
+        "tests/src/**/systemui/power/PowerNotificationWarningsTest.java",
+        "tests/src/**/systemui/user/domain/interactor/RefreshUsersSchedulerTest.kt",
         "tests/src/**/systemui/dreams/touch/CommunalTouchHandlerTest.java",
         "tests/src/**/systemui/shade/NotificationShadeWindowViewControllerTest.kt",
         "tests/src/**/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorSceneContainerTest.kt",
@@ -278,13 +281,14 @@
         "tests/src/**/systemui/media/controls/domain/pipeline/LegacyMediaDataManagerImplTest.kt",
         "tests/src/**/systemui/temporarydisplay/chipbar/SwipeChipbarAwayGestureHandlerTest.kt",
         "tests/src/**/systemui/qs/tiles/HotspotTileTest.java",
-        "tests/src/**/systemui/qs/tiles/dialog/InternetDialogDelegateTest.java",
+        "tests/src/**/systemui/qs/tiles/dialog/InternetDialogDelegateLegacyTest.java",
         "tests/src/**/systemui/navigationbar/NavigationBarControllerImplTest.java",
         "tests/src/**/systemui/wmshell/BubblesTest.java",
         "tests/src/**/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java",
         "tests/src/**/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java",
         "tests/src/**/systemui/shared/system/RemoteTransitionTest.java",
-        "tests/src/**/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java",
+        "tests/src/**/systemui/qs/tiles/dialog/InternetDetailsContentControllerTest.java",
+        "tests/src/**/systemui/qs/tiles/dialog/InternetDetailsContentManagerTest.kt",
         "tests/src/**/systemui/qs/external/TileLifecycleManagerTest.java",
         "tests/src/**/systemui/ScreenDecorationsTest.java",
         "tests/src/**/systemui/statusbar/policy/BatteryControllerStartableTest.java",
@@ -841,7 +845,6 @@
         "androidx.test.ext.truth",
     ],
 
-    upstream: true,
 
     instrumentation_for: "SystemUIRobo-stub",
     java_resource_dirs: ["tests/robolectric/config"],
@@ -879,7 +882,6 @@
         "androidx.test.ext.truth",
     ],
 
-    upstream: true,
 
     instrumentation_for: "SystemUIRobo-stub",
     java_resource_dirs: ["tests/robolectric/config"],
@@ -916,6 +918,7 @@
         "android.test.mock.impl",
     ],
     auto_gen_config: true,
+    team: "trendy_team_ravenwood",
     plugins: [
         "dagger2-compiler",
     ],
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 31d5bfa..51ea529 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -86,6 +86,7 @@
     <uses-permission android:name="android.permission.LOCATION_HARDWARE" />
     <uses-permission android:name="android.permission.NETWORK_FACTORY" />
     <uses-permission android:name="android.permission.SATELLITE_COMMUNICATION" />
+    <uses-permission android:name="android.permission.INTERNET" />
     <!-- Physical hardware -->
     <uses-permission android:name="android.permission.MANAGE_USB" />
     <uses-permission android:name="android.permission.CONTROL_DISPLAY_BRIGHTNESS" />
@@ -412,7 +413,8 @@
         android:defaultToDeviceProtectedStorage="true"
         android:directBootAware="true"
         tools:replace="android:appComponentFactory"
-        android:appComponentFactory=".PhoneSystemUIAppComponentFactory">
+        android:appComponentFactory=".PhoneSystemUIAppComponentFactory"
+        android:enableOnBackInvokedCallback="true">
         <!-- Keep theme in sync with SystemUIApplication.onCreate().
              Setting the theme on the application does not affect views inflated by services.
              The application theme is set again from onCreate to take effect for those views. -->
diff --git a/packages/SystemUI/OWNERS b/packages/SystemUI/OWNERS
index 795b395..c6cc9a9 100644
--- a/packages/SystemUI/OWNERS
+++ b/packages/SystemUI/OWNERS
@@ -119,4 +119,5 @@
 yeinj@google.com
 yuandizhou@google.com
 yurilin@google.com
+yuzhechen@google.com
 zakcohen@google.com
diff --git a/packages/SystemUI/aconfig/predictive_back.aconfig b/packages/SystemUI/aconfig/predictive_back.aconfig
index 46eb9e1..ee918c2 100644
--- a/packages/SystemUI/aconfig/predictive_back.aconfig
+++ b/packages/SystemUI/aconfig/predictive_back.aconfig
@@ -2,29 +2,8 @@
 container: "system"
 
 flag {
-    name: "predictive_back_sysui"
-    namespace: "systemui"
-    description: "Predictive Back Dispatching for SysUI"
-    bug: "327737297"
-}
-
-flag {
     name: "predictive_back_animate_shade"
     namespace: "systemui"
     description: "Enable Shade Animations"
     bug: "327732946"
 }
-
-flag {
-    name: "predictive_back_animate_bouncer"
-    namespace: "systemui"
-    description: "Enable Predictive Back Animation in Bouncer"
-    bug: "327733487"
-}
-
-flag {
-    name: "predictive_back_animate_dialogs"
-    namespace: "systemui"
-    description: "Enable Predictive Back Animation for SysUI dialogs"
-    bug: "327721544"
-}
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 87fb58e..7d5fd90 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -133,14 +133,6 @@
 }
 
 flag {
-    name: "notifications_footer_view_refactor"
-    namespace: "systemui"
-    description: "Enables the refactored version of the footer view in the notification shade "
-        "(containing the \"Clear all\" button). Should not bring any behavior changes"
-    bug: "293167744"
-}
-
-flag {
     name: "notifications_icon_container_refactor"
     namespace: "systemui"
     description: "Enables the refactored version of the notification icon container in StatusBar, "
@@ -457,6 +449,13 @@
 }
 
 flag {
+   name: "status_bar_signal_policy_refactor_ethernet"
+   namespace: "systemui"
+   description: "Use recommended architecture for ethernet icon in status bar"
+   bug: "291321279"
+}
+
+flag {
     name: "status_bar_swipe_over_chip"
     namespace: "systemui"
     description: "Allow users to swipe over the status bar chip to open the shade"
@@ -492,7 +491,7 @@
     name: "status_bar_notification_chips"
     namespace: "systemui"
     description: "Show promoted ongoing notifications as chips in the status bar"
-    bug: "361346412"
+    bug: "364653005"
 }
 
 flag {
@@ -675,6 +674,16 @@
 }
 
 flag {
+    name: "clipboard_overlay_multiuser"
+    namespace: "systemui"
+    description: "Fix clipboard overlay for secondary users"
+    bug: "217922018"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
+
+flag {
     name: "clipboard_shared_transitions"
     namespace: "systemui"
     description: "Show shared transitions from clipboard"
@@ -753,13 +762,6 @@
 }
 
 flag {
-    name: "screenshot_context_url"
-    namespace: "systemui"
-    description: "Include optional app-provided context URL when sharing a screenshot."
-    bug: "242791070"
-}
-
-flag {
    name: "run_fingerprint_detect_on_dismissible_keyguard"
    namespace: "systemui"
    description: "Run fingerprint detect instead of authenticate if the keyguard is dismissible."
diff --git a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginRemoteTransition.java b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginRemoteTransition.java
index 7d27a56..56d85ab 100644
--- a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginRemoteTransition.java
+++ b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginRemoteTransition.java
@@ -43,6 +43,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
  * An implementation of {@link IRemoteTransition} that accepts a {@link UIComponent} as the origin
@@ -52,6 +53,7 @@
  */
 public class OriginRemoteTransition extends IRemoteTransition.Stub {
     private static final String TAG = "OriginRemoteTransition";
+    private static final long FINISH_ANIMATION_TIMEOUT_MS = 100;
 
     private final Context mContext;
     private final boolean mIsEntry;
@@ -105,7 +107,7 @@
             IBinder mergeTarget,
             IRemoteTransitionFinishedCallback finishCallback) {
         logD("mergeAnimation - " + info);
-        mHandler.post(this::cancel);
+        cancel();
     }
 
     @Override
@@ -127,7 +129,7 @@
     @Override
     public void onTransitionConsumed(IBinder transition, boolean aborted) {
         logD("onTransitionConsumed - aborted: " + aborted);
-        mHandler.post(this::cancel);
+        cancel();
     }
 
     private void startAnimationInternal(
@@ -248,23 +250,20 @@
 
         if (mIsEntry) {
             if (!closingSurfaces.isEmpty()) {
-                tmpTransaction
-                        .setRelativeLayer(mOriginLeash, closingSurfaces.get(0), 1);
+                tmpTransaction.setRelativeLayer(mOriginLeash, closingSurfaces.get(0), 1);
             } else {
                 logW("Missing closing surface is entry transition");
             }
             if (!openingSurfaces.isEmpty()) {
-                tmpTransaction
-                        .setRelativeLayer(
-                                openingSurfaces.get(openingSurfaces.size() - 1), mOriginLeash, 1);
+                tmpTransaction.setRelativeLayer(
+                        openingSurfaces.get(openingSurfaces.size() - 1), mOriginLeash, 1);
             } else {
                 logW("Missing opening surface is entry transition");
             }
 
         } else {
             if (!openingSurfaces.isEmpty()) {
-                tmpTransaction
-                        .setRelativeLayer(mOriginLeash, openingSurfaces.get(0), 1);
+                tmpTransaction.setRelativeLayer(mOriginLeash, openingSurfaces.get(0), 1);
             } else {
                 logW("Missing opening surface is exit transition");
             }
@@ -293,12 +292,26 @@
 
     private void finishAnimation(boolean finished) {
         logD("finishAnimation: finished=" + finished);
+        OneShotRunnable finishInternalRunnable = new OneShotRunnable(this::finishInternal);
+        Runnable timeoutRunnable =
+                () -> {
+                    Log.w(TAG, "Timeout waiting for surface transaction!");
+                    finishInternalRunnable.run();
+                };
+        Runnable committedRunnable =
+                () -> {
+                    // Remove the timeout runnable.
+                    mHandler.removeCallbacks(timeoutRunnable);
+                    finishInternalRunnable.run();
+                };
         if (mAnimator == null) {
             // The transition didn't start. Ensure we apply the start transaction and report
             // finish afterwards.
             mStartTransaction
-                    .addTransactionCommittedListener(mHandler::post, this::finishInternal)
+                    .addTransactionCommittedListener(mHandler::post, committedRunnable::run)
                     .apply();
+            // Call finishInternal() anyway after the timeout.
+            mHandler.postDelayed(timeoutRunnable, FINISH_ANIMATION_TIMEOUT_MS);
             return;
         }
         mAnimator = null;
@@ -306,8 +319,10 @@
         mPlayer.onEnd(finished);
         // Detach the origin from the transition leash and report finish after it's done.
         mOriginTransaction
-                .detachFromTransitionLeash(mOrigin, mHandler::post, this::finishInternal)
+                .detachFromTransitionLeash(mOrigin, mHandler::post, committedRunnable)
                 .commit();
+        // Call finishInternal() anyway after the timeout.
+        mHandler.postDelayed(timeoutRunnable, FINISH_ANIMATION_TIMEOUT_MS);
     }
 
     private void finishInternal() {
@@ -327,11 +342,14 @@
         mFinishCallback = null;
     }
 
-    private void cancel() {
+    public void cancel() {
         logD("cancel()");
-        if (mAnimator != null) {
-            mAnimator.cancel();
-        }
+        mHandler.post(
+                () -> {
+                    if (mAnimator != null) {
+                        mAnimator.cancel();
+                    }
+                });
     }
 
     private static void logD(String msg) {
@@ -423,6 +441,23 @@
         return out;
     }
 
+    /** A {@link Runnable} that will only run once. */
+    private static class OneShotRunnable implements Runnable {
+        private final AtomicBoolean mDone = new AtomicBoolean();
+        private final Runnable mRunnable;
+
+        OneShotRunnable(Runnable runnable) {
+            this.mRunnable = runnable;
+        }
+
+        @Override
+        public void run() {
+            if (!mDone.getAndSet(true)) {
+                mRunnable.run();
+            }
+        }
+    }
+
     /**
      * An interface that represents an origin transitions.
      *
diff --git a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginTransitionSession.java b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginTransitionSession.java
index 6d6aa88..cb3dfb9 100644
--- a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginTransitionSession.java
+++ b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/OriginTransitionSession.java
@@ -43,6 +43,7 @@
 /**
  * A session object that holds origin transition states for starting an activity from an on-screen
  * UI component and smoothly transitioning back from the activity to the same UI component.
+ *
  * @hide
  */
 public class OriginTransitionSession {
@@ -143,6 +144,12 @@
                 logE("Unable to cancel origin transition!", e);
             }
         }
+        if (mEntryTransition instanceof OriginRemoteTransition) {
+            ((OriginRemoteTransition) mEntryTransition).cancel();
+        }
+        if (mExitTransition instanceof OriginRemoteTransition) {
+            ((OriginRemoteTransition) mExitTransition).cancel();
+        }
     }
 
     private boolean hasEntryTransition() {
@@ -182,6 +189,7 @@
 
     /**
      * A builder to build a {@link OriginTransitionSession}.
+     *
      * @hide
      */
     public static class Builder {
diff --git a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/ViewUIComponent.java b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/ViewUIComponent.java
index d0404ec..2e8f928 100644
--- a/packages/SystemUI/animation/lib/src/com/android/systemui/animation/ViewUIComponent.java
+++ b/packages/SystemUI/animation/lib/src/com/android/systemui/animation/ViewUIComponent.java
@@ -24,12 +24,15 @@
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.os.Build;
+import android.os.Handler;
+import android.os.Looper;
 import android.util.Log;
 import android.view.Surface;
 import android.view.SurfaceControl;
 import android.view.View;
 import android.view.ViewGroup;
-import android.view.ViewTreeObserver.OnDrawListener;
+import android.view.ViewRootImpl;
+import android.view.ViewTreeObserver;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -50,8 +53,9 @@
     private final Path mClippingPath = new Path();
     private final Outline mClippingOutline = new Outline();
 
-    private final OnDrawListener mOnDrawListener = this::postDraw;
+    private final LifecycleListener mLifecycleListener = new LifecycleListener();
     private final View mView;
+    private final Handler mMainHandler;
 
     @Nullable private SurfaceControl mSurfaceControl;
     @Nullable private Surface mSurface;
@@ -61,6 +65,7 @@
 
     public ViewUIComponent(View view) {
         mView = view;
+        mMainHandler = new Handler(Looper.getMainLooper());
     }
 
     /**
@@ -109,11 +114,11 @@
         t.reparent(mSurfaceControl, transitionLeash).show(mSurfaceControl);
 
         // Make sure view draw triggers surface draw.
-        mView.getViewTreeObserver().addOnDrawListener(mOnDrawListener);
+        mLifecycleListener.register();
 
         // Make the view invisible AFTER the surface is shown.
         t.addTransactionCommittedListener(
-                        mView::post,
+                        this::post,
                         () -> {
                             logD("Surface attached!");
                             forceDraw();
@@ -128,22 +133,29 @@
         SurfaceControl sc = mSurfaceControl;
         mSurface = null;
         mSurfaceControl = null;
-        mView.getViewTreeObserver().removeOnDrawListener(mOnDrawListener);
+        mLifecycleListener.unregister();
         // Restore view visibility
         mView.setVisibility(mVisibleOverride ? View.VISIBLE : View.INVISIBLE);
-        mView.invalidate();
         // Clean up surfaces.
         SurfaceControl.Transaction t = new SurfaceControl.Transaction();
         t.reparent(sc, null)
                 .addTransactionCommittedListener(
-                        mView::post,
+                        this::post,
                         () -> {
                             s.release();
                             sc.release();
                             executor.execute(onDone);
                         });
-        // Apply transaction AFTER the view is drawn.
-        mView.getRootSurfaceControl().applyTransactionOnDraw(t);
+        ViewRootImpl viewRoot = mView.getViewRootImpl();
+        if (viewRoot == null) {
+            t.apply();
+        } else {
+            // Apply transaction AFTER the view is drawn.
+            viewRoot.applyTransactionOnDraw(t);
+            // Request layout to force redrawing the entire view tree, so that the transaction is
+            // guaranteed to be applied.
+            viewRoot.requestLayout();
+        }
     }
 
     @Override
@@ -261,7 +273,66 @@
             return;
         }
         mDirty = true;
-        mView.post(this::draw);
+        post(this::draw);
+    }
+
+    private void post(Runnable r) {
+        if (mView.isAttachedToWindow()) {
+            mView.post(r);
+        } else {
+            // If the view is detached from window, {@code View.post()} will postpone the action
+            // until the view is attached again. However, we don't know if the view will be attached
+            // again, so we post the action to the main thread in this case. This could lead to race
+            // condition if the attachment change caused a thread switching, and it's the caller's
+            // responsibility to ensure the window attachment state doesn't change unexpectedly.
+            if (DEBUG) {
+                Log.w(TAG, mView + " is not attached. Posting action to main thread!");
+            }
+            mMainHandler.post(r);
+        }
+    }
+
+    /** A listener for monitoring view life cycles. */
+    private class LifecycleListener
+            implements ViewTreeObserver.OnDrawListener, View.OnAttachStateChangeListener {
+        private boolean mRegistered;
+
+        @Override
+        public void onDraw() {
+            // View draw should trigger surface draw.
+            postDraw();
+        }
+
+        @Override
+        public void onViewAttachedToWindow(View v) {
+            // empty
+        }
+
+        @Override
+        public void onViewDetachedFromWindow(View v) {
+            Log.w(
+                    TAG,
+                    v + " is detached from the window. Unregistering the life cycle listener ...");
+            unregister();
+        }
+
+        public void register() {
+            if (mRegistered) {
+                return;
+            }
+            mRegistered = true;
+            mView.getViewTreeObserver().addOnDrawListener(this);
+            mView.addOnAttachStateChangeListener(this);
+        }
+
+        public void unregister() {
+            if (!mRegistered) {
+                return;
+            }
+            mRegistered = false;
+            mView.getViewTreeObserver().removeOnDrawListener(this);
+            mView.removeOnAttachStateChangeListener(this);
+        }
     }
 
     /** @hide */
@@ -270,34 +341,33 @@
 
         @Override
         public Transaction setAlpha(ViewUIComponent ui, float alpha) {
-            mChanges.add(() -> ui.mView.post(() -> ui.setAlpha(alpha)));
+            mChanges.add(() -> ui.post(() -> ui.setAlpha(alpha)));
             return this;
         }
 
         @Override
         public Transaction setVisible(ViewUIComponent ui, boolean visible) {
-            mChanges.add(() -> ui.mView.post(() -> ui.setVisible(visible)));
+            mChanges.add(() -> ui.post(() -> ui.setVisible(visible)));
             return this;
         }
 
         @Override
         public Transaction setBounds(ViewUIComponent ui, Rect bounds) {
-            mChanges.add(() -> ui.mView.post(() -> ui.setBounds(bounds)));
+            mChanges.add(() -> ui.post(() -> ui.setBounds(bounds)));
             return this;
         }
 
         @Override
         public Transaction attachToTransitionLeash(
                 ViewUIComponent ui, SurfaceControl transitionLeash, int w, int h) {
-            mChanges.add(
-                    () -> ui.mView.post(() -> ui.attachToTransitionLeash(transitionLeash, w, h)));
+            mChanges.add(() -> ui.post(() -> ui.attachToTransitionLeash(transitionLeash, w, h)));
             return this;
         }
 
         @Override
         public Transaction detachFromTransitionLeash(
                 ViewUIComponent ui, Executor executor, Runnable onDone) {
-            mChanges.add(() -> ui.mView.post(() -> ui.detachFromTransitionLeash(executor, onDone)));
+            mChanges.add(() -> ui.post(() -> ui.detachFromTransitionLeash(executor, onDone)));
             return this;
         }
 
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/AnimationFeatureFlags.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/AnimationFeatureFlags.kt
deleted file mode 100644
index 1c9dabb..0000000
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/AnimationFeatureFlags.kt
+++ /dev/null
@@ -1,6 +0,0 @@
-package com.android.systemui.animation
-
-interface AnimationFeatureFlags {
-    val isPredictiveBackQsDialogAnim: Boolean
-        get() = false
-}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogTransitionAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogTransitionAnimator.kt
index 907c39d..c88c4ebb 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogTransitionAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogTransitionAnimator.kt
@@ -59,13 +59,8 @@
     private val mainExecutor: Executor,
     private val callback: Callback,
     private val interactionJankMonitor: InteractionJankMonitor,
-    private val featureFlags: AnimationFeatureFlags,
     private val transitionAnimator: TransitionAnimator =
-        TransitionAnimator(
-            mainExecutor,
-            TIMINGS,
-            INTERPOLATORS,
-        ),
+        TransitionAnimator(mainExecutor, TIMINGS, INTERPOLATORS),
     private val isForTesting: Boolean = false,
 ) {
     private companion object {
@@ -219,7 +214,7 @@
         dialog: Dialog,
         view: View,
         cuj: DialogCuj? = null,
-        animateBackgroundBoundsChange: Boolean = false
+        animateBackgroundBoundsChange: Boolean = false,
     ) {
         val controller = Controller.fromView(view, cuj)
         if (controller == null) {
@@ -245,7 +240,7 @@
     fun show(
         dialog: Dialog,
         controller: Controller,
-        animateBackgroundBoundsChange: Boolean = false
+        animateBackgroundBoundsChange: Boolean = false,
     ) {
         if (Looper.myLooper() != Looper.getMainLooper()) {
             throw IllegalStateException(
@@ -263,15 +258,14 @@
         val controller =
             animatedParent?.dialogContentWithBackground?.let {
                 Controller.fromView(it, controller.cuj)
-            }
-                ?: controller
+            } ?: controller
 
         // Make sure we don't run the launch animation from the same source twice at the same time.
         if (openedDialogs.any { it.controller.sourceIdentity == controller.sourceIdentity }) {
             Log.e(
                 TAG,
                 "Not running dialog launch animation from source as it is already expanded into a" +
-                    " dialog"
+                    " dialog",
             )
             dialog.show()
             return
@@ -288,7 +282,6 @@
                 animateBackgroundBoundsChange = animateBackgroundBoundsChange,
                 parentAnimatedDialog = animatedParent,
                 forceDisableSynchronization = isForTesting,
-                featureFlags = featureFlags,
             )
 
         openedDialogs.add(animatedDialog)
@@ -305,7 +298,7 @@
         dialog: Dialog,
         animateFrom: Dialog,
         cuj: DialogCuj? = null,
-        animateBackgroundBoundsChange: Boolean = false
+        animateBackgroundBoundsChange: Boolean = false,
     ) {
         val view =
             openedDialogs.firstOrNull { it.dialog == animateFrom }?.dialogContentWithBackground
@@ -313,7 +306,7 @@
             Log.w(
                 TAG,
                 "Showing dialog $dialog normally as the dialog it is shown from was not shown " +
-                    "using DialogTransitionAnimator"
+                    "using DialogTransitionAnimator",
             )
             dialog.show()
             return
@@ -323,7 +316,7 @@
             dialog,
             view,
             animateBackgroundBoundsChange = animateBackgroundBoundsChange,
-            cuj = cuj
+            cuj = cuj,
         )
     }
 
@@ -346,8 +339,7 @@
         val animatedDialog =
             openedDialogs.firstOrNull {
                 it.dialog.window?.decorView?.viewRootImpl == view.viewRootImpl
-            }
-                ?: return null
+            } ?: return null
         return createActivityTransitionController(animatedDialog, cujType)
     }
 
@@ -373,7 +365,7 @@
 
     private fun createActivityTransitionController(
         animatedDialog: AnimatedDialog,
-        cujType: Int? = null
+        cujType: Int? = null,
     ): ActivityTransitionAnimator.Controller? {
         // At this point, we know that the intent of the caller is to dismiss the dialog to show
         // an app, so we disable the exit animation into the source because we will never want to
@@ -440,7 +432,7 @@
             }
 
             private fun disableDialogDismiss() {
-                dialog.setDismissOverride { /* Do nothing */}
+                dialog.setDismissOverride { /* Do nothing */ }
             }
 
             private fun enableDialogDismiss() {
@@ -530,7 +522,6 @@
      * Whether synchronization should be disabled, which can be useful if we are running in a test.
      */
     private val forceDisableSynchronization: Boolean,
-    private val featureFlags: AnimationFeatureFlags,
 ) {
     /**
      * The DecorView of this dialog window.
@@ -643,8 +634,7 @@
         originalDialogBackgroundColor =
             GhostedViewTransitionAnimatorController.findGradientDrawable(background)
                 ?.color
-                ?.defaultColor
-                ?: Color.BLACK
+                ?.defaultColor ?: Color.BLACK
 
         // Make the background view invisible until we start the animation. We use the transition
         // visibility like GhostView does so that we don't mess up with the accessibility tree (see
@@ -700,7 +690,7 @@
                     oldLeft: Int,
                     oldTop: Int,
                     oldRight: Int,
-                    oldBottom: Int
+                    oldBottom: Int,
                 ) {
                     dialogContentWithBackground.removeOnLayoutChangeListener(this)
 
@@ -717,9 +707,7 @@
         // the dialog.
         dialog.setDismissOverride(this::onDialogDismissed)
 
-        if (featureFlags.isPredictiveBackQsDialogAnim) {
-            dialog.registerAnimationOnBackInvoked(targetView = dialogContentWithBackground)
-        }
+        dialog.registerAnimationOnBackInvoked(targetView = dialogContentWithBackground)
 
         // Show the dialog.
         dialog.show()
@@ -815,7 +803,7 @@
                 if (hasInstrumentedJank) {
                     interactionJankMonitor.end(controller.cuj!!.cujType)
                 }
-            }
+            },
         )
     }
 
@@ -888,14 +876,14 @@
                     onAnimationFinished(true /* instantDismiss */)
                     onDialogDismissed(this@AnimatedDialog)
                 }
-            }
+            },
         )
     }
 
     private fun startAnimation(
         isLaunching: Boolean,
         onLaunchAnimationStart: () -> Unit = {},
-        onLaunchAnimationEnd: () -> Unit = {}
+        onLaunchAnimationEnd: () -> Unit = {},
     ) {
         // Create 2 controllers to animate both the dialog and the source.
         val startController =
@@ -969,7 +957,7 @@
                 override fun onTransitionAnimationProgress(
                     state: TransitionAnimator.State,
                     progress: Float,
-                    linearProgress: Float
+                    linearProgress: Float,
                 ) {
                     startController.onTransitionAnimationProgress(state, progress, linearProgress)
 
@@ -1026,7 +1014,7 @@
             oldLeft: Int,
             oldTop: Int,
             oldRight: Int,
-            oldBottom: Int
+            oldBottom: Int,
         ) {
             // Don't animate if bounds didn't actually change.
             if (left == oldLeft && top == oldTop && right == oldRight && bottom == oldBottom) {
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt b/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt
index 35e85a0..c7d6e8a 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedDraggable.kt
@@ -34,9 +34,11 @@
 import androidx.compose.ui.input.pointer.AwaitPointerEventScope
 import androidx.compose.ui.input.pointer.PointerEvent
 import androidx.compose.ui.input.pointer.PointerEventPass
+import androidx.compose.ui.input.pointer.PointerEventType
 import androidx.compose.ui.input.pointer.PointerId
 import androidx.compose.ui.input.pointer.PointerInputChange
 import androidx.compose.ui.input.pointer.PointerInputScope
+import androidx.compose.ui.input.pointer.PointerType
 import androidx.compose.ui.input.pointer.SuspendingPointerInputModifierNode
 import androidx.compose.ui.input.pointer.changedToDownIgnoreConsumed
 import androidx.compose.ui.input.pointer.changedToUpIgnoreConsumed
@@ -52,10 +54,9 @@
 import androidx.compose.ui.platform.LocalViewConfiguration
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.Velocity
-import androidx.compose.ui.util.fastAny
-import androidx.compose.ui.util.fastSumBy
 import com.android.compose.modifiers.thenIf
 import kotlin.math.sign
+import kotlinx.coroutines.CompletableDeferred
 import kotlinx.coroutines.async
 import kotlinx.coroutines.launch
 
@@ -81,7 +82,13 @@
      * in the direction given by [sign], with the given number of [pointersDown] when the touch slop
      * was detected.
      */
-    fun onDragStarted(position: Offset, sign: Float, pointersDown: Int): Controller
+    fun onDragStarted(
+        position: Offset,
+        sign: Float,
+        pointersDown: Int,
+        // TODO(b/382665591): Make this non-nullable.
+        pointerType: PointerType?,
+    ): Controller
 
     /**
      * Whether this draggable should consume any scroll amount with the given [sign] coming from a
@@ -107,12 +114,16 @@
         /**
          * Stop the current drag with the given [velocity].
          *
+         * @param velocity the velocity of the drag when it stopped.
+         * @param awaitFling a lambda that can be used to wait for the end of the full fling, i.e.
+         *   wait for the end of the nested scroll fling or overscroll fling performed with the
+         *   unconsumed velocity *after* this call to [onDragStopped] returned.
          * @return the consumed [velocity]. Any non-consumed velocity will be dispatched to the next
          *   nested scroll connection to be consumed by any composable above in the hierarchy. If
          *   the drag was performed on this draggable directly (instead of on a nested scrollable),
          *   any remaining velocity will be used to animate the overscroll of this draggable.
          */
-        suspend fun onDragStopped(velocity: Float): Float
+        suspend fun onDragStopped(velocity: Float, awaitFling: suspend () -> Unit): Float
     }
 }
 
@@ -158,6 +169,12 @@
     CompositionLocalConsumerModifierNode,
     OrientationAware {
     private val nestedScrollDispatcher = NestedScrollDispatcher()
+    private var trackWheelScroll: SuspendingPointerInputModifierNode? = null
+        set(value) {
+            field?.let { undelegate(it) }
+            field = value?.also { delegate(it) }
+        }
+
     private var trackDownPositionDelegate: SuspendingPointerInputModifierNode? = null
         set(value) {
             field?.let { undelegate(it) }
@@ -179,9 +196,10 @@
      * This is use to track the started position of a drag started on a nested scrollable.
      */
     private var lastFirstDown: Offset? = null
+    private var lastEventWasScrollWheel: Boolean = false
 
-    /** The number of pointers down. */
-    private var pointersDownCount = 0
+    /** The pointers currently down, in order of which they were done and mapping to their type. */
+    private val pointersDown = linkedMapOf<PointerId, PointerType>()
 
     init {
         delegate(nestedScrollModifierNode(this, nestedScrollDispatcher))
@@ -208,8 +226,11 @@
         nestedScrollController?.ensureOnDragStoppedIsCalled()
         nestedScrollController = null
 
-        if (!enabled && trackDownPositionDelegate != null) {
+        if (!enabled && trackWheelScroll != null) {
+            check(trackDownPositionDelegate != null)
             check(detectDragsDelegate != null)
+
+            trackWheelScroll = null
             trackDownPositionDelegate = null
             detectDragsDelegate = null
         }
@@ -222,17 +243,22 @@
     ) {
         if (!enabled) return
 
-        if (trackDownPositionDelegate == null) {
+        if (trackWheelScroll == null) {
+            check(trackDownPositionDelegate == null)
             check(detectDragsDelegate == null)
+
+            trackWheelScroll = SuspendingPointerInputModifierNode { trackWheelScroll() }
             trackDownPositionDelegate = SuspendingPointerInputModifierNode { trackDownPosition() }
             detectDragsDelegate = SuspendingPointerInputModifierNode { detectDrags() }
         }
 
+        checkNotNull(trackWheelScroll).onPointerEvent(pointerEvent, pass, bounds)
         checkNotNull(trackDownPositionDelegate).onPointerEvent(pointerEvent, pass, bounds)
         checkNotNull(detectDragsDelegate).onPointerEvent(pointerEvent, pass, bounds)
     }
 
     override fun onCancelPointerInput() {
+        trackWheelScroll?.onCancelPointerInput()
         trackDownPositionDelegate?.onCancelPointerInput()
         detectDragsDelegate?.onCancelPointerInput()
     }
@@ -252,7 +278,9 @@
             check(down.position == lastFirstDown) {
                 "Position from detectDrags() is not the same as position in trackDownPosition()"
             }
-            check(pointersDownCount == 1) { "pointersDownCount is equal to $pointersDownCount" }
+            check(pointersDown.size == 1 && pointersDown.keys.first() == down.id) {
+                "pointersDown should only contain $down but it contains $pointersDown"
+            }
 
             var overSlop = 0f
             val onTouchSlopReached = { change: PointerInputChange, over: Float ->
@@ -277,29 +305,7 @@
                 }
             }
 
-            var drag = awaitTouchSlopOrCancellation(down.id)
-
-            // We try to pick-up the drag gesture in case the touch slop swipe was consumed by a
-            // nested scrollable child that disappeared.
-            // This was copied from http://shortn/_10L8U02IoL.
-            // TODO(b/380838584): Reuse detect(Horizontal|Vertical)DragGestures() instead.
-            while (drag == null && currentEvent.changes.fastAny { it.pressed }) {
-                var event: PointerEvent
-                do {
-                    event = awaitPointerEvent()
-                } while (
-                    event.changes.fastAny { it.isConsumed } && event.changes.fastAny { it.pressed }
-                )
-
-                // An event was not consumed and there's still a pointer in the screen.
-                if (event.changes.fastAny { it.pressed }) {
-                    // Await touch slop again, using the initial down as starting point.
-                    // For most cases this should return immediately since we probably moved
-                    // far enough from the initial down event.
-                    drag = awaitTouchSlopOrCancellation(down.id)
-                }
-            }
-
+            val drag = awaitTouchSlopOrCancellation(down.id)
             if (drag != null) {
                 velocityTracker.resetTracking()
                 val sign = drag.positionChangeIgnoreConsumed().toFloat().sign
@@ -313,8 +319,9 @@
                     }
                 }
 
-                check(pointersDownCount > 0) { "pointersDownCount is equal to $pointersDownCount" }
-                val controller = draggable.onDragStarted(down.position, sign, pointersDownCount)
+                check(pointersDown.size > 0) { "pointersDown is empty" }
+                val controller =
+                    draggable.onDragStarted(down.position, sign, pointersDown.size, drag.type)
                 if (overSlop != 0f) {
                     onDrag(controller, drag, overSlop, velocityTracker)
                 }
@@ -378,10 +385,20 @@
         // We launch in the scope of the dispatcher so that the fling is not cancelled if this node
         // is removed right after onDragStopped() is called.
         nestedScrollDispatcher.coroutineScope.launch {
-            flingWithOverscroll(velocity) { velocityFromOverscroll ->
-                flingWithNestedScroll(velocityFromOverscroll) { velocityFromNestedScroll ->
-                    controller.onDragStopped(velocityFromNestedScroll.toFloat()).toVelocity()
+            val flingCompletable = CompletableDeferred<Unit>()
+            try {
+                flingWithOverscroll(velocity) { velocityFromOverscroll ->
+                    flingWithNestedScroll(velocityFromOverscroll) { velocityFromNestedScroll ->
+                        controller
+                            .onDragStopped(
+                                velocityFromNestedScroll.toFloat(),
+                                awaitFling = { flingCompletable.await() },
+                            )
+                            .toVelocity()
+                    }
                 }
+            } finally {
+                flingCompletable.complete(Unit)
             }
         }
     }
@@ -456,22 +473,33 @@
      * ===============================
      */
 
+    private suspend fun PointerInputScope.trackWheelScroll() {
+        awaitEachGesture {
+            val event = awaitPointerEvent(pass = PointerEventPass.Initial)
+            lastEventWasScrollWheel = event.type == PointerEventType.Scroll
+        }
+    }
+
     private suspend fun PointerInputScope.trackDownPosition() {
         awaitEachGesture {
-            val down = awaitFirstDown(requireUnconsumed = false)
-            lastFirstDown = down.position
-            pointersDownCount = 1
+            try {
+                val down = awaitFirstDown(requireUnconsumed = false)
+                lastFirstDown = down.position
+                pointersDown[down.id] = down.type
 
-            do {
-                pointersDownCount +=
-                    awaitPointerEvent().changes.fastSumBy { change ->
+                do {
+                    awaitPointerEvent().changes.forEach { change ->
                         when {
-                            change.changedToDownIgnoreConsumed() -> 1
-                            change.changedToUpIgnoreConsumed() -> -1
-                            else -> 0
+                            change.changedToDownIgnoreConsumed() -> {
+                                pointersDown[change.id] = change.type
+                            }
+                            change.changedToUpIgnoreConsumed() -> pointersDown.remove(change.id)
                         }
                     }
-            } while (pointersDownCount > 0)
+                } while (pointersDown.size > 0)
+            } finally {
+                pointersDown.clear()
+            }
         }
     }
 
@@ -496,15 +524,21 @@
         }
 
         val sign = offset.sign
-        if (nestedScrollController == null && draggable.shouldConsumeNestedScroll(sign)) {
+        if (
+            nestedScrollController == null &&
+                // TODO(b/388231324): Remove this.
+                !lastEventWasScrollWheel &&
+                draggable.shouldConsumeNestedScroll(sign)
+        ) {
             val startedPosition = checkNotNull(lastFirstDown) { "lastFirstDown is not set" }
 
-            // TODO(b/382665591): Replace this by check(pointersDownCount > 0).
-            val pointersDown = pointersDownCount.coerceAtLeast(1)
+            // TODO(b/382665591): Ensure that there is at least one pointer down.
+            val pointersDownCount = pointersDown.size.coerceAtLeast(1)
+            val pointerType = pointersDown.entries.firstOrNull()?.value
             nestedScrollController =
                 NestedScrollController(
                     overscrollEffect,
-                    draggable.onDragStarted(startedPosition, sign, pointersDown),
+                    draggable.onDragStarted(startedPosition, sign, pointersDownCount, pointerType),
                 )
         }
 
@@ -536,8 +570,15 @@
         }
 
         suspend fun flingWithOverscroll(velocity: Velocity): Velocity {
-            return flingWithOverscroll(overscrollEffect, velocity) {
-                controller.onDragStopped(it.toFloat()).toVelocity()
+            val flingCompletable = CompletableDeferred<Unit>()
+            return try {
+                flingWithOverscroll(overscrollEffect, velocity) {
+                    controller
+                        .onDragStopped(it.toFloat(), awaitFling = { flingCompletable.await() })
+                        .toVelocity()
+                }
+            } finally {
+                flingCompletable.complete(Unit)
             }
         }
     }
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedScrollController.kt b/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedScrollController.kt
new file mode 100644
index 0000000..2530a4f
--- /dev/null
+++ b/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedScrollController.kt
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.compose.gesture
+
+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.input.nestedscroll.NestedScrollConnection
+import androidx.compose.ui.input.nestedscroll.NestedScrollSource
+import androidx.compose.ui.input.nestedscroll.nestedScrollModifierNode
+import androidx.compose.ui.node.DelegatingNode
+import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.platform.LocalLayoutDirection
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.Velocity
+
+/**
+ * Update [state] and disallow outer scroll after a child node consumed a non-zero scroll amount
+ * before reaching its [bounds], so that the child is overscrolled instead of letting the outer
+ * scrollable(s) consume the extra scroll.
+ *
+ * Example:
+ * ```
+ * val nestedScrollControlState = remember { NestedScrollControlState() }
+ * Column(
+ *     Modifier
+ *         // Note: Any scrollable/draggable parent should use nestedScrollControlState to
+ *         // enable/disable themselves.
+ *         .verticalScroll(
+ *             rememberScrollState(),
+ *             enabled = nestedScrollControlState.isOuterScrollAllowed,
+ *         )
+ * ) {
+ *     Column(
+ *         Modifier
+ *             .nestedScrollController(nestedScrollControlState)
+ *             .verticalScroll(rememberScrollState())
+ *     ) { ...}
+ * }
+ * ```
+ */
+fun Modifier.nestedScrollController(
+    state: NestedScrollControlState,
+    bounds: NestedScrollableBound = NestedScrollableBound.Any,
+): Modifier {
+    return this.then(NestedScrollControllerElement(state, bounds))
+}
+
+/**
+ * A state that should be used by outer scrollables to disable themselves so that nested scrollables
+ * will overscroll when reaching their bounds.
+ *
+ * @see nestedScrollController
+ */
+class NestedScrollControlState {
+    var isOuterScrollAllowed by mutableStateOf(true)
+        internal set
+}
+
+/**
+ * Specifies when to disable outer scroll after reaching the bounds of a nested scrollable.
+ *
+ * @see nestedScrollController
+ */
+enum class NestedScrollableBound {
+    /** Disable after reaching any of the scrollable bounds. */
+    Any,
+
+    /** Disable after reaching the top (left) bound when scrolling vertically (horizontally). */
+    TopLeft,
+
+    /** Disable after reaching the bottom (right) bound when scrolling vertically (horizontally). */
+    BottomRight;
+
+    companion object {
+        /**
+         * Disable after reaching the left (right) bound when scrolling horizontally in a LTR (RTL)
+         * layout.
+         */
+        val Start: NestedScrollableBound
+            @Composable
+            get() =
+                when (LocalLayoutDirection.current) {
+                    LayoutDirection.Ltr -> TopLeft
+                    LayoutDirection.Rtl -> BottomRight
+                }
+
+        /**
+         * Disable after reaching the right (left) bound when scrolling horizontally in a LTR (RTL)
+         * layout.
+         */
+        val End: NestedScrollableBound
+            @Composable
+            get() =
+                when (LocalLayoutDirection.current) {
+                    LayoutDirection.Ltr -> BottomRight
+                    LayoutDirection.Rtl -> TopLeft
+                }
+    }
+}
+
+private data class NestedScrollControllerElement(
+    private val state: NestedScrollControlState,
+    private val bounds: NestedScrollableBound,
+) : ModifierNodeElement<NestedScrollControllerNode>() {
+    override fun create(): NestedScrollControllerNode {
+        return NestedScrollControllerNode(state, bounds)
+    }
+
+    override fun update(node: NestedScrollControllerNode) {
+        node.update(state, bounds)
+    }
+}
+
+private class NestedScrollControllerNode(
+    private var state: NestedScrollControlState,
+    private var bounds: NestedScrollableBound,
+) : DelegatingNode(), NestedScrollConnection {
+    private var childrenConsumedAnyScroll = false
+
+    init {
+        delegate(nestedScrollModifierNode(this, dispatcher = null))
+    }
+
+    override fun onDetach() {
+        state.isOuterScrollAllowed = true
+    }
+
+    fun update(controller: NestedScrollControlState, bounds: NestedScrollableBound) {
+        if (controller != this.state) {
+            controller.isOuterScrollAllowed = this.state.isOuterScrollAllowed
+            this.state.isOuterScrollAllowed = true
+            this.state = controller
+        }
+
+        this.bounds = bounds
+    }
+
+    override fun onPostScroll(
+        consumed: Offset,
+        available: Offset,
+        source: NestedScrollSource,
+    ): Offset {
+        if (hasConsumedScrollInBounds(consumed.x) || hasConsumedScrollInBounds(consumed.y)) {
+            childrenConsumedAnyScroll = true
+        }
+
+        if (!childrenConsumedAnyScroll) {
+            state.isOuterScrollAllowed = true
+        } else {
+            state.isOuterScrollAllowed = false
+        }
+
+        return Offset.Zero
+    }
+
+    override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity {
+        childrenConsumedAnyScroll = false
+        state.isOuterScrollAllowed = true
+        return super.onPostFling(consumed, available)
+    }
+
+    private fun hasConsumedScrollInBounds(consumed: Float): Boolean {
+        return when {
+            consumed < 0f ->
+                bounds == NestedScrollableBound.Any || bounds == NestedScrollableBound.BottomRight
+
+            consumed > 0f ->
+                bounds == NestedScrollableBound.Any || bounds == NestedScrollableBound.TopLeft
+
+            else -> false
+        }
+    }
+}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/effect/ContentOverscrollEffect.kt b/packages/SystemUI/compose/core/src/com/android/compose/gesture/effect/ContentOverscrollEffect.kt
similarity index 75%
rename from packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/effect/ContentOverscrollEffect.kt
rename to packages/SystemUI/compose/core/src/com/android/compose/gesture/effect/ContentOverscrollEffect.kt
index 2233deb..4ee6db3 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/effect/ContentOverscrollEffect.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/gesture/effect/ContentOverscrollEffect.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.compose.animation.scene.effect
+package com.android.compose.gesture.effect
 
 import androidx.compose.animation.core.Animatable
 import androidx.compose.animation.core.AnimationSpec
@@ -118,56 +118,3 @@
         }
     }
 }
-
-/** An overscroll effect that ensures only a single fling animation is triggered. */
-internal class GestureEffect(private val delegate: ContentOverscrollEffect) :
-    ContentOverscrollEffect by delegate {
-    private var shouldFling = false
-
-    override fun applyToScroll(
-        delta: Offset,
-        source: NestedScrollSource,
-        performScroll: (Offset) -> Offset,
-    ): Offset {
-        shouldFling = true
-        return delegate.applyToScroll(delta, source, performScroll)
-    }
-
-    override suspend fun applyToFling(
-        velocity: Velocity,
-        performFling: suspend (Velocity) -> Velocity,
-    ) {
-        if (!shouldFling) {
-            performFling(velocity)
-            return
-        }
-        shouldFling = false
-        delegate.applyToFling(velocity, performFling)
-    }
-
-    suspend fun ensureApplyToFlingIsCalled() {
-        applyToFling(Velocity.Zero) { Velocity.Zero }
-    }
-}
-
-/**
- * An overscroll effect that only applies visual effects and does not interfere with the actual
- * scrolling or flinging behavior.
- */
-internal class VisualEffect(private val delegate: ContentOverscrollEffect) :
-    ContentOverscrollEffect by delegate {
-    override fun applyToScroll(
-        delta: Offset,
-        source: NestedScrollSource,
-        performScroll: (Offset) -> Offset,
-    ): Offset {
-        return performScroll(delta)
-    }
-
-    override suspend fun applyToFling(
-        velocity: Velocity,
-        performFling: suspend (Velocity) -> Velocity,
-    ) {
-        performFling(velocity)
-    }
-}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/effect/OffsetOverscrollEffect.kt b/packages/SystemUI/compose/core/src/com/android/compose/gesture/effect/OffsetOverscrollEffect.kt
similarity index 71%
rename from packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/effect/OffsetOverscrollEffect.kt
rename to packages/SystemUI/compose/core/src/com/android/compose/gesture/effect/OffsetOverscrollEffect.kt
index f459c46..d992403 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/effect/OffsetOverscrollEffect.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/gesture/effect/OffsetOverscrollEffect.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.compose.animation.scene.effect
+package com.android.compose.gesture.effect
 
 import androidx.annotation.VisibleForTesting
 import androidx.compose.animation.core.AnimationSpec
@@ -34,7 +34,6 @@
 import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.Density
 import androidx.compose.ui.unit.dp
-import com.android.compose.animation.scene.ProgressConverter
 import kotlin.math.roundToInt
 import kotlinx.coroutines.CoroutineScope
 
@@ -80,7 +79,7 @@
             )
 
         @VisibleForTesting
-        internal fun computeOffset(density: Density, overscrollDistance: Float): Int {
+        fun computeOffset(density: Density, overscrollDistance: Float): Int {
             val maxDistancePx = with(density) { MaxDistance.toPx() }
             val progress = ProgressConverter.Default.convert(overscrollDistance / maxDistancePx)
             return (progress * maxDistancePx).roundToInt()
@@ -98,3 +97,35 @@
         OffsetOverscrollEffect(orientation, animationScope, animationSpec)
     }
 }
+
+/** 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 * kotlin.math.tanh(x = it / (maxProgress * tilt))
+        }
+    }
+}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/ui/util/SpaceVectorConverter.kt b/packages/SystemUI/compose/core/src/com/android/compose/ui/util/SpaceVectorConverter.kt
similarity index 100%
rename from packages/SystemUI/compose/scene/src/com/android/compose/ui/util/SpaceVectorConverter.kt
rename to packages/SystemUI/compose/core/src/com/android/compose/ui/util/SpaceVectorConverter.kt
diff --git a/packages/SystemUI/compose/core/tests/AndroidManifest.xml b/packages/SystemUI/compose/core/tests/AndroidManifest.xml
index 28f80d4..7c721b9 100644
--- a/packages/SystemUI/compose/core/tests/AndroidManifest.xml
+++ b/packages/SystemUI/compose/core/tests/AndroidManifest.xml
@@ -15,6 +15,7 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
     package="com.android.compose.core.tests" >
 
     <application>
@@ -23,7 +24,8 @@
         <activity
             android:name="androidx.activity.ComponentActivity"
             android:theme="@android:style/Theme.DeviceDefault.DayNight"
-            android:exported="true" />
+            android:exported="true"
+            tools:replace="android:theme" />
     </application>
 
     <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt b/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt
index f98b090..f9cf495 100644
--- a/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt
+++ b/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedDraggableTest.kt
@@ -18,6 +18,8 @@
 
 import androidx.compose.foundation.ScrollState
 import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.gestures.rememberScrollableState
+import androidx.compose.foundation.gestures.scrollable
 import androidx.compose.foundation.horizontalScroll
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.fillMaxSize
@@ -33,10 +35,13 @@
 import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
 import androidx.compose.ui.input.nestedscroll.nestedScroll
 import androidx.compose.ui.input.pointer.PointerInputChange
+import androidx.compose.ui.input.pointer.PointerType
 import androidx.compose.ui.platform.LocalViewConfiguration
+import androidx.compose.ui.test.ScrollWheel
 import androidx.compose.ui.test.junit4.ComposeContentTestRule
 import androidx.compose.ui.test.junit4.createComposeRule
 import androidx.compose.ui.test.onRoot
+import androidx.compose.ui.test.performMouseInput
 import androidx.compose.ui.test.performTouchInput
 import androidx.compose.ui.test.swipeDown
 import androidx.compose.ui.test.swipeLeft
@@ -45,6 +50,9 @@
 import kotlin.math.ceil
 import kotlinx.coroutines.CompletableDeferred
 import kotlinx.coroutines.delay
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
 import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
@@ -593,6 +601,145 @@
         assertThat(effect.applyToFlingDone).isTrue()
     }
 
+    @Test
+    fun awaitFling() = runTest {
+        var flingIsDone = false
+        val draggable =
+            TestDraggable(
+                onDragStopped = { _, awaitFling ->
+                    // Start a coroutine in the background that waits for the fling to be finished.
+                    launch {
+                        awaitFling()
+                        flingIsDone = true
+                    }
+
+                    0f
+                }
+            )
+
+        val effectPostFlingCompletable = CompletableDeferred<Unit>()
+        val effect =
+            TestOverscrollEffect(
+                orientation,
+                onPostScroll = { 0f },
+                onPostFling = {
+                    effectPostFlingCompletable.await()
+                    it
+                },
+            )
+
+        val touchSlop =
+            rule.setContentWithTouchSlop {
+                Box(
+                    Modifier.fillMaxSize()
+                        .nestedDraggable(draggable, orientation, overscrollEffect = effect)
+                )
+            }
+
+        assertThat(draggable.onDragStartedCalled).isFalse()
+
+        rule.onRoot().performTouchInput {
+            down(center)
+            moveBy(touchSlop.toOffset())
+            up()
+        }
+
+        // The drag was started and stopped, but the fling is not finished yet as the overscroll
+        // effect is stuck on the effectPostFlingCompletable.
+        runCurrent()
+        rule.waitForIdle()
+        assertThat(draggable.onDragStartedCalled).isTrue()
+        assertThat(draggable.onDragStoppedCalled).isTrue()
+        assertThat(flingIsDone).isFalse()
+
+        effectPostFlingCompletable.complete(Unit)
+        runCurrent()
+        rule.waitForIdle()
+        assertThat(flingIsDone).isTrue()
+    }
+
+    @Test
+    fun pointerType() {
+        val draggable = TestDraggable()
+        val touchSlop =
+            rule.setContentWithTouchSlop {
+                Box(Modifier.fillMaxSize().nestedDraggable(draggable, orientation))
+            }
+
+        rule.onRoot().performTouchInput {
+            down(center)
+            moveBy(touchSlop.toOffset())
+        }
+
+        assertThat(draggable.onDragStartedPointerType).isEqualTo(PointerType.Touch)
+    }
+
+    @Test
+    fun pointerType_mouse() {
+        val draggable = TestDraggable()
+        val touchSlop =
+            rule.setContentWithTouchSlop {
+                Box(Modifier.fillMaxSize().nestedDraggable(draggable, orientation))
+            }
+
+        rule.onRoot().performMouseInput {
+            moveTo(center)
+            press()
+            moveBy(touchSlop.toOffset())
+            release()
+        }
+
+        assertThat(draggable.onDragStartedPointerType).isEqualTo(PointerType.Mouse)
+    }
+
+    @Test
+    fun pointersDown_clearedWhenDisabled() {
+        val draggable = TestDraggable()
+        var enabled by mutableStateOf(true)
+        rule.setContent {
+            Box(Modifier.fillMaxSize().nestedDraggable(draggable, orientation, enabled = enabled))
+        }
+
+        rule.onRoot().performTouchInput { down(center) }
+
+        enabled = false
+        rule.waitForIdle()
+
+        rule.onRoot().performTouchInput { up() }
+
+        enabled = true
+        rule.waitForIdle()
+
+        rule.onRoot().performTouchInput { down(center) }
+    }
+
+    @Test
+    // TODO(b/388231324): Remove this.
+    fun nestedScrollWithMouseWheelIsIgnored() {
+        val draggable = TestDraggable()
+        val touchSlop =
+            rule.setContentWithTouchSlop {
+                Box(
+                    Modifier.fillMaxSize()
+                        .nestedDraggable(draggable, orientation)
+                        .scrollable(rememberScrollableState { 0f }, orientation)
+                )
+            }
+
+        rule.onRoot().performMouseInput {
+            enter(center)
+            scroll(
+                touchSlop + 1f,
+                when (orientation) {
+                    Orientation.Horizontal -> ScrollWheel.Horizontal
+                    Orientation.Vertical -> ScrollWheel.Vertical
+                },
+            )
+        }
+
+        assertThat(draggable.onDragStartedCalled).isFalse()
+    }
+
     private fun ComposeContentTestRule.setContentWithTouchSlop(
         content: @Composable () -> Unit
     ): Float {
@@ -614,7 +761,10 @@
     private class TestDraggable(
         private val onDragStarted: (Offset, Float) -> Unit = { _, _ -> },
         private val onDrag: (Float) -> Float = { it },
-        private val onDragStopped: suspend (Float) -> Float = { it },
+        private val onDragStopped: suspend (Float, awaitFling: suspend () -> Unit) -> Float =
+            { velocity, _ ->
+                velocity
+            },
         private val shouldConsumeNestedScroll: (Float) -> Boolean = { true },
     ) : NestedDraggable {
         var shouldStartDrag = true
@@ -625,6 +775,7 @@
         var onDragStartedPosition = Offset.Zero
         var onDragStartedSign = 0f
         var onDragStartedPointersDown = 0
+        var onDragStartedPointerType: PointerType? = null
         var onDragDelta = 0f
 
         override fun shouldStartDrag(change: PointerInputChange): Boolean = shouldStartDrag
@@ -633,11 +784,13 @@
             position: Offset,
             sign: Float,
             pointersDown: Int,
+            pointerType: PointerType?,
         ): NestedDraggable.Controller {
             onDragStartedCalled = true
             onDragStartedPosition = position
             onDragStartedSign = sign
             onDragStartedPointersDown = pointersDown
+            onDragStartedPointerType = pointerType
             onDragDelta = 0f
 
             onDragStarted.invoke(position, sign)
@@ -648,9 +801,12 @@
                     return onDrag.invoke(delta)
                 }
 
-                override suspend fun onDragStopped(velocity: Float): Float {
+                override suspend fun onDragStopped(
+                    velocity: Float,
+                    awaitFling: suspend () -> Unit,
+                ): Float {
                     onDragStoppedCalled = true
-                    return onDragStopped.invoke(velocity)
+                    return onDragStopped.invoke(velocity, awaitFling)
                 }
             }
         }
diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedScrollControllerTest.kt b/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedScrollControllerTest.kt
new file mode 100644
index 0000000..424af33
--- /dev/null
+++ b/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedScrollControllerTest.kt
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.gesture
+
+import androidx.compose.foundation.gestures.Orientation
+import androidx.compose.foundation.gestures.rememberScrollableState
+import androidx.compose.foundation.gestures.scrollable
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+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.test.junit4.createComposeRule
+import androidx.compose.ui.test.onRoot
+import androidx.compose.ui.test.performTouchInput
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.compose.modifiers.thenIf
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class NestedScrollControllerTest {
+    @get:Rule val rule = createComposeRule()
+
+    @Test
+    fun nestedScrollController() {
+        val state = NestedScrollControlState()
+        var nestedScrollConsumesDelta = false
+        rule.setContent {
+            Box(
+                Modifier.fillMaxSize()
+                    .nestedScrollController(state)
+                    .scrollable(
+                        rememberScrollableState { if (nestedScrollConsumesDelta) it else 0f },
+                        Orientation.Vertical,
+                    )
+            )
+        }
+
+        // If the nested child does not consume scrolls, then outer scrolling is allowed.
+        assertThat(state.isOuterScrollAllowed).isTrue()
+        nestedScrollConsumesDelta = false
+        rule.onRoot().performTouchInput {
+            down(topLeft)
+            moveBy(Offset(0f, bottom))
+        }
+        assertThat(state.isOuterScrollAllowed).isTrue()
+        rule.onRoot().performTouchInput { up() }
+
+        // If the nested child consumes scrolls, then outer scrolling is disabled.
+        nestedScrollConsumesDelta = true
+        rule.onRoot().performTouchInput {
+            down(topLeft)
+            moveBy(Offset(0f, bottom))
+        }
+        assertThat(state.isOuterScrollAllowed).isFalse()
+
+        // Outer scrolling is enabled again when stopping the scroll.
+        rule.onRoot().performTouchInput { up() }
+        assertThat(state.isOuterScrollAllowed).isTrue()
+    }
+
+    @Test
+    fun nestedScrollController_detached() {
+        val state = NestedScrollControlState()
+        var composeNestedScroll by mutableStateOf(true)
+        rule.setContent {
+            val scrollableState = rememberScrollableState { it }
+            Box(
+                Modifier.fillMaxSize().thenIf(composeNestedScroll) {
+                    Modifier.nestedScrollController(state)
+                        .scrollable(scrollableState, Orientation.Vertical)
+                }
+            )
+        }
+        // The nested child consumes scrolls, so outer scrolling is disabled.
+        rule.onRoot().performTouchInput {
+            down(topLeft)
+            moveBy(Offset(0f, bottom))
+        }
+        assertThat(state.isOuterScrollAllowed).isFalse()
+
+        // Outer scrolling is enabled again when removing the controller from composition.
+        composeNestedScroll = false
+        rule.waitForIdle()
+        assertThat(state.isOuterScrollAllowed).isTrue()
+    }
+}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/effect/OffsetOverscrollEffectTest.kt b/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/effect/OffsetOverscrollEffectTest.kt
similarity index 98%
rename from packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/effect/OffsetOverscrollEffectTest.kt
rename to packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/effect/OffsetOverscrollEffectTest.kt
index da8fe30..5a3f240 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/effect/OffsetOverscrollEffectTest.kt
+++ b/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/effect/OffsetOverscrollEffectTest.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.compose.animation.scene.effect
+package com.android.compose.gesture.effect
 
 import androidx.compose.foundation.gestures.Orientation
 import androidx.compose.foundation.gestures.rememberScrollableState
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 1a8c7f8..0054a4c8 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
@@ -14,8 +14,6 @@
  * limitations under the License.
  */
 
-@file:OptIn(ExperimentalFoundationApi::class)
-
 package com.android.systemui.bouncer.ui.composable
 
 import android.app.AlertDialog
@@ -26,7 +24,6 @@
 import androidx.compose.animation.core.animateFloatAsState
 import androidx.compose.animation.core.snap
 import androidx.compose.animation.core.tween
-import androidx.compose.foundation.ExperimentalFoundationApi
 import androidx.compose.foundation.Image
 import androidx.compose.foundation.background
 import androidx.compose.foundation.combinedClickable
@@ -99,7 +96,6 @@
 import com.android.compose.windowsizeclass.LocalWindowSizeClass
 import com.android.systemui.bouncer.shared.model.BouncerActionButtonModel
 import com.android.systemui.bouncer.ui.BouncerDialogFactory
-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.BouncerSceneContentViewModel
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerSceneLayout.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerSceneLayout.kt
index eb62d33..328fec5 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerSceneLayout.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/BouncerSceneLayout.kt
@@ -16,13 +16,11 @@
 
 package com.android.systemui.bouncer.ui.composable
 
+import androidx.annotation.VisibleForTesting
 import androidx.compose.material3.windowsizeclass.WindowHeightSizeClass
 import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
 import androidx.compose.runtime.Composable
 import com.android.compose.windowsizeclass.LocalWindowSizeClass
-import com.android.systemui.bouncer.ui.helper.BouncerSceneLayout
-import com.android.systemui.bouncer.ui.helper.SizeClass
-import com.android.systemui.bouncer.ui.helper.calculateLayoutInternal
 
 /**
  * Returns the [BouncerSceneLayout] that should be used by the bouncer scene. If
@@ -57,3 +55,50 @@
         else -> error("Unsupported WindowHeightSizeClass \"$this\"")
     }
 }
+
+/** Enumerates all known adaptive layout configurations. */
+enum class BouncerSceneLayout {
+    /** The default UI with the bouncer laid out normally. */
+    STANDARD_BOUNCER,
+    /** The bouncer is displayed vertically stacked with the user switcher. */
+    BELOW_USER_SWITCHER,
+    /** The bouncer is displayed side-by-side with the user switcher or an empty space. */
+    BESIDE_USER_SWITCHER,
+    /** The bouncer is split in two with both sides shown side-by-side. */
+    SPLIT_BOUNCER,
+}
+
+/** Enumerates the supported window size classes. */
+enum class SizeClass {
+    COMPACT,
+    MEDIUM,
+    EXPANDED,
+}
+
+/**
+ * Internal version of `calculateLayout` in the System UI Compose library, extracted here to allow
+ * for testing that's not dependent on Compose.
+ */
+@VisibleForTesting
+fun calculateLayoutInternal(
+    width: SizeClass,
+    height: SizeClass,
+    isOneHandedModeSupported: Boolean,
+): BouncerSceneLayout {
+    return when (height) {
+        SizeClass.COMPACT -> BouncerSceneLayout.SPLIT_BOUNCER
+        SizeClass.MEDIUM ->
+            when (width) {
+                SizeClass.COMPACT -> BouncerSceneLayout.STANDARD_BOUNCER
+                SizeClass.MEDIUM -> BouncerSceneLayout.STANDARD_BOUNCER
+                SizeClass.EXPANDED -> BouncerSceneLayout.BESIDE_USER_SWITCHER
+            }
+        SizeClass.EXPANDED ->
+            when (width) {
+                SizeClass.COMPACT -> BouncerSceneLayout.STANDARD_BOUNCER
+                SizeClass.MEDIUM -> BouncerSceneLayout.BELOW_USER_SWITCHER
+                SizeClass.EXPANDED -> BouncerSceneLayout.BESIDE_USER_SWITCHER
+            }
+    }.takeIf { it != BouncerSceneLayout.BESIDE_USER_SWITCHER || isOneHandedModeSupported }
+        ?: BouncerSceneLayout.STANDARD_BOUNCER
+}
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 7956d02..9643f19 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
@@ -34,7 +34,6 @@
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.scene.SceneScope
 import com.android.compose.modifiers.padding
-import com.android.compose.modifiers.thenIf
 import com.android.systemui.compose.modifiers.sysuiResTag
 import com.android.systemui.keyguard.ui.composable.LockscreenLongPress
 import com.android.systemui.keyguard.ui.composable.section.AmbientIndicationSection
@@ -97,18 +96,15 @@
                             )
                         }
 
-                        Box {
+                        Box(modifier = Modifier.fillMaxWidth()) {
                             with(topAreaSection) {
                                 DefaultClockLayout(
                                     smartSpacePaddingTop = viewModel::getSmartSpacePaddingTop,
                                     isShadeLayoutWide = isShadeLayoutWide,
                                     modifier =
-                                        Modifier.thenIf(isShadeLayoutWide) {
-                                                Modifier.fillMaxWidth(0.5f)
-                                            }
-                                            .graphicsLayer {
-                                                translationX = unfoldTranslations.start
-                                            },
+                                        Modifier.fillMaxWidth().graphicsLayer {
+                                            translationX = unfoldTranslations.start
+                                        },
                                 )
                             }
                             if (isShadeLayoutWide && !isBypassEnabled) {
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 eae46e9..fb01e70 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
@@ -81,10 +81,7 @@
                     .padding(horizontal = dimensionResource(R.dimen.clock_padding_start))
                     .padding(top = { smallTopMargin })
                     .onTopPlacementChanged(onTopChanged)
-                    .burnInAware(
-                        viewModel = aodBurnInViewModel,
-                        params = burnInParams,
-                    )
+                    .burnInAware(viewModel = aodBurnInViewModel, params = burnInParams)
                     .element(smallClockElementKey),
         )
     }
@@ -114,10 +111,7 @@
             val dir = if (transition.toContent == splitShadeLargeClockScene) -1f else 1f
             val distance = dir * getClockCenteringDistance()
             val largeClock = checkNotNull(currentClock).largeClock
-            largeClock.animations.onPositionUpdated(
-                distance = distance,
-                fraction = progress,
-            )
+            largeClock.animations.onPositionUpdated(distance = distance, fraction = progress)
         }
 
         Element(key = largeClockElementKey, modifier = modifier) {
@@ -125,6 +119,16 @@
                 AndroidView(
                     factory = { context ->
                         FrameLayout(context).apply {
+                            // By default, ViewGroups like FrameLayout clip their children. Turning
+                            // off the clipping allows the child view to render outside of its
+                            // bounds - letting the step animation of the clock push the digits out
+                            // when needed.
+                            //
+                            // Note that, in Compose, clipping is actually disabled by default so
+                            // there's no need to propagate this up the composable hierarchy.
+                            clipChildren = false
+                            clipToPadding = false
+
                             ensureClockViewExists(checkNotNull(currentClock).largeClock.view)
                         }
                     },
@@ -136,8 +140,8 @@
                             .burnInAware(
                                 viewModel = aodBurnInViewModel,
                                 params = burnInParams,
-                                isClock = true
-                            )
+                                isClock = true,
+                            ),
                 )
             }
         }
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 46e0efa..183929c 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
@@ -43,6 +43,7 @@
 import androidx.compose.foundation.layout.imeAnimationTarget
 import androidx.compose.foundation.layout.offset
 import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.safeDrawing
 import androidx.compose.foundation.layout.systemBars
 import androidx.compose.foundation.layout.windowInsetsBottomHeight
 import androidx.compose.foundation.overscroll
@@ -180,10 +181,12 @@
     stackScrollView: NotificationScrollView,
     viewModel: NotificationsPlaceholderViewModel,
 ) {
+
     val isHeadsUp by viewModel.isHeadsUpOrAnimatingAway.collectAsStateWithLifecycle(false)
 
     var scrollOffset by remember { mutableFloatStateOf(0f) }
-    val minScrollOffset = -(stackScrollView.getHeadsUpInset().toFloat())
+    val headsUpInset = with(LocalDensity.current) { headsUpTopInset().toPx() }
+    val minScrollOffset = -headsUpInset
     val maxScrollOffset = 0f
 
     val scrollableState = rememberScrollableState { delta ->
@@ -241,6 +244,12 @@
     )
 }
 
+/** Y position of the HUNs at rest, when the shade is closed. */
+@Composable
+fun headsUpTopInset(): Dp =
+    WindowInsets.safeDrawing.asPaddingValues().calculateTopPadding() +
+        dimensionResource(R.dimen.heads_up_status_bar_padding)
+
 /** Adds the space where notification stack should appear in the scene. */
 @Composable
 fun ContentScope.ConstrainedNotificationStack(
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt b/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt
index 75226b3..2e1100a 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreen.kt
@@ -19,6 +19,7 @@
 import android.annotation.StringRes
 import androidx.compose.foundation.Image
 import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.Spacer
@@ -31,7 +32,6 @@
 import androidx.compose.foundation.rememberScrollState
 import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.foundation.verticalScroll
-import androidx.compose.material3.HorizontalDivider
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.material3.Surface
 import androidx.compose.material3.Text
@@ -45,6 +45,7 @@
 import androidx.compose.ui.res.dimensionResource
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.systemui.compose.modifiers.sysuiResTag
@@ -60,7 +61,11 @@
  *   the Activity/Fragment/View hosting this Composable once a result is available.
  */
 @Composable
-fun PeopleScreen(viewModel: PeopleViewModel, onResult: (PeopleViewModel.Result) -> Unit) {
+fun PeopleScreen(
+    viewModel: PeopleViewModel,
+    onResult: (PeopleViewModel.Result) -> Unit,
+    modifier: Modifier = Modifier,
+) {
     val priorityTiles by viewModel.priorityTiles.collectAsStateWithLifecycle()
     val recentTiles by viewModel.recentTiles.collectAsStateWithLifecycle()
 
@@ -74,7 +79,7 @@
         }
     }
 
-    Surface(color = MaterialTheme.colorScheme.background, modifier = Modifier.fillMaxSize()) {
+    Surface(color = MaterialTheme.colorScheme.background, modifier = modifier.fillMaxSize()) {
         if (priorityTiles.isNotEmpty() || recentTiles.isNotEmpty()) {
             PeopleScreenWithConversations(priorityTiles, recentTiles, viewModel.onTileClicked)
         } else {
@@ -88,9 +93,10 @@
     priorityTiles: List<PeopleTileViewModel>,
     recentTiles: List<PeopleTileViewModel>,
     onTileClicked: (PeopleTileViewModel) -> Unit,
+    modifier: Modifier = Modifier,
 ) {
     Column(
-        Modifier.fillMaxSize().safeDrawingPadding().sysuiResTag("top_level_with_conversations")
+        modifier.fillMaxSize().safeDrawingPadding().sysuiResTag("top_level_with_conversations")
     ) {
         Column(
             Modifier.fillMaxWidth().padding(PeopleSpacePadding),
@@ -139,28 +145,32 @@
     @StringRes headerTextResource: Int,
     tiles: List<PeopleTileViewModel>,
     onTileClicked: (PeopleTileViewModel) -> Unit,
+    modifier: Modifier = Modifier,
 ) {
-    Text(
-        stringResource(headerTextResource),
-        Modifier.padding(start = 16.dp),
-        style = MaterialTheme.typography.labelLarge,
-        color = MaterialTheme.colorScheme.primary,
-    )
+    val largeCornerRadius = dimensionResource(R.dimen.people_space_widget_radius)
+    val smallCornerRadius = 4.dp
 
-    Spacer(Modifier.height(10.dp))
+    fun topRadius(i: Int): Dp = if (i == 0) largeCornerRadius else smallCornerRadius
+    fun bottomRadius(i: Int): Dp =
+        if (i == tiles.lastIndex) largeCornerRadius else smallCornerRadius
 
-    tiles.forEachIndexed { index, tile ->
-        if (index > 0) {
-            HorizontalDivider(color = MaterialTheme.colorScheme.background, thickness = 2.dp)
-        }
+    Column(modifier, verticalArrangement = Arrangement.spacedBy(2.dp)) {
+        Text(
+            stringResource(headerTextResource),
+            Modifier.padding(start = 16.dp, bottom = 8.dp),
+            style = MaterialTheme.typography.labelLarge,
+            color = MaterialTheme.colorScheme.primary,
+        )
 
-        key(tile.key.toString()) {
-            Tile(
-                tile,
-                onTileClicked,
-                withTopCornerRadius = index == 0,
-                withBottomCornerRadius = index == tiles.lastIndex,
-            )
+        tiles.forEachIndexed { index, tile ->
+            key(tile.key.toString()) {
+                Tile(
+                    tile,
+                    onTileClicked,
+                    topCornerRadius = topRadius(index),
+                    bottomCornerRadius = bottomRadius(index),
+                )
+            }
         }
     }
 }
@@ -169,14 +179,12 @@
 private fun Tile(
     tile: PeopleTileViewModel,
     onTileClicked: (PeopleTileViewModel) -> Unit,
-    withTopCornerRadius: Boolean,
-    withBottomCornerRadius: Boolean,
+    topCornerRadius: Dp,
+    bottomCornerRadius: Dp,
+    modifier: Modifier = Modifier,
 ) {
-    val cornerRadius = dimensionResource(R.dimen.people_space_widget_radius)
-    val topCornerRadius = if (withTopCornerRadius) cornerRadius else 0.dp
-    val bottomCornerRadius = if (withBottomCornerRadius) cornerRadius else 0.dp
-
     Surface(
+        modifier,
         color = MaterialTheme.colorScheme.secondaryContainer,
         shape =
             RoundedCornerShape(
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreenEmpty.kt b/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreenEmpty.kt
index 527314d..d4dea65 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreenEmpty.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/people/ui/compose/PeopleScreenEmpty.kt
@@ -44,9 +44,9 @@
 import com.android.systemui.res.R
 
 @Composable
-internal fun PeopleScreenEmpty(onGotItClicked: () -> Unit) {
+internal fun PeopleScreenEmpty(onGotItClicked: () -> Unit, modifier: Modifier = Modifier) {
     Column(
-        Modifier.fillMaxSize().safeDrawingPadding().padding(PeopleSpacePadding),
+        modifier.fillMaxSize().safeDrawingPadding().padding(PeopleSpacePadding),
         horizontalAlignment = Alignment.CenterHorizontally,
     ) {
         Text(
@@ -74,8 +74,9 @@
 }
 
 @Composable
-private fun ExampleTile() {
+private fun ExampleTile(modifier: Modifier = Modifier) {
     Surface(
+        modifier,
         shape = RoundedCornerShape(28.dp),
         color = MaterialTheme.colorScheme.secondaryContainer,
     ) {
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 e4f4df3..9ee25c3 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
@@ -24,6 +24,7 @@
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalDensity
 import com.android.compose.animation.scene.SceneScope
 import com.android.compose.animation.scene.UserAction
 import com.android.compose.animation.scene.UserActionResult
@@ -34,6 +35,7 @@
 import com.android.systemui.lifecycle.ExclusiveActivatable
 import com.android.systemui.lifecycle.rememberViewModel
 import com.android.systemui.notifications.ui.composable.SnoozeableHeadsUpNotificationSpace
+import com.android.systemui.notifications.ui.composable.headsUpTopInset
 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
@@ -78,6 +80,8 @@
             }
         }
 
+        val headsUpInset = with(LocalDensity.current) { headsUpTopInset().toPx() }
+
         LaunchedEffect(isIdleAndNotOccluded) {
             // Wait for being Idle on this Scene, otherwise LaunchedEffect would fire too soon,
             // and another transition could override the NSSL stack bounds.
@@ -86,7 +90,7 @@
                 // and not to confuse the StackScrollAlgorithm when it displays a HUN over GONE.
                 notificationStackScrolLView.get().apply {
                     // use -headsUpInset to allow HUN translation outside bounds for snoozing
-                    setStackTop(-getHeadsUpInset().toFloat())
+                    setStackTop(-headsUpInset)
                     setStackCutoff(0f)
                 }
             }
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 c3dc84d..a6a6362 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
@@ -33,6 +33,7 @@
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.input.pointer.pointerInput
 import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalView
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import com.android.compose.animation.scene.ContentKey
 import com.android.compose.animation.scene.MutableSceneTransitionLayoutState
@@ -43,11 +44,13 @@
 import com.android.compose.animation.scene.UserAction
 import com.android.compose.animation.scene.UserActionResult
 import com.android.compose.animation.scene.observableTransitionState
+import com.android.systemui.lifecycle.rememberActivated
 import com.android.systemui.qs.ui.adapter.QSSceneAdapter
 import com.android.systemui.qs.ui.composable.QuickSettingsTheme
 import com.android.systemui.ribbon.ui.composable.BottomRightCornerRibbon
 import com.android.systemui.scene.shared.model.SceneDataSourceDelegator
 import com.android.systemui.scene.shared.model.Scenes
+import com.android.systemui.scene.ui.view.SceneJankMonitor
 import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
 import javax.inject.Provider
 
@@ -82,16 +85,38 @@
     sceneTransitions: SceneTransitions,
     dataSourceDelegator: SceneDataSourceDelegator,
     qsSceneAdapter: Provider<QSSceneAdapter>,
+    sceneJankMonitorFactory: SceneJankMonitor.Factory,
     modifier: Modifier = Modifier,
 ) {
     val coroutineScope = rememberCoroutineScope()
-    val state: MutableSceneTransitionLayoutState = remember {
-        MutableSceneTransitionLayoutState(
-            initialScene = initialSceneKey,
-            canChangeScene = { toScene -> viewModel.canChangeScene(toScene) },
-            transitions = sceneTransitions,
-        )
-    }
+
+    val view = LocalView.current
+    val sceneJankMonitor =
+        rememberActivated(traceName = "sceneJankMonitor") { sceneJankMonitorFactory.create() }
+
+    val state: MutableSceneTransitionLayoutState =
+        remember(view, sceneJankMonitor) {
+            MutableSceneTransitionLayoutState(
+                initialScene = initialSceneKey,
+                canChangeScene = { toScene -> viewModel.canChangeScene(toScene) },
+                transitions = sceneTransitions,
+                onTransitionStart = { transition ->
+                    sceneJankMonitor.onTransitionStart(
+                        view = view,
+                        from = transition.fromContent,
+                        to = transition.toContent,
+                        cuj = transition.cuj,
+                    )
+                },
+                onTransitionEnd = { transition ->
+                    sceneJankMonitor.onTransitionEnd(
+                        from = transition.fromContent,
+                        to = transition.toContent,
+                        cuj = transition.cuj,
+                    )
+                },
+            )
+        }
 
     DisposableEffect(state) {
         val dataSource = SceneTransitionLayoutDataSource(state, coroutineScope)
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 ee8535e..6d24fc16 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
@@ -3,6 +3,7 @@
 import androidx.compose.animation.core.spring
 import com.android.compose.animation.scene.TransitionKey
 import com.android.compose.animation.scene.transitions
+import com.android.internal.jank.Cuj
 import com.android.systemui.notifications.ui.composable.Notifications
 import com.android.systemui.scene.shared.model.Overlays
 import com.android.systemui.scene.shared.model.Scenes
@@ -56,14 +57,41 @@
     from(Scenes.Dream, to = Scenes.Bouncer) { dreamToBouncerTransition() }
     from(Scenes.Dream, to = Scenes.Communal) { dreamToCommunalTransition() }
     from(Scenes.Dream, to = Scenes.Gone) { dreamToGoneTransition() }
-    from(Scenes.Dream, to = Scenes.Shade) { dreamToShadeTransition() }
-    from(Scenes.Gone, to = Scenes.Shade) { goneToShadeTransition() }
-    from(Scenes.Gone, to = Scenes.Shade, key = ToSplitShade) { goneToSplitShadeTransition() }
-    from(Scenes.Gone, to = Scenes.Shade, key = SlightlyFasterShadeCollapse) {
+    from(Scenes.Dream, to = Scenes.Shade, cuj = Cuj.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE) {
+        dreamToShadeTransition()
+    }
+    from(Scenes.Gone, to = Scenes.Shade, cuj = Cuj.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE) {
+        goneToShadeTransition()
+    }
+    from(
+        Scenes.Gone,
+        to = Scenes.Shade,
+        key = ToSplitShade,
+        cuj = Cuj.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE,
+    ) {
+        goneToSplitShadeTransition()
+    }
+    from(
+        Scenes.Gone,
+        to = Scenes.Shade,
+        key = SlightlyFasterShadeCollapse,
+        cuj = Cuj.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE,
+    ) {
         goneToShadeTransition(durationScale = 0.9)
     }
-    from(Scenes.Gone, to = Scenes.QuickSettings) { goneToQuickSettingsTransition() }
-    from(Scenes.Gone, to = Scenes.QuickSettings, key = SlightlyFasterShadeCollapse) {
+    from(
+        Scenes.Gone,
+        to = Scenes.QuickSettings,
+        cuj = Cuj.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE,
+    ) {
+        goneToQuickSettingsTransition()
+    }
+    from(
+        Scenes.Gone,
+        to = Scenes.QuickSettings,
+        key = SlightlyFasterShadeCollapse,
+        cuj = Cuj.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE,
+    ) {
         goneToQuickSettingsTransition(durationScale = 0.9)
     }
 
@@ -78,49 +106,112 @@
     }
     from(Scenes.Lockscreen, to = Scenes.Communal) { lockscreenToCommunalTransition() }
     from(Scenes.Lockscreen, to = Scenes.Dream) { lockscreenToDreamTransition() }
-    from(Scenes.Lockscreen, to = Scenes.Shade) { lockscreenToShadeTransition() }
-    from(Scenes.Lockscreen, to = Scenes.Shade, key = ToSplitShade) {
+    from(Scenes.Lockscreen, to = Scenes.Shade, cuj = Cuj.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE) {
+        lockscreenToShadeTransition()
+    }
+    from(
+        Scenes.Lockscreen,
+        to = Scenes.Shade,
+        key = ToSplitShade,
+        cuj = Cuj.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE,
+    ) {
         lockscreenToSplitShadeTransition()
         sharedElement(Shade.Elements.BackgroundScrim, enabled = false)
     }
-    from(Scenes.Lockscreen, to = Scenes.Shade, key = SlightlyFasterShadeCollapse) {
+    from(
+        Scenes.Lockscreen,
+        to = Scenes.Shade,
+        key = SlightlyFasterShadeCollapse,
+        cuj = Cuj.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE,
+    ) {
         lockscreenToShadeTransition(durationScale = 0.9)
     }
-    from(Scenes.Lockscreen, to = Scenes.QuickSettings) { lockscreenToQuickSettingsTransition() }
+    from(
+        Scenes.Lockscreen,
+        to = Scenes.QuickSettings,
+        cuj = Cuj.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE,
+    ) {
+        lockscreenToQuickSettingsTransition()
+    }
     from(Scenes.Lockscreen, to = Scenes.Gone) { lockscreenToGoneTransition() }
-    from(Scenes.QuickSettings, to = Scenes.Shade) {
+    from(
+        Scenes.QuickSettings,
+        to = Scenes.Shade,
+        cuj = Cuj.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE,
+    ) {
         reversed { shadeToQuickSettingsTransition() }
         sharedElement(Notifications.Elements.HeadsUpNotificationPlaceholder, enabled = false)
     }
-    from(Scenes.Shade, to = Scenes.QuickSettings) { shadeToQuickSettingsTransition() }
-    from(Scenes.Shade, to = Scenes.Lockscreen) {
+    from(
+        Scenes.Shade,
+        to = Scenes.QuickSettings,
+        cuj = Cuj.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE,
+    ) {
+        shadeToQuickSettingsTransition()
+    }
+    from(Scenes.Shade, to = Scenes.Lockscreen, cuj = Cuj.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE) {
         reversed { lockscreenToShadeTransition() }
         sharedElement(Notifications.Elements.NotificationStackPlaceholder, enabled = false)
         sharedElement(Notifications.Elements.HeadsUpNotificationPlaceholder, enabled = false)
     }
-    from(Scenes.Shade, to = Scenes.Lockscreen, key = ToSplitShade) {
+    from(
+        Scenes.Shade,
+        to = Scenes.Lockscreen,
+        key = ToSplitShade,
+        cuj = Cuj.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE,
+    ) {
         reversed { lockscreenToSplitShadeTransition() }
     }
-    from(Scenes.Communal, to = Scenes.Shade) { communalToShadeTransition() }
+    from(Scenes.Communal, to = Scenes.Shade, cuj = Cuj.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE) {
+        communalToShadeTransition()
+    }
     from(Scenes.Communal, to = Scenes.Bouncer) { communalToBouncerTransition() }
 
     // Overlay transitions
 
-    to(Overlays.NotificationsShade) { toNotificationsShadeTransition() }
-    to(Overlays.QuickSettingsShade) { toQuickSettingsShadeTransition() }
-    from(Overlays.NotificationsShade, to = Overlays.QuickSettingsShade) {
+    to(Overlays.NotificationsShade, cuj = Cuj.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE) {
+        toNotificationsShadeTransition()
+    }
+    to(Overlays.QuickSettingsShade, cuj = Cuj.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE) {
+        toQuickSettingsShadeTransition()
+    }
+    from(
+        Overlays.NotificationsShade,
+        to = Overlays.QuickSettingsShade,
+        cuj = Cuj.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE,
+    ) {
         notificationsShadeToQuickSettingsShadeTransition()
     }
-    from(Scenes.Gone, to = Overlays.NotificationsShade, key = SlightlyFasterShadeCollapse) {
+    from(
+        Scenes.Gone,
+        to = Overlays.NotificationsShade,
+        key = SlightlyFasterShadeCollapse,
+        cuj = Cuj.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE,
+    ) {
         toNotificationsShadeTransition(durationScale = 0.9)
     }
-    from(Scenes.Gone, to = Overlays.QuickSettingsShade, key = SlightlyFasterShadeCollapse) {
+    from(
+        Scenes.Gone,
+        to = Overlays.QuickSettingsShade,
+        key = SlightlyFasterShadeCollapse,
+        cuj = Cuj.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE,
+    ) {
         toQuickSettingsShadeTransition(durationScale = 0.9)
     }
-    from(Scenes.Lockscreen, to = Overlays.NotificationsShade, key = SlightlyFasterShadeCollapse) {
+    from(
+        Scenes.Lockscreen,
+        to = Overlays.NotificationsShade,
+        key = SlightlyFasterShadeCollapse,
+        cuj = Cuj.CUJ_NOTIFICATION_SHADE_EXPAND_COLLAPSE,
+    ) {
         toNotificationsShadeTransition(durationScale = 0.9)
     }
-    from(Scenes.Lockscreen, to = Overlays.QuickSettingsShade, key = SlightlyFasterShadeCollapse) {
+    from(
+        Scenes.Lockscreen,
+        to = Overlays.QuickSettingsShade,
+        key = SlightlyFasterShadeCollapse,
+        cuj = Cuj.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE,
+    ) {
         toQuickSettingsShadeTransition(durationScale = 0.9)
     }
 }
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 cfbe667..ffdf509 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
@@ -58,7 +58,7 @@
 import com.android.compose.animation.scene.ContentScope
 import com.android.compose.animation.scene.ElementKey
 import com.android.compose.animation.scene.LowestZIndexContentPicker
-import com.android.compose.animation.scene.effect.rememberOffsetOverscrollEffect
+import com.android.compose.gesture.effect.rememberOffsetOverscrollEffect
 import com.android.compose.windowsizeclass.LocalWindowSizeClass
 import com.android.systemui.res.R
 
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt
index 1480db9..5f991fb 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt
@@ -101,20 +101,21 @@
     Column(modifier) {
         Row(
             horizontalArrangement = Arrangement.spacedBy(12.dp),
-            modifier = Modifier.fillMaxWidth(),
+            modifier = Modifier.fillMaxWidth().height(40.dp),
+            verticalAlignment = Alignment.CenterVertically,
         ) {
             state.icon?.let {
                 Icon(
                     icon = it,
                     tint = MaterialTheme.colorScheme.onSurface,
-                    modifier = Modifier.size(40.dp).padding(8.dp),
+                    modifier = Modifier.size(24.dp),
                 )
             }
             Text(
                 text = state.label,
                 style = MaterialTheme.typography.titleMedium,
                 color = MaterialTheme.colorScheme.onSurface,
-                modifier = Modifier.weight(1f).align(Alignment.CenterVertically),
+                modifier = Modifier.weight(1f),
             )
             button?.invoke()
         }
@@ -125,43 +126,47 @@
             onValueChangeFinished = onValueChangeFinished,
             enabled = state.isEnabled,
             modifier =
-                Modifier.height(40.dp).sysuiResTag(state.label).clearAndSetSemantics {
-                    if (state.isEnabled) {
-                        contentDescription = state.label
-                        state.a11yClickDescription?.let {
-                            customActions =
-                                listOf(
-                                    CustomAccessibilityAction(it) {
-                                        onIconTapped()
-                                        true
-                                    }
-                                )
-                        }
-
-                        state.a11yStateDescription?.let { stateDescription = it }
-                        progressBarRangeInfo = ProgressBarRangeInfo(state.value, state.valueRange)
-                    } else {
-                        disabled()
-                        contentDescription =
-                            state.disabledMessage?.let { "${state.label}, $it" } ?: state.label
-                    }
-                    setProgress { targetValue ->
-                        val targetDirection =
-                            when {
-                                targetValue > value -> 1
-                                targetValue < value -> -1
-                                else -> 0
+                Modifier.height(40.dp)
+                    .padding(vertical = 8.dp)
+                    .sysuiResTag(state.label)
+                    .clearAndSetSemantics {
+                        if (state.isEnabled) {
+                            contentDescription = state.label
+                            state.a11yClickDescription?.let {
+                                customActions =
+                                    listOf(
+                                        CustomAccessibilityAction(it) {
+                                            onIconTapped()
+                                            true
+                                        }
+                                    )
                             }
 
-                        val newValue =
-                            (value + targetDirection * state.a11yStep).coerceIn(
-                                state.valueRange.start,
-                                state.valueRange.endInclusive,
-                            )
-                        onValueChange(newValue)
-                        true
-                    }
-                },
+                            state.a11yStateDescription?.let { stateDescription = it }
+                            progressBarRangeInfo =
+                                ProgressBarRangeInfo(state.value, state.valueRange)
+                        } else {
+                            disabled()
+                            contentDescription =
+                                state.disabledMessage?.let { "${state.label}, $it" } ?: state.label
+                        }
+                        setProgress { targetValue ->
+                            val targetDirection =
+                                when {
+                                    targetValue > value -> 1
+                                    targetValue < value -> -1
+                                    else -> 0
+                                }
+
+                            val newValue =
+                                (value + targetDirection * state.a11yStep).coerceIn(
+                                    state.valueRange.start,
+                                    state.valueRange.endInclusive,
+                                )
+                            onValueChange(newValue)
+                            true
+                        }
+                    },
         )
     }
 }
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 6bb579d..b41c558 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
@@ -60,17 +60,12 @@
      * Stop the current drag with the given [velocity].
      *
      * @param velocity The velocity of the drag when it stopped.
-     * @param canChangeContent Whether the content can be changed as a result of this drag.
      * @return the consumed [velocity] when the animation complete
      */
-    suspend fun onStop(velocity: Float, canChangeContent: Boolean): Float
+    suspend fun onStop(velocity: Float): Float
 
-    /**
-     * Cancels the current drag.
-     *
-     * @param canChangeContent Whether the content can be changed as a result of this drag.
-     */
-    fun onCancel(canChangeContent: Boolean)
+    /** Cancels the current drag. */
+    fun onCancel()
 }
 
 internal class DraggableHandlerImpl(
@@ -295,17 +290,16 @@
         return newOffset - previousOffset
     }
 
-    override suspend fun onStop(velocity: Float, canChangeContent: Boolean): Float {
+    override suspend fun onStop(velocity: Float): Float {
         // To ensure that any ongoing animation completes gracefully and avoids an undefined state,
         // we execute the actual `onStop` logic in a non-cancellable context. This prevents the
         // coroutine from being cancelled prematurely, which could interrupt the animation.
         // TODO(b/378470603) Remove this check once NestedDraggable is used to handle drags.
-        return withContext(NonCancellable) { onStop(velocity, canChangeContent, swipeAnimation) }
+        return withContext(NonCancellable) { onStop(velocity, swipeAnimation) }
     }
 
     private suspend 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
@@ -319,35 +313,27 @@
         }
 
         val fromContent = swipeAnimation.fromContent
+        // 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 toContent = swipeAnimation.toContent
+
+        // Compute the destination content (and therefore offset) to settle in.
+        val offset = swipeAnimation.dragOffset
+        val distance = swipeAnimation.distance()
         val targetContent =
-            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 toContent = swipeAnimation.toContent
-
-                // Compute the destination content (and therefore offset) to settle in.
-                val offset = swipeAnimation.dragOffset
-                val distance = swipeAnimation.distance()
-                if (
-                    distance != DistanceUnspecified &&
-                        shouldCommitSwipe(
-                            offset = offset,
-                            distance = distance,
-                            velocity = velocity,
-                            wasCommitted = swipeAnimation.currentContent == toContent,
-                            requiresFullDistanceSwipe = swipeAnimation.requiresFullDistanceSwipe,
-                        )
-                ) {
-                    toContent
-                } else {
-                    fromContent
-                }
+            if (
+                distance != DistanceUnspecified &&
+                    shouldCommitSwipe(
+                        offset = offset,
+                        distance = distance,
+                        velocity = velocity,
+                        wasCommitted = swipeAnimation.currentContent == toContent,
+                        requiresFullDistanceSwipe = swipeAnimation.requiresFullDistanceSwipe,
+                    )
+            ) {
+                toContent
             } else {
-                // We are doing an overscroll preview animation between scenes.
-                check(fromContent == swipeAnimation.currentContent) {
-                    "canChangeContent is false but currentContent != fromContent"
-                }
                 fromContent
             }
 
@@ -423,10 +409,8 @@
         }
     }
 
-    override fun onCancel(canChangeContent: Boolean) {
-        swipeAnimation.contentTransition.coroutineScope.launch {
-            onStop(velocity = 0f, canChangeContent = canChangeContent)
-        }
+    override fun onCancel() {
+        swipeAnimation.contentTransition.coroutineScope.launch { onStop(velocity = 0f) }
     }
 }
 
@@ -519,11 +503,11 @@
         }
 
         override suspend fun OnStopScope.onStop(initialVelocity: Float): Float {
-            return dragController.onStop(velocity = initialVelocity, canChangeContent = true)
+            return dragController.onStop(velocity = initialVelocity)
         }
 
         override fun onCancel() {
-            dragController.onCancel(canChangeContent = true)
+            dragController.onCancel()
         }
 
         /**
@@ -547,9 +531,9 @@
 private object NoOpDragController : DragController {
     override fun onDrag(delta: Float) = 0f
 
-    override suspend fun onStop(velocity: Float, canChangeContent: Boolean) = 0f
+    override suspend fun onStop(velocity: Float) = 0f
 
-    override fun onCancel(canChangeContent: Boolean) {
+    override fun onCancel() {
         /* do nothing */
     }
 }
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 f5f01d4..89320f13 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
@@ -307,13 +307,13 @@
                                             velocityTracker.calculateVelocity(maxVelocity)
                                         }
                                         .toFloat(),
-                                onFling = { controller.onStop(it, canChangeContent = true) },
+                                onFling = { controller.onStop(it) },
                             )
                         },
                         onDragCancel = { controller ->
                             startFlingGesture(
                                 initialVelocity = 0f,
-                                onFling = { controller.onStop(it, canChangeContent = true) },
+                                onFling = { controller.onStop(it) },
                             )
                         },
                         swipeDetector = swipeDetector,
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 7b30a2a..de428a7 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
@@ -35,7 +35,8 @@
 import androidx.compose.ui.unit.IntOffset
 import androidx.compose.ui.unit.IntSize
 import androidx.compose.ui.unit.LayoutDirection
-import com.android.compose.animation.scene.effect.ContentOverscrollEffect
+import com.android.compose.gesture.NestedScrollableBound
+import com.android.compose.gesture.effect.ContentOverscrollEffect
 
 /**
  * [SceneTransitionLayout] is a container that automatically animates its content whenever its state
@@ -238,6 +239,18 @@
     fun Modifier.noResizeDuringTransitions(): Modifier
 
     /**
+     * Temporarily disable this content swipe actions when any scrollable below this modifier has
+     * consumed any amount of scroll delta, until the scroll gesture is finished.
+     *
+     * This can for instance be used to ensure that a scrollable list is overscrolled once it
+     * reached its bounds instead of directly starting a scene transition from the same scroll
+     * gesture.
+     */
+    fun Modifier.disableSwipesWhenScrolling(
+        bounds: NestedScrollableBound = NestedScrollableBound.Any
+    ): Modifier
+
+    /**
      * A [NestedSceneTransitionLayout] will share its elements with its ancestor STLs therefore
      * enabling sharedElement transitions between them.
      */
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 568a358..8153586 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
@@ -232,6 +232,8 @@
     canShowOverlay: (OverlayKey) -> Boolean = { true },
     canHideOverlay: (OverlayKey) -> Boolean = { true },
     canReplaceOverlay: (from: OverlayKey, to: OverlayKey) -> Boolean = { _, _ -> true },
+    onTransitionStart: (TransitionState.Transition) -> Unit = {},
+    onTransitionEnd: (TransitionState.Transition) -> Unit = {},
 ): MutableSceneTransitionLayoutState {
     return MutableSceneTransitionLayoutStateImpl(
         initialScene,
@@ -241,6 +243,8 @@
         canShowOverlay,
         canHideOverlay,
         canReplaceOverlay,
+        onTransitionStart,
+        onTransitionEnd,
     )
 }
 
@@ -252,7 +256,11 @@
     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 },
+    internal val canReplaceOverlay: (from: OverlayKey, to: OverlayKey) -> Boolean = { _, _ ->
+        true
+    },
+    private val onTransitionStart: (TransitionState.Transition) -> Unit = {},
+    private val onTransitionEnd: (TransitionState.Transition) -> Unit = {},
 ) : MutableSceneTransitionLayoutState {
     private val creationThread: Thread = Thread.currentThread()
 
@@ -367,9 +375,11 @@
             startTransitionInternal(transition, chain)
 
             // Run the transition until it is finished.
+            onTransitionStart(transition)
             transition.runInternal()
         } finally {
             finishTransition(transition)
+            onTransitionEnd(transition)
         }
     }
 
@@ -384,14 +394,10 @@
         val toContent = transition.toContent
 
         // Update the transition specs.
-        transition.transformationSpec =
-            transitions
-                .transitionSpec(fromContent, toContent, key = transition.key)
-                .transformationSpec(transition)
-        transition.previewTransformationSpec =
-            transitions
-                .transitionSpec(fromContent, toContent, key = transition.key)
-                .previewTransformationSpec(transition)
+        val spec = transitions.transitionSpec(fromContent, toContent, key = transition.key)
+        transition._cuj = spec.cuj
+        transition.transformationSpec = spec.transformationSpec(transition)
+        transition.previewTransformationSpec = spec.previewTransformationSpec(transition)
     }
 
     private fun startTransitionInternal(transition: TransitionState.Transition, chain: Boolean) {
@@ -411,9 +417,7 @@
                     if (tooManyTransitions) logTooManyTransitions()
 
                     // Force finish all transitions.
-                    while (currentTransitions.isNotEmpty()) {
-                        finishTransition(transitionStates[0] as TransitionState.Transition)
-                    }
+                    currentTransitions.fastForEach { finishTransition(it) }
 
                     // We finished all transitions, so we are now idle. We remove this state so that
                     // we end up only with the new transition after appending it.
@@ -475,46 +479,36 @@
         // Mark this transition as finished.
         finishedTransitions.add(transition)
 
-        // Keep a reference to the last transition, in case we remove all transitions and should
-        // settle to Idle.
+        if (finishedTransitions.size != transitionStates.size) {
+            // Some transitions were not finished, so we won't settle to idle.
+            return
+        }
+
+        // Keep a reference to the last transition, in case all transitions are finished and we
+        // should settle to Idle.
         val lastTransition = transitionStates.last()
 
-        // Remove all first n finished transitions.
-        var i = 0
-        val nStates = transitionStates.size
-        while (i < nStates) {
-            val t = transitionStates[i]
-            if (!finishedTransitions.contains(t)) {
-                // Stop here.
-                break
+        transitionStates.fastForEach { state ->
+            if (!finishedTransitions.contains(state)) {
+                // Some transitions were not finished, so we won't settle to idle.
+                return
             }
-
-            // Remove the transition from the set of finished transitions.
-            finishedTransitions.remove(t)
-            i++
         }
 
-        // If all transitions are finished, we are idle.
-        if (i == nStates) {
-            check(finishedTransitions.isEmpty())
-            val idle =
-                TransitionState.Idle(lastTransition.currentScene, lastTransition.currentOverlays)
-            Log.i(TAG, "all transitions finished. idle=$idle")
-            this.transitionStates = listOf(idle)
-        } else if (i > 0) {
-            this.transitionStates = transitionStates.subList(fromIndex = i, toIndex = nStates)
-        }
+        val idle = TransitionState.Idle(lastTransition.currentScene, lastTransition.currentOverlays)
+        Log.i(TAG, "all transitions finished. idle=$idle")
+        finishedTransitions.clear()
+        this.transitionStates = listOf(idle)
     }
 
     override fun snapToScene(scene: SceneKey, currentOverlays: Set<OverlayKey>) {
         checkThread()
 
         // Force finish all transitions.
-        while (currentTransitions.isNotEmpty()) {
-            finishTransition(transitionStates[0] as TransitionState.Transition)
-        }
+        currentTransitions.fastForEach { finishTransition(it) }
 
         check(transitionStates.size == 1)
+        check(currentTransitions.isEmpty())
         transitionStates = listOf(TransitionState.Idle(scene, currentOverlays))
     }
 
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 756d71c..ff8efc2 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
@@ -29,6 +29,7 @@
 import com.android.compose.animation.scene.transformation.SharedElementTransformation
 import com.android.compose.animation.scene.transformation.TransformationMatcher
 import com.android.compose.animation.scene.transformation.TransformationWithRange
+import com.android.internal.jank.Cuj.CujType
 
 /** The transitions configuration of a [SceneTransitionLayout]. */
 class SceneTransitions
@@ -111,7 +112,15 @@
     }
 
     private fun defaultTransition(from: ContentKey, to: ContentKey) =
-        TransitionSpecImpl(key = null, from, to, null, null, TransformationSpec.EmptyProvider)
+        TransitionSpecImpl(
+            key = null,
+            from,
+            to,
+            cuj = null,
+            previewTransformationSpec = null,
+            reversePreviewTransformationSpec = null,
+            TransformationSpec.EmptyProvider,
+        )
 
     companion object {
         internal val DefaultSwipeSpec =
@@ -147,6 +156,9 @@
      */
     val to: ContentKey?
 
+    /** The CUJ covered by this transition. */
+    @CujType val cuj: Int?
+
     /**
      * Return a reversed version of this [TransitionSpec] for a transition going from [to] to
      * [from].
@@ -213,6 +225,7 @@
     override val key: TransitionKey?,
     override val from: ContentKey?,
     override val to: ContentKey?,
+    override val cuj: Int?,
     private val previewTransformationSpec:
         ((TransitionState.Transition) -> TransformationSpecImpl)? =
         null,
@@ -226,6 +239,7 @@
             key = key,
             from = to,
             to = from,
+            cuj = cuj,
             previewTransformationSpec = reversePreviewTransformationSpec,
             reversePreviewTransformationSpec = previewTransformationSpec,
             transformationSpec = { transition ->
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt
index 607e4fa..ba92f9b 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SwipeAnimation.kt
@@ -315,16 +315,10 @@
         val skipAnimation =
             hasReachedTargetContent && !contentTransition.isWithinProgressRange(initialProgress)
 
-        val targetOffset =
-            if (targetContent == fromContent) {
-                0f
-            } else {
-                val distance = distance()
-                check(distance != DistanceUnspecified) {
-                    "distance is equal to $DistanceUnspecified"
-                }
-                distance
-            }
+        val distance = distance()
+        check(distance != DistanceUnspecified) { "distance is equal to $DistanceUnspecified" }
+
+        val targetOffset = if (targetContent == fromContent) 0f else 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
@@ -343,7 +337,16 @@
             }
 
         val animatable =
-            Animatable(initialOffset, OffsetVisibilityThreshold).also { offsetAnimation = it }
+            Animatable(initialOffset, OffsetVisibilityThreshold).also {
+                offsetAnimation = it
+
+                // We should animate when the progress value is between [0, 1].
+                if (distance > 0) {
+                    it.updateBounds(0f, distance)
+                } else {
+                    it.updateBounds(distance, 0f)
+                }
+            }
 
         check(isAnimatingOffset())
 
@@ -370,42 +373,26 @@
         val velocityConsumed = CompletableDeferred<Float>()
 
         offsetAnimationRunnable.complete {
-            try {
+            val result =
                 animatable.animateTo(
                     targetValue = targetOffset,
                     animationSpec = swipeSpec,
                     initialVelocity = initialVelocity,
-                ) {
-                    // Immediately stop this transition if we are bouncing on a content that
-                    // does not bounce.
-                    if (!contentTransition.isWithinProgressRange(progress)) {
-                        // We are no longer able to consume the velocity, the rest can be
-                        // consumed by another component in the hierarchy.
-                        velocityConsumed.complete(initialVelocity - velocity)
-                        throw SnapException()
-                    }
-                }
-            } catch (_: SnapException) {
-                /* Ignore. */
-            } finally {
-                if (!velocityConsumed.isCompleted) {
-                    // The animation consumed the whole available velocity
-                    velocityConsumed.complete(initialVelocity)
-                }
+                )
 
-                // Wait for overscroll to finish so that the transition is removed from the STLState
-                // only after the overscroll is done, to avoid dropping frame right when the user
-                // lifts their finger and overscroll is animated to 0.
-                overscrollCompletable?.await()
-            }
+            // We are no longer able to consume the velocity, the rest can be consumed by another
+            // component in the hierarchy.
+            velocityConsumed.complete(initialVelocity - result.endState.velocity)
+
+            // Wait for overscroll to finish so that the transition is removed from the STLState
+            // only after the overscroll is done, to avoid dropping frame right when the user
+            // lifts their finger and overscroll is animated to 0.
+            overscrollCompletable?.await()
         }
 
         return velocityConsumed.await()
     }
 
-    /** 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 ->
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 c5b3df2..3f6bce7 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
@@ -54,7 +54,7 @@
 
 /** Whether swipe should be enabled in the given [orientation]. */
 internal fun Content.shouldEnableSwipes(orientation: Orientation): Boolean {
-    if (userActions.isEmpty()) {
+    if (userActions.isEmpty() || !areSwipesAllowed()) {
         return false
     }
 
@@ -69,6 +69,10 @@
  * @return The best matching [UserActionResult], or `null` if no match is found.
  */
 internal fun Content.findActionResultBestMatch(swipe: Swipe.Resolved): UserActionResult? {
+    if (!areSwipesAllowed()) {
+        return null
+    }
+
     var bestPoints = Int.MIN_VALUE
     var bestMatch: UserActionResult? = null
     userActions.forEach { (actionSwipe, actionResult) ->
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 8794df0..998054e 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
@@ -25,7 +25,7 @@
 import androidx.compose.ui.unit.dp
 import com.android.compose.animation.scene.content.state.TransitionState
 import com.android.compose.animation.scene.transformation.Transformation
-import kotlin.math.tanh
+import com.android.internal.jank.Cuj.CujType
 
 /** Define the [transitions][SceneTransitions] to be used with a [SceneTransitionLayout]. */
 fun transitions(builder: SceneTransitionsBuilder.() -> Unit): SceneTransitions {
@@ -65,6 +65,7 @@
     fun to(
         to: ContentKey,
         key: TransitionKey? = null,
+        @CujType cuj: Int? = null,
         preview: (TransitionBuilder.() -> Unit)? = null,
         reversePreview: (TransitionBuilder.() -> Unit)? = null,
         builder: TransitionBuilder.() -> Unit = {},
@@ -91,6 +92,7 @@
         from: ContentKey,
         to: ContentKey? = null,
         key: TransitionKey? = null,
+        @CujType cuj: Int? = null,
         preview: (TransitionBuilder.() -> Unit)? = null,
         reversePreview: (TransitionBuilder.() -> Unit)? = null,
         builder: TransitionBuilder.() -> Unit = {},
@@ -147,6 +149,9 @@
      */
     var swipeSpec: SpringSpec<Float>?
 
+    /** The CUJ associated to this transitions. */
+    @CujType var cuj: Int?
+
     /**
      * Define a timestamp-based range for the transformations inside [builder].
      *
@@ -476,35 +481,3 @@
     /** Apply a [transformation] to the element(s) matching [matcher]. */
     fun transformation(matcher: ElementMatcher, transformation: Transformation.Factory)
 }
-
-/** 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 a164996..7ca5215 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
@@ -37,6 +37,7 @@
 import com.android.compose.animation.scene.transformation.TransformationMatcher
 import com.android.compose.animation.scene.transformation.TransformationRange
 import com.android.compose.animation.scene.transformation.Translate
+import com.android.internal.jank.Cuj.CujType
 
 internal fun transitionsImpl(builder: SceneTransitionsBuilder.() -> Unit): SceneTransitions {
     val impl = SceneTransitionsBuilderImpl().apply(builder)
@@ -52,28 +53,47 @@
     override fun to(
         to: ContentKey,
         key: TransitionKey?,
+        @CujType cuj: Int?,
         preview: (TransitionBuilder.() -> Unit)?,
         reversePreview: (TransitionBuilder.() -> Unit)?,
         builder: TransitionBuilder.() -> Unit,
     ) {
-        transition(from = null, to = to, key = key, preview, reversePreview, builder)
+        transition(
+            from = null,
+            to = to,
+            key = key,
+            cuj = cuj,
+            preview = preview,
+            reversePreview = reversePreview,
+            builder = builder,
+        )
     }
 
     override fun from(
         from: ContentKey,
         to: ContentKey?,
         key: TransitionKey?,
+        @CujType cuj: Int?,
         preview: (TransitionBuilder.() -> Unit)?,
         reversePreview: (TransitionBuilder.() -> Unit)?,
         builder: TransitionBuilder.() -> Unit,
     ) {
-        transition(from = from, to = to, key = key, preview, reversePreview, builder)
+        transition(
+            from = from,
+            to = to,
+            key = key,
+            cuj = cuj,
+            preview = preview,
+            reversePreview = reversePreview,
+            builder = builder,
+        )
     }
 
     private fun transition(
         from: ContentKey?,
         to: ContentKey?,
         key: TransitionKey?,
+        @CujType cuj: Int?,
         preview: (TransitionBuilder.() -> Unit)?,
         reversePreview: (TransitionBuilder.() -> Unit)?,
         builder: TransitionBuilder.() -> Unit,
@@ -93,9 +113,10 @@
 
         val spec =
             TransitionSpecImpl(
-                key,
-                from,
-                to,
+                key = key,
+                from = from,
+                to = to,
+                cuj = cuj,
                 previewTransformationSpec = preview?.let { { t -> transformationSpec(t, it) } },
                 reversePreviewTransformationSpec =
                     reversePreview?.let { { t -> transformationSpec(t, it) } },
@@ -190,6 +211,7 @@
     override var spec: AnimationSpec<Float> = spring(stiffness = Spring.StiffnessLow)
     override var swipeSpec: SpringSpec<Float>? = null
     override var distance: UserActionDistance? = null
+    override var cuj: Int? = null
     private val durationMillis: Int by lazy {
         val spec = spec
         if (spec !is DurationBasedAnimationSpec) {
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 8c5a727..59b4a09 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
@@ -53,10 +53,13 @@
 import com.android.compose.animation.scene.ValueKey
 import com.android.compose.animation.scene.animateSharedValueAsState
 import com.android.compose.animation.scene.effect.GestureEffect
-import com.android.compose.animation.scene.effect.OffsetOverscrollEffect
 import com.android.compose.animation.scene.effect.VisualEffect
 import com.android.compose.animation.scene.element
 import com.android.compose.animation.scene.modifiers.noResizeDuringTransitions
+import com.android.compose.gesture.NestedScrollControlState
+import com.android.compose.gesture.NestedScrollableBound
+import com.android.compose.gesture.effect.OffsetOverscrollEffect
+import com.android.compose.gesture.nestedScrollController
 import com.android.compose.modifiers.thenIf
 import com.android.compose.ui.graphics.ContainerState
 import com.android.compose.ui.graphics.container
@@ -70,7 +73,8 @@
     actions: Map<UserAction.Resolved, UserActionResult>,
     zIndex: Float,
 ) {
-    internal val scope = ContentScopeImpl(layoutImpl, content = this)
+    private val nestedScrollControlState = NestedScrollControlState()
+    internal val scope = ContentScopeImpl(layoutImpl, content = this, nestedScrollControlState)
     val containerState = ContainerState()
 
     var content by mutableStateOf(content)
@@ -101,11 +105,14 @@
             scope.content()
         }
     }
+
+    fun areSwipesAllowed(): Boolean = nestedScrollControlState.isOuterScrollAllowed
 }
 
 internal class ContentScopeImpl(
     private val layoutImpl: SceneTransitionLayoutImpl,
     private val content: Content,
+    private val nestedScrollControlState: NestedScrollControlState,
 ) : ContentScope, ElementStateScope by layoutImpl.elementStateScope {
     override val contentKey: ContentKey
         get() = content.key
@@ -176,6 +183,10 @@
         return noResizeDuringTransitions(layoutState = layoutImpl.state)
     }
 
+    override fun Modifier.disableSwipesWhenScrolling(bounds: NestedScrollableBound): Modifier {
+        return nestedScrollController(nestedScrollControlState, bounds)
+    }
+
     @Composable
     override fun NestedSceneTransitionLayout(
         state: SceneTransitionLayoutState,
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 e7ca511..712af56 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
@@ -32,6 +32,7 @@
 import com.android.compose.animation.scene.TransformationSpec
 import com.android.compose.animation.scene.TransformationSpecImpl
 import com.android.compose.animation.scene.TransitionKey
+import com.android.internal.jank.Cuj.CujType
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.launch
@@ -237,6 +238,11 @@
         /** Whether user input is currently driving the transition. */
         abstract val isUserInputOngoing: Boolean
 
+        /** The CUJ covered by this transition. */
+        @CujType
+        val cuj: Int?
+            get() = _cuj
+
         /**
          * 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
@@ -251,13 +257,15 @@
         internal open val isInPreviewStage: Boolean = false
 
         /**
-         * The current [TransformationSpecImpl] associated to this transition.
+         * The current [TransformationSpecImpl] and other values associated to this transition from
+         * the spec.
          *
          * 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
+        internal var _cuj: Int? = null
 
         /**
          * An animatable that animates from 1f to 0f. This will be used to nicely animate the sudden
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/effect/GestureEffect.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/effect/GestureEffect.kt
new file mode 100644
index 0000000..2db45aa
--- /dev/null
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/effect/GestureEffect.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.effect
+
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.input.nestedscroll.NestedScrollSource
+import androidx.compose.ui.unit.Velocity
+import com.android.compose.gesture.effect.ContentOverscrollEffect
+
+/** An overscroll effect that ensures only a single fling animation is triggered. */
+internal class GestureEffect(private val delegate: ContentOverscrollEffect) :
+    ContentOverscrollEffect by delegate {
+    private var shouldFling = false
+
+    override fun applyToScroll(
+        delta: Offset,
+        source: NestedScrollSource,
+        performScroll: (Offset) -> Offset,
+    ): Offset {
+        shouldFling = true
+        return delegate.applyToScroll(delta, source, performScroll)
+    }
+
+    override suspend fun applyToFling(
+        velocity: Velocity,
+        performFling: suspend (Velocity) -> Velocity,
+    ) {
+        if (!shouldFling) {
+            performFling(velocity)
+            return
+        }
+        shouldFling = false
+        delegate.applyToFling(velocity, performFling)
+    }
+
+    suspend fun ensureApplyToFlingIsCalled() {
+        applyToFling(Velocity.Zero) { Velocity.Zero }
+    }
+}
+
+/**
+ * An overscroll effect that only applies visual effects and does not interfere with the actual
+ * scrolling or flinging behavior.
+ */
+internal class VisualEffect(private val delegate: ContentOverscrollEffect) :
+    ContentOverscrollEffect by delegate {
+    override fun applyToScroll(
+        delta: Offset,
+        source: NestedScrollSource,
+        performScroll: (Offset) -> Offset,
+    ): Offset {
+        return performScroll(delta)
+    }
+
+    override suspend fun applyToFling(
+        velocity: Velocity,
+        performFling: suspend (Velocity) -> Velocity,
+    ) {
+        performFling(velocity)
+    }
+}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ContentTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ContentTest.kt
new file mode 100644
index 0000000..06a9735
--- /dev/null
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ContentTest.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 androidx.compose.foundation.gestures.rememberScrollableState
+import androidx.compose.foundation.gestures.scrollable
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.compose.ui.test.onRoot
+import androidx.compose.ui.test.performTouchInput
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.compose.animation.scene.TestScenes.SceneA
+import com.google.common.truth.Truth.assertThat
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class ContentTest {
+    @get:Rule val rule = createComposeRule()
+
+    @Test
+    fun disableSwipesWhenScrolling() {
+        lateinit var layoutImpl: SceneTransitionLayoutImpl
+        rule.setContent {
+            SceneTransitionLayoutForTesting(
+                remember { MutableSceneTransitionLayoutState(SceneA) },
+                onLayoutImpl = { layoutImpl = it },
+            ) {
+                scene(SceneA) {
+                    Box(
+                        Modifier.fillMaxSize()
+                            .disableSwipesWhenScrolling()
+                            .scrollable(rememberScrollableState { it }, Orientation.Vertical)
+                    )
+                }
+            }
+        }
+
+        val content = layoutImpl.content(SceneA)
+        assertThat(content.areSwipesAllowed()).isTrue()
+        rule.onRoot().performTouchInput {
+            down(topLeft)
+            moveBy(bottomLeft)
+        }
+
+        assertThat(content.areSwipesAllowed()).isFalse()
+        rule.onRoot().performTouchInput { up() }
+        assertThat(content.areSwipesAllowed()).isTrue()
+    }
+}
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 5a35d11..dbac62f 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
@@ -247,32 +247,26 @@
 
         suspend fun DragController.onDragStoppedAnimateNow(
             velocity: Float,
-            canChangeScene: Boolean = true,
             onAnimationStart: () -> Unit,
             onAnimationEnd: (Float) -> Unit,
         ) {
-            val velocityConsumed = onDragStoppedAnimateLater(velocity, canChangeScene)
+            val velocityConsumed = onDragStoppedAnimateLater(velocity)
             onAnimationStart()
             onAnimationEnd(velocityConsumed.await())
         }
 
         suspend fun DragController.onDragStoppedAnimateNow(
             velocity: Float,
-            canChangeScene: Boolean = true,
             onAnimationStart: () -> Unit,
         ) =
             onDragStoppedAnimateNow(
                 velocity = velocity,
-                canChangeScene = canChangeScene,
                 onAnimationStart = onAnimationStart,
                 onAnimationEnd = {},
             )
 
-        fun DragController.onDragStoppedAnimateLater(
-            velocity: Float,
-            canChangeScene: Boolean = true,
-        ): Deferred<Float> {
-            val velocityConsumed = testScope.async { onStop(velocity, canChangeScene) }
+        fun DragController.onDragStoppedAnimateLater(velocity: Float): Deferred<Float> {
+            val velocityConsumed = testScope.async { onStop(velocity) }
             testScope.testScheduler.runCurrent()
             return velocityConsumed
         }
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 6769032..53495be 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
@@ -70,8 +70,8 @@
 import com.android.compose.animation.scene.TestScenes.SceneA
 import com.android.compose.animation.scene.TestScenes.SceneB
 import com.android.compose.animation.scene.TestScenes.SceneC
-import com.android.compose.animation.scene.effect.OffsetOverscrollEffect
 import com.android.compose.animation.scene.subjects.assertThat
+import com.android.compose.gesture.effect.OffsetOverscrollEffect
 import com.android.compose.test.assertSizeIsEqualTo
 import com.android.compose.test.setContentAndCreateMainScope
 import com.android.compose.test.transition
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 4153350..5c6f91b 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
@@ -72,12 +72,12 @@
             return delta
         }
 
-        override suspend fun onStop(velocity: Float, canChangeContent: Boolean): Float {
+        override suspend fun onStop(velocity: Float): Float {
             onStop.invoke(velocity)
             return velocity
         }
 
-        override fun onCancel(canChangeContent: Boolean) {
+        override fun onCancel() {
             error("MultiPointerDraggable never calls onCancel()")
         }
     }
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 d1bd52b..f3be5e4 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
@@ -198,23 +198,30 @@
         assertThat(state.currentTransitions).containsExactly(aToB, bToC).inOrder()
 
         // C => A. This should automatically call freezeAndAnimateToCurrentState() on bToC.
-        state.startTransitionImmediately(animationScope = backgroundScope, cToA)
+        val cToAJob = 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.
-        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.
+        // Mark aToB and bToC as finished. The list of current transitions does not change because
+        // cToA is still running.
         aToB.finish()
         aToBJob.join()
+        assertThat(state.finishedTransitions).containsExactly(aToB)
+        assertThat(state.currentTransitions).containsExactly(aToB, bToC, cToA).inOrder()
+
+        bToC.finish()
+        bToCJob.join()
+        assertThat(state.finishedTransitions).containsExactly(aToB, bToC)
+        assertThat(state.currentTransitions).containsExactly(aToB, bToC, cToA).inOrder()
+
+        // Mark cToA as finished. This should clear all transitions and settle to idle.
+        cToA.finish()
+        cToAJob.join()
         assertThat(state.finishedTransitions).isEmpty()
-        assertThat(state.currentTransitions).containsExactly(cToA).inOrder()
+        assertThat(state.currentTransitions).isEmpty()
+        assertThat(state.transitionState).isIdle()
+        assertThat(state.transitionState).hasCurrentScene(SceneA)
     }
 
     @Test
@@ -473,4 +480,77 @@
                     "SceneKey(debugName=SceneB)"
             )
     }
+
+    @Test
+    fun snapToScene_multipleTransitions() = runMonotonicClockTest {
+        val state = MutableSceneTransitionLayoutState(SceneA)
+        state.startTransitionImmediately(this, transition(SceneA, SceneB))
+        state.startTransitionImmediately(this, transition(SceneB, SceneC))
+        state.snapToScene(SceneC)
+
+        assertThat(state.transitionState).isIdle()
+        assertThat(state.transitionState).hasCurrentScene(SceneC)
+    }
+
+    @Test
+    fun trackTransitionCujs() = runTest {
+        val started = mutableSetOf<TransitionState.Transition>()
+        val finished = mutableSetOf<TransitionState.Transition>()
+        val cujWhenStarting = mutableMapOf<TransitionState.Transition, Int?>()
+        val state =
+            MutableSceneTransitionLayoutState(
+                SceneA,
+                transitions {
+                    // A <=> B.
+                    from(SceneA, to = SceneB, cuj = 1)
+
+                    // A <=> C.
+                    from(SceneA, to = SceneC, cuj = 2)
+                    from(SceneC, to = SceneA, cuj = 3)
+                },
+                onTransitionStart = { transition ->
+                    started.add(transition)
+                    cujWhenStarting[transition] = transition.cuj
+                },
+                onTransitionEnd = { finished.add(it) },
+            )
+
+        val aToB = transition(SceneA, SceneB)
+        val bToA = transition(SceneB, SceneA)
+        val aToC = transition(SceneA, SceneC)
+        val cToA = transition(SceneC, SceneA)
+
+        val animationScope = this
+        state.startTransitionImmediately(animationScope, aToB)
+        assertThat(started).containsExactly(aToB)
+        assertThat(finished).isEmpty()
+
+        state.startTransitionImmediately(animationScope, bToA)
+        assertThat(started).containsExactly(aToB, bToA)
+        assertThat(finished).isEmpty()
+
+        aToB.finish()
+        runCurrent()
+        assertThat(finished).containsExactly(aToB)
+
+        state.startTransitionImmediately(animationScope, aToC)
+        assertThat(started).containsExactly(aToB, bToA, aToC)
+        assertThat(finished).containsExactly(aToB)
+
+        state.startTransitionImmediately(animationScope, cToA)
+        assertThat(started).containsExactly(aToB, bToA, aToC, cToA)
+        assertThat(finished).containsExactly(aToB)
+
+        bToA.finish()
+        aToC.finish()
+        cToA.finish()
+        runCurrent()
+        assertThat(started).containsExactly(aToB, bToA, aToC, cToA)
+        assertThat(finished).containsExactly(aToB, bToA, aToC, cToA)
+
+        assertThat(cujWhenStarting[aToB]).isEqualTo(1)
+        assertThat(cujWhenStarting[bToA]).isEqualTo(1)
+        assertThat(cujWhenStarting[aToC]).isEqualTo(2)
+        assertThat(cujWhenStarting[cToA]).isEqualTo(3)
+    }
 }
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 fdbd0f6..e580e3c 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
@@ -21,6 +21,7 @@
 import androidx.compose.animation.core.tween
 import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.offset
 import androidx.compose.foundation.layout.size
@@ -33,6 +34,7 @@
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalViewConfiguration
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.test.SemanticsNodeInteraction
 import androidx.compose.ui.test.assertHeightIsEqualTo
@@ -43,6 +45,9 @@
 import androidx.compose.ui.test.onChild
 import androidx.compose.ui.test.onNodeWithTag
 import androidx.compose.ui.test.onNodeWithText
+import androidx.compose.ui.test.onRoot
+import androidx.compose.ui.test.performTouchInput
+import androidx.compose.ui.test.swipeDown
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.DpOffset
 import androidx.compose.ui.unit.IntOffset
@@ -379,8 +384,8 @@
         assertThat(transition).hasProgress(0.5f)
         rule.waitForIdle()
 
-        // B and C are composed.
-        rule.onNodeWithTag("aRoot").assertDoesNotExist()
+        // A, B and C are still composed given that B => C is not finished yet.
+        rule.onNodeWithTag("aRoot").assertExists()
         rule.onNodeWithTag("bRoot").assertExists()
         rule.onNodeWithTag("cRoot").assertExists()
 
@@ -469,4 +474,41 @@
 
         assertThat(layoutImpl.overlaysOrNullForTest()).isNull()
     }
+
+    @Test
+    fun transitionProgressBoundedBetween0And1() {
+        val layoutWidth = 200.dp
+        val layoutHeight = 400.dp
+
+        // The draggable touch slop, i.e. the min px distance a touch pointer must move before it is
+        // detected as a drag event.
+        var touchSlop = 0f
+        val state = rule.runOnUiThread { MutableSceneTransitionLayoutState(initialScene = SceneA) }
+        rule.setContent {
+            touchSlop = LocalViewConfiguration.current.touchSlop
+            SceneTransitionLayout(state, Modifier.size(layoutWidth, layoutHeight)) {
+                scene(SceneA, userActions = mapOf(Swipe.Down to SceneB)) {
+                    Spacer(Modifier.fillMaxSize())
+                }
+                scene(SceneB) { Spacer(Modifier.fillMaxSize()) }
+            }
+        }
+        assertThat(state.transitionState).isIdle()
+
+        rule.mainClock.autoAdvance = false
+
+        // Swipe the verticalSwipeDistance.
+        rule.onRoot().performTouchInput {
+            swipeDown(endY = bottom + touchSlop, durationMillis = 50)
+        }
+
+        rule.mainClock.advanceTimeBy(16)
+        val transition = assertThat(state.transitionState).isSceneTransition()
+        assertThat(transition).isNotNull()
+        assertThat(transition).hasProgress(1f, tolerance = 0.01f)
+
+        rule.mainClock.advanceTimeBy(16)
+        // Fling animation, we are overscrolling now. Progress should always be between [0, 1].
+        assertThat(transition).hasProgress(1f)
+    }
 }
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockDesign.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockDesign.kt
deleted file mode 100644
index 15373d3..0000000
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockDesign.kt
+++ /dev/null
@@ -1,282 +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.shared.clocks
-
-import android.graphics.Point
-import android.view.animation.Interpolator
-import com.android.app.animation.Interpolators
-import com.android.internal.annotations.Keep
-import com.android.systemui.monet.Style as MonetStyle
-import com.android.systemui.shared.clocks.view.HorizontalAlignment
-import com.android.systemui.shared.clocks.view.VerticalAlignment
-
-/** Data format for a simple asset-defined clock */
-@Keep
-data class ClockDesign(
-    val id: String,
-    val name: String? = null,
-    val description: String? = null,
-    val thumbnail: String? = null,
-    val large: ClockFace? = null,
-    val small: ClockFace? = null,
-    @MonetStyle.Type val colorPalette: Int? = null,
-)
-
-/** Describes a clock using layers */
-@Keep
-data class ClockFace(
-    val layers: List<ClockLayer> = listOf<ClockLayer>(),
-    val layerBounds: LayerBounds = LayerBounds.FIT,
-    val wallpaper: String? = null,
-    val faceLayout: DigitalFaceLayout? = null,
-    val pickerScale: ClockFaceScaleInPicker? = ClockFaceScaleInPicker(1.0f, 1.0f),
-)
-
-@Keep data class ClockFaceScaleInPicker(val scaleX: Float, val scaleY: Float)
-
-/** Base Type for a Clock Layer */
-@Keep
-interface ClockLayer {
-    /** Override of face LayerBounds setting for this layer */
-    val layerBounds: LayerBounds?
-}
-
-/** Clock layer that renders a static asset */
-@Keep
-data class AssetLayer(
-    /** Asset to render in this layer */
-    val asset: AssetReference,
-    override val layerBounds: LayerBounds? = null,
-) : ClockLayer
-
-/** Clock layer that renders the time (or a component of it) using numerals */
-@Keep
-data class DigitalHandLayer(
-    /** See SimpleDateFormat for timespec format info */
-    val timespec: DigitalTimespec,
-    val style: TextStyle,
-    // adoStyle concrete type must match style,
-    // cause styles will transition between style and aodStyle
-    val aodStyle: TextStyle?,
-    val timer: Int? = null,
-    override val layerBounds: LayerBounds? = null,
-    var faceLayout: DigitalFaceLayout? = null,
-    // we pass 12-hour format from json, which will be converted to 24-hour format in codes
-    val dateTimeFormat: String,
-    val alignment: DigitalAlignment?,
-    // ratio of margins to measured size, currently used for handwritten clocks
-    val marginRatio: DigitalMarginRatio? = DigitalMarginRatio(),
-) : ClockLayer
-
-/** Clock layer that renders the time (or a component of it) using numerals */
-@Keep
-data class ComposedDigitalHandLayer(
-    val customizedView: String? = null,
-    /** See SimpleDateFormat for timespec format info */
-    val digitalLayers: List<DigitalHandLayer> = listOf<DigitalHandLayer>(),
-    override val layerBounds: LayerBounds? = null,
-) : ClockLayer
-
-@Keep
-data class DigitalAlignment(
-    val horizontalAlignment: HorizontalAlignment?,
-    val verticalAlignment: VerticalAlignment?,
-)
-
-@Keep
-data class DigitalMarginRatio(
-    val left: Float = 0F,
-    val top: Float = 0F,
-    val right: Float = 0F,
-    val bottom: Float = 0F,
-)
-
-/** Clock layer which renders a component of the time using an analog hand */
-@Keep
-data class AnalogHandLayer(
-    val timespec: AnalogTimespec,
-    val tickMode: AnalogTickMode,
-    val asset: AssetReference,
-    val timer: Int? = null,
-    val clock_pivot: Point = Point(0, 0),
-    val asset_pivot: Point? = null,
-    val length: Float = 1f,
-    override val layerBounds: LayerBounds? = null,
-) : ClockLayer
-
-/** Clock layer which renders the time using an AVD */
-@Keep
-data class AnimatedHandLayer(
-    val timespec: AnalogTimespec,
-    val asset: AssetReference,
-    val timer: Int? = null,
-    override val layerBounds: LayerBounds? = null,
-) : ClockLayer
-
-/** A collection of asset references for use in different device modes */
-@Keep
-data class AssetReference(
-    val light: String,
-    val dark: String,
-    val doze: String? = null,
-    val lightTint: String? = null,
-    val darkTint: String? = null,
-    val dozeTint: String? = null,
-)
-
-/**
- * Core TextStyling attributes for text clocks. Both color and sizing information can be applied to
- * either subtype.
- */
-@Keep
-interface TextStyle {
-    // fontSizeScale is a scale factor applied to the default clock's font size.
-    val fontSizeScale: Float?
-}
-
-/**
- * This specifies a font and styling parameters for that font. This is rendered using a text view
- * and the text animation classes used by the default clock. To ensure default value take effects,
- * all parameters MUST have a default value
- */
-@Keep
-data class FontTextStyle(
-    // Font to load and use in the TextView
-    val fontFamily: String? = null,
-    val lineHeight: Float? = null,
-    val borderWidth: String? = null,
-    // ratio of borderWidth / fontSize
-    val borderWidthScale: Float? = null,
-    // A color literal like `#FF00FF` or a color resource like `@android:color/system_accent1_100`
-    val fillColorLight: String? = null,
-    // A color literal like `#FF00FF` or a color resource like `@android:color/system_accent1_100`
-    val fillColorDark: String? = null,
-    override val fontSizeScale: Float? = null,
-    // used when alternate in one font file is needed
-    var fontFeatureSettings: String? = null,
-    val renderType: RenderType = RenderType.STROKE_TEXT,
-    val outlineColor: String? = null,
-    val transitionDuration: Long = -1L,
-    val transitionInterpolator: InterpolatorEnum? = null,
-) : TextStyle
-
-/**
- * As an alternative to using a font, we can instead render a digital clock using a set of drawables
- * for each numeral, and optionally a colon. These drawables will be rendered directly after sizing
- * and placing them. This may be easier than generating a font file in some cases, and is provided
- * for ease of use. Unlike fonts, these are not localizable to other numeric systems (like Burmese).
- */
-@Keep
-data class LottieTextStyle(
-    val numbers: List<String> = listOf(),
-    // Spacing between numbers, dimension string
-    val spacing: String = "0dp",
-    // Colon drawable may be omitted if unused in format spec
-    val colon: String? = null,
-    // key is keypath name to get strokes from lottie, value is the color name to query color in
-    // palette, e.g. @android:color/system_accent1_100
-    val fillColorLightMap: Map<String, String>? = null,
-    val fillColorDarkMap: Map<String, String>? = null,
-    override val fontSizeScale: Float? = null,
-    val paddingVertical: String = "0dp",
-    val paddingHorizontal: String = "0dp",
-) : TextStyle
-
-/** Layer sizing mode for the clockface or layer */
-enum class LayerBounds {
-    /**
-     * Sized so the larger dimension matches the allocated space. This results in some of the
-     * allocated space being unused.
-     */
-    FIT,
-
-    /**
-     * Sized so the smaller dimension matches the allocated space. This will clip some content to
-     * the edges of the space.
-     */
-    FILL,
-
-    /** Fills the allocated space exactly by stretching the layer */
-    STRETCH,
-}
-
-/** Ticking mode for analog hands. */
-enum class AnalogTickMode {
-    SWEEP,
-    TICK,
-}
-
-/** Timspec options for Analog Hands. Named for tick interval. */
-enum class AnalogTimespec {
-    SECONDS,
-    MINUTES,
-    HOURS,
-    HOURS_OF_DAY,
-    DAY_OF_WEEK,
-    DAY_OF_MONTH,
-    DAY_OF_YEAR,
-    WEEK,
-    MONTH,
-    TIMER,
-}
-
-enum class DigitalTimespec {
-    TIME_FULL_FORMAT,
-    DIGIT_PAIR,
-    FIRST_DIGIT,
-    SECOND_DIGIT,
-    DATE_FORMAT,
-}
-
-enum class DigitalFaceLayout {
-    // can only use HH_PAIR, MM_PAIR from DigitalTimespec
-    TWO_PAIRS_VERTICAL,
-    TWO_PAIRS_HORIZONTAL,
-    // can only use HOUR_FIRST_DIGIT, HOUR_SECOND_DIGIT, MINUTE_FIRST_DIGIT, MINUTE_SECOND_DIGIT
-    // from DigitalTimespec, used for tabular layout when the font doesn't support tnum
-    FOUR_DIGITS_ALIGN_CENTER,
-    FOUR_DIGITS_HORIZONTAL,
-}
-
-enum class RenderType {
-    CHANGE_WEIGHT,
-    HOLLOW_TEXT,
-    STROKE_TEXT,
-    OUTER_OUTLINE_TEXT,
-}
-
-enum class InterpolatorEnum(factory: () -> Interpolator) {
-    STANDARD({ Interpolators.STANDARD }),
-    EMPHASIZED({ Interpolators.EMPHASIZED });
-
-    val interpolator: Interpolator by lazy(factory)
-}
-
-fun generateDigitalLayerIdString(layer: DigitalHandLayer): String {
-    return if (
-        layer.timespec == DigitalTimespec.TIME_FULL_FORMAT ||
-            layer.timespec == DigitalTimespec.DATE_FORMAT
-    ) {
-        layer.timespec.toString()
-    } else {
-        if ("h" in layer.dateTimeFormat) {
-            "HOUR" + "_" + layer.timespec.toString()
-        } else {
-            "MINUTE" + "_" + layer.timespec.toString()
-        }
-    }
-}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt
index d0a32dc..9fb60c7 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ComposedDigitalLayerController.kt
@@ -18,6 +18,7 @@
 
 import android.graphics.Rect
 import androidx.annotation.VisibleForTesting
+import com.android.app.animation.Interpolators
 import com.android.systemui.log.core.Logger
 import com.android.systemui.plugins.clocks.AlarmData
 import com.android.systemui.plugins.clocks.ClockAnimations
@@ -29,14 +30,13 @@
 import com.android.systemui.plugins.clocks.WeatherData
 import com.android.systemui.plugins.clocks.ZenData
 import com.android.systemui.shared.clocks.view.FlexClockView
-import com.android.systemui.shared.clocks.view.SimpleDigitalClockTextView
+import com.android.systemui.shared.clocks.view.HorizontalAlignment
+import com.android.systemui.shared.clocks.view.VerticalAlignment
 import java.util.Locale
 import java.util.TimeZone
 
-class ComposedDigitalLayerController(
-    private val clockCtx: ClockContext,
-    private val layer: ComposedDigitalHandLayer,
-) : SimpleClockLayerController {
+class ComposedDigitalLayerController(private val clockCtx: ClockContext) :
+    SimpleClockLayerController {
     private val logger =
         Logger(clockCtx.messageBuffer, ComposedDigitalLayerController::class.simpleName!!)
 
@@ -46,14 +46,40 @@
     override val view = FlexClockView(clockCtx)
 
     init {
-        layer.digitalLayers.forEach {
-            val childView = SimpleDigitalClockTextView(clockCtx)
-            val controller =
-                SimpleDigitalHandLayerController(clockCtx, it as DigitalHandLayer, childView)
-
-            view.addView(childView)
+        fun createController(cfg: LayerConfig) {
+            val controller = SimpleDigitalHandLayerController(clockCtx, cfg)
+            view.addView(controller.view)
             layerControllers.add(controller)
         }
+
+        val layerCfg =
+            LayerConfig(
+                style = FontTextStyle(lineHeight = 147.25f),
+                aodStyle =
+                    FontTextStyle(
+                        transitionInterpolator = Interpolators.EMPHASIZED,
+                        transitionDuration = 750,
+                    ),
+                alignment =
+                    DigitalAlignment(HorizontalAlignment.CENTER, VerticalAlignment.BASELINE),
+
+                // Placeholders
+                timespec = DigitalTimespec.TIME_FULL_FORMAT,
+                dateTimeFormat = "hh:mm",
+            )
+
+        createController(
+            layerCfg.copy(timespec = DigitalTimespec.FIRST_DIGIT, dateTimeFormat = "hh")
+        )
+        createController(
+            layerCfg.copy(timespec = DigitalTimespec.SECOND_DIGIT, dateTimeFormat = "hh")
+        )
+        createController(
+            layerCfg.copy(timespec = DigitalTimespec.FIRST_DIGIT, dateTimeFormat = "mm")
+        )
+        createController(
+            layerCfg.copy(timespec = DigitalTimespec.SECOND_DIGIT, dateTimeFormat = "mm")
+        )
     }
 
     private fun refreshTime() {
@@ -79,17 +105,11 @@
                 refreshTime()
             }
 
-            override fun onWeatherDataChanged(data: WeatherData) {
-                view.onWeatherDataChanged(data)
-            }
+            override fun onWeatherDataChanged(data: WeatherData) {}
 
-            override fun onAlarmDataChanged(data: AlarmData) {
-                view.onAlarmDataChanged(data)
-            }
+            override fun onAlarmDataChanged(data: AlarmData) {}
 
-            override fun onZenDataChanged(data: ZenData) {
-                view.onZenDataChanged(data)
-            }
+            override fun onZenDataChanged(data: ZenData) {}
 
             override fun onFontAxesChanged(axes: List<ClockFontAxisSetting>) {
                 view.updateAxes(axes)
@@ -123,15 +143,11 @@
                 view.animateCharge()
             }
 
-            override fun onPositionUpdated(fromLeft: Int, direction: Int, fraction: Float) {
-                view.onPositionUpdated(fromLeft, direction, fraction)
-            }
+            override fun onPositionUpdated(fromLeft: Int, direction: Int, fraction: Float) {}
 
             override fun onPositionUpdated(distance: Float, fraction: Float) {}
 
-            override fun onPickerCarouselSwiping(swipingFraction: Float) {
-                view.onPickerCarouselSwiping(swipingFraction)
-            }
+            override fun onPickerCarouselSwiping(swipingFraction: Float) {}
         }
 
     override val faceEvents =
@@ -163,9 +179,8 @@
 
     override val config =
         ClockFaceConfig(
-            hasCustomWeatherDataDisplay = view.hasCustomWeatherDataDisplay,
-            hasCustomPositionUpdatedAnimation = view.hasCustomPositionUpdatedAnimation,
-            useCustomClockScene = view.useCustomClockScene,
+            hasCustomWeatherDataDisplay = false,
+            hasCustomPositionUpdatedAnimation = true,
         )
 
     @VisibleForTesting
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
index c73e1c3..f6ff3268 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
@@ -27,8 +27,6 @@
 import com.android.systemui.plugins.clocks.ClockPickerConfig
 import com.android.systemui.plugins.clocks.ClockProvider
 import com.android.systemui.plugins.clocks.ClockSettings
-import com.android.systemui.shared.clocks.view.HorizontalAlignment
-import com.android.systemui.shared.clocks.view.VerticalAlignment
 
 private val TAG = DefaultClockProvider::class.simpleName
 const val DEFAULT_CLOCK_ID = "DEFAULT"
@@ -78,8 +76,7 @@
                     typefaceCache,
                     buffers,
                     buffers.infraMessageBuffer,
-                ),
-                FLEX_DESIGN,
+                )
             )
         } else {
             DefaultClockController(ctx, layoutInflater, resources, settings, messageBuffers)
@@ -128,119 +125,5 @@
             // TODO(b/364680873): Move constant to config_clockFontFamily when shipping
             Typeface.create("google-sans-flex-clock", Typeface.NORMAL)
         }
-
-        val FLEX_DESIGN = run {
-            val largeLayer =
-                listOf(
-                    ComposedDigitalHandLayer(
-                        layerBounds = LayerBounds.FIT,
-                        customizedView = "FlexClockView",
-                        digitalLayers =
-                            listOf(
-                                DigitalHandLayer(
-                                    layerBounds = LayerBounds.FIT,
-                                    timespec = DigitalTimespec.FIRST_DIGIT,
-                                    style = FontTextStyle(lineHeight = 147.25f),
-                                    aodStyle =
-                                        FontTextStyle(
-                                            fillColorLight = "#FFFFFFFF",
-                                            outlineColor = "#00000000",
-                                            renderType = RenderType.CHANGE_WEIGHT,
-                                            transitionInterpolator = InterpolatorEnum.EMPHASIZED,
-                                            transitionDuration = 750,
-                                        ),
-                                    alignment =
-                                        DigitalAlignment(
-                                            HorizontalAlignment.CENTER,
-                                            VerticalAlignment.BASELINE,
-                                        ),
-                                    dateTimeFormat = "hh",
-                                ),
-                                DigitalHandLayer(
-                                    layerBounds = LayerBounds.FIT,
-                                    timespec = DigitalTimespec.SECOND_DIGIT,
-                                    style = FontTextStyle(lineHeight = 147.25f),
-                                    aodStyle =
-                                        FontTextStyle(
-                                            fillColorLight = "#FFFFFFFF",
-                                            outlineColor = "#00000000",
-                                            renderType = RenderType.CHANGE_WEIGHT,
-                                            transitionInterpolator = InterpolatorEnum.EMPHASIZED,
-                                            transitionDuration = 750,
-                                        ),
-                                    alignment =
-                                        DigitalAlignment(
-                                            HorizontalAlignment.CENTER,
-                                            VerticalAlignment.BASELINE,
-                                        ),
-                                    dateTimeFormat = "hh",
-                                ),
-                                DigitalHandLayer(
-                                    layerBounds = LayerBounds.FIT,
-                                    timespec = DigitalTimespec.FIRST_DIGIT,
-                                    style = FontTextStyle(lineHeight = 147.25f),
-                                    aodStyle =
-                                        FontTextStyle(
-                                            fillColorLight = "#FFFFFFFF",
-                                            outlineColor = "#00000000",
-                                            renderType = RenderType.CHANGE_WEIGHT,
-                                            transitionInterpolator = InterpolatorEnum.EMPHASIZED,
-                                            transitionDuration = 750,
-                                        ),
-                                    alignment =
-                                        DigitalAlignment(
-                                            HorizontalAlignment.CENTER,
-                                            VerticalAlignment.BASELINE,
-                                        ),
-                                    dateTimeFormat = "mm",
-                                ),
-                                DigitalHandLayer(
-                                    layerBounds = LayerBounds.FIT,
-                                    timespec = DigitalTimespec.SECOND_DIGIT,
-                                    style = FontTextStyle(lineHeight = 147.25f),
-                                    aodStyle =
-                                        FontTextStyle(
-                                            fillColorLight = "#FFFFFFFF",
-                                            outlineColor = "#00000000",
-                                            renderType = RenderType.CHANGE_WEIGHT,
-                                            transitionInterpolator = InterpolatorEnum.EMPHASIZED,
-                                            transitionDuration = 750,
-                                        ),
-                                    alignment =
-                                        DigitalAlignment(
-                                            HorizontalAlignment.CENTER,
-                                            VerticalAlignment.BASELINE,
-                                        ),
-                                    dateTimeFormat = "mm",
-                                ),
-                            ),
-                    )
-                )
-
-            val smallLayer =
-                listOf(
-                    DigitalHandLayer(
-                        layerBounds = LayerBounds.FIT,
-                        timespec = DigitalTimespec.TIME_FULL_FORMAT,
-                        style = FontTextStyle(fontSizeScale = 0.98f),
-                        aodStyle =
-                            FontTextStyle(
-                                fillColorLight = "#FFFFFFFF",
-                                outlineColor = "#00000000",
-                                renderType = RenderType.CHANGE_WEIGHT,
-                            ),
-                        alignment = DigitalAlignment(HorizontalAlignment.LEFT, null),
-                        dateTimeFormat = "h:mm",
-                    )
-                )
-
-            ClockDesign(
-                id = DEFAULT_CLOCK_ID,
-                name = "@string/clock_default_name",
-                description = "@string/clock_default_description",
-                large = ClockFace(layers = largeLayer),
-                small = ClockFace(layers = smallLayer),
-            )
-        }
     }
 }
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt
index 7f01fd7..aed3a2d 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt
@@ -32,21 +32,16 @@
 import java.util.TimeZone
 
 /** Controller for the default flex clock */
-class FlexClockController(
-    private val clockCtx: ClockContext,
-    val design: ClockDesign, // TODO(b/364680879): Remove when done inlining
-) : ClockController {
+class FlexClockController(private val clockCtx: ClockContext) : ClockController {
     override val smallClock =
         FlexClockFaceController(
             clockCtx.copy(messageBuffer = clockCtx.messageBuffers.smallClockMessageBuffer),
-            design.small ?: design.large!!,
             isLargeClock = false,
         )
 
     override val largeClock =
         FlexClockFaceController(
             clockCtx.copy(messageBuffer = clockCtx.messageBuffers.largeClockMessageBuffer),
-            design.large ?: design.small!!,
             isLargeClock = true,
         )
 
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt
index 4a47f1b..827bd68 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockFaceController.kt
@@ -35,17 +35,14 @@
 import com.android.systemui.plugins.clocks.WeatherData
 import com.android.systemui.plugins.clocks.ZenData
 import com.android.systemui.shared.clocks.view.FlexClockView
-import com.android.systemui.shared.clocks.view.SimpleDigitalClockTextView
+import com.android.systemui.shared.clocks.view.HorizontalAlignment
 import java.util.Locale
 import java.util.TimeZone
 import kotlin.math.max
 
 // TODO(b/364680879): Merge w/ ComposedDigitalLayerController
-class FlexClockFaceController(
-    clockCtx: ClockContext,
-    face: ClockFace,
-    private val isLargeClock: Boolean,
-) : ClockFaceController {
+class FlexClockFaceController(clockCtx: ClockContext, private val isLargeClock: Boolean) :
+    ClockFaceController {
     override val view: View
         get() = layerController.view
 
@@ -59,19 +56,12 @@
     val timespecHandler = DigitalTimespecHandler(DigitalTimespec.TIME_FULL_FORMAT, "hh:mm")
 
     init {
-        val lp = FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)
-        lp.gravity = Gravity.CENTER
-
-        val layer = face.layers[0]
-
         layerController =
-            if (isLargeClock) {
-                ComposedDigitalLayerController(clockCtx, layer as ComposedDigitalHandLayer)
-            } else {
-                val childView = SimpleDigitalClockTextView(clockCtx)
-                SimpleDigitalHandLayerController(clockCtx, layer as DigitalHandLayer, childView)
-            }
-        layerController.view.layoutParams = lp
+            if (isLargeClock) ComposedDigitalLayerController(clockCtx)
+            else SimpleDigitalHandLayerController(clockCtx, SMALL_LAYER_CONFIG)
+
+        layerController.view.layoutParams =
+            FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT).apply { gravity = Gravity.CENTER }
     }
 
     /** See documentation at [FlexClockView.offsetGlyphsForStepClockAnimation]. */
@@ -227,10 +217,6 @@
             }
 
             override fun onPickerCarouselSwiping(swipingFraction: Float) {
-                face.pickerScale?.let {
-                    view.scaleX = swipingFraction * (1 - it.scaleX) + it.scaleX
-                    view.scaleY = swipingFraction * (1 - it.scaleY) + it.scaleY
-                }
                 if (isLargeClock && !(view as FlexClockView).isAlignedWithScreen()) {
                     view.translationY = keyguardLargeClockTopMargin / 2F * swipingFraction
                 }
@@ -248,4 +234,15 @@
                 // TODO(b/378128811) port stepping animation
             }
         }
+
+    companion object {
+        val SMALL_LAYER_CONFIG =
+            LayerConfig(
+                timespec = DigitalTimespec.TIME_FULL_FORMAT,
+                style = FontTextStyle(fontSizeScale = 0.98f),
+                aodStyle = FontTextStyle(),
+                alignment = DigitalAlignment(HorizontalAlignment.LEFT, null),
+                dateTimeFormat = "h:mm",
+            )
+    }
 }
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleClockRelativeLayout.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleClockRelativeLayout.kt
deleted file mode 100644
index 6e1b9aa..0000000
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleClockRelativeLayout.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.systemui.shared.clocks
-
-import android.content.Context
-import android.view.View.MeasureSpec.EXACTLY
-import android.widget.RelativeLayout
-import androidx.core.view.children
-import com.android.systemui.shared.clocks.view.SimpleDigitalClockView
-
-class SimpleClockRelativeLayout(context: Context, val faceLayout: DigitalFaceLayout?) :
-    RelativeLayout(context) {
-    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
-        // For migrate_clocks_to_blueprint, mode is EXACTLY
-        // when the flag is turned off, we won't execute this codes
-        if (MeasureSpec.getMode(heightMeasureSpec) == EXACTLY) {
-            if (
-                faceLayout == DigitalFaceLayout.TWO_PAIRS_VERTICAL ||
-                    faceLayout == DigitalFaceLayout.FOUR_DIGITS_ALIGN_CENTER
-            ) {
-                val constrainedHeight = MeasureSpec.getSize(heightMeasureSpec) / 2F
-                children.forEach {
-                    // The assumption here is the height of text view is linear to font size
-                    (it as SimpleDigitalClockView).applyTextSize(
-                        constrainedHeight,
-                        constrainedByHeight = true,
-                    )
-                }
-            }
-        }
-        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
-    }
-}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleDigitalHandLayerController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleDigitalHandLayerController.kt
index ebac4b24..82fc3501 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleDigitalHandLayerController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/SimpleDigitalHandLayerController.kt
@@ -17,8 +17,8 @@
 package com.android.systemui.shared.clocks
 
 import android.graphics.Rect
-import android.view.View
 import android.view.ViewGroup
+import android.view.animation.Interpolator
 import android.widget.RelativeLayout
 import androidx.annotation.VisibleForTesting
 import com.android.systemui.customization.R
@@ -32,22 +32,56 @@
 import com.android.systemui.plugins.clocks.ThemeConfig
 import com.android.systemui.plugins.clocks.WeatherData
 import com.android.systemui.plugins.clocks.ZenData
-import com.android.systemui.shared.clocks.view.SimpleDigitalClockView
+import com.android.systemui.shared.clocks.view.HorizontalAlignment
+import com.android.systemui.shared.clocks.view.SimpleDigitalClockTextView
+import com.android.systemui.shared.clocks.view.VerticalAlignment
 import java.util.Locale
 import java.util.TimeZone
 
 private val TAG = SimpleDigitalHandLayerController::class.simpleName!!
 
-open class SimpleDigitalHandLayerController<T>(
-    private val clockCtx: ClockContext,
-    private val layer: DigitalHandLayer,
-    override val view: T,
-) : SimpleClockLayerController where T : View, T : SimpleDigitalClockView {
-    private val logger = Logger(clockCtx.messageBuffer, TAG)
-    val timespec = DigitalTimespecHandler(layer.timespec, layer.dateTimeFormat)
+// TODO(b/364680879): The remains of ClockDesign. Cut further.
+data class LayerConfig(
+    val style: FontTextStyle,
+    val aodStyle: FontTextStyle,
+    val alignment: DigitalAlignment,
+    val timespec: DigitalTimespec,
+    val dateTimeFormat: String,
+) {
+    fun generateDigitalLayerIdString(): String {
+        return when {
+            timespec == DigitalTimespec.TIME_FULL_FORMAT -> "$timespec"
+            "h" in dateTimeFormat -> "HOUR_$timespec"
+            else -> "MINUTE_$timespec"
+        }
+    }
+}
 
-    @VisibleForTesting
-    fun hasLeadingZero() = layer.dateTimeFormat.startsWith("hh") || timespec.is24Hr
+data class DigitalAlignment(
+    val horizontalAlignment: HorizontalAlignment?,
+    val verticalAlignment: VerticalAlignment?,
+)
+
+data class FontTextStyle(
+    val lineHeight: Float? = null,
+    val fontSizeScale: Float? = null,
+    val transitionDuration: Long = -1L,
+    val transitionInterpolator: Interpolator? = null,
+)
+
+enum class DigitalTimespec {
+    TIME_FULL_FORMAT,
+    FIRST_DIGIT,
+    SECOND_DIGIT,
+}
+
+open class SimpleDigitalHandLayerController(
+    private val clockCtx: ClockContext,
+    private val layerCfg: LayerConfig,
+) : SimpleClockLayerController {
+    override val view = SimpleDigitalClockTextView(clockCtx)
+    private val logger = Logger(clockCtx.messageBuffer, TAG)
+    val timespec = DigitalTimespecHandler(layerCfg.timespec, layerCfg.dateTimeFormat)
 
     @VisibleForTesting
     override var fakeTimeMills: Long?
@@ -65,145 +99,17 @@
                 ViewGroup.LayoutParams.WRAP_CONTENT,
                 ViewGroup.LayoutParams.WRAP_CONTENT,
             )
-        if (layer.alignment != null) {
-            layer.alignment.verticalAlignment?.let { view.verticalAlignment = it }
-            layer.alignment.horizontalAlignment?.let { view.horizontalAlignment = it }
-        }
-        view.applyStyles(layer.style, layer.aodStyle)
+        layerCfg.alignment.verticalAlignment?.let { view.verticalAlignment = it }
+        layerCfg.alignment.horizontalAlignment?.let { view.horizontalAlignment = it }
+        view.applyStyles(layerCfg.style, layerCfg.aodStyle)
         view.id =
             clockCtx.resources.getIdentifier(
-                generateDigitalLayerIdString(layer),
+                layerCfg.generateDigitalLayerIdString(),
                 "id",
                 clockCtx.context.getPackageName(),
             )
     }
 
-    fun applyLayout(layout: DigitalFaceLayout?) {
-        when (layout) {
-            DigitalFaceLayout.FOUR_DIGITS_ALIGN_CENTER,
-            DigitalFaceLayout.FOUR_DIGITS_HORIZONTAL -> applyFourDigitsLayout(layout)
-            DigitalFaceLayout.TWO_PAIRS_HORIZONTAL,
-            DigitalFaceLayout.TWO_PAIRS_VERTICAL -> applyTwoPairsLayout(layout)
-            else -> {
-                // one view always use FrameLayout
-                // no need to change here
-            }
-        }
-        applyMargin()
-    }
-
-    private fun applyMargin() {
-        if (view.layoutParams is RelativeLayout.LayoutParams) {
-            val lp = view.layoutParams as RelativeLayout.LayoutParams
-            layer.marginRatio?.let {
-                lp.setMargins(
-                    /* left = */ (it.left * view.measuredWidth).toInt(),
-                    /* top = */ (it.top * view.measuredHeight).toInt(),
-                    /* right = */ (it.right * view.measuredWidth).toInt(),
-                    /* bottom = */ (it.bottom * view.measuredHeight).toInt(),
-                )
-            }
-            view.layoutParams = lp
-        }
-    }
-
-    private fun applyTwoPairsLayout(twoPairsLayout: DigitalFaceLayout) {
-        val lp = view.layoutParams as RelativeLayout.LayoutParams
-        lp.addRule(RelativeLayout.TEXT_ALIGNMENT_CENTER)
-        if (twoPairsLayout == DigitalFaceLayout.TWO_PAIRS_HORIZONTAL) {
-            when (view.id) {
-                R.id.HOUR_DIGIT_PAIR -> {
-                    lp.addRule(RelativeLayout.CENTER_VERTICAL)
-                    lp.addRule(RelativeLayout.ALIGN_PARENT_START)
-                }
-                R.id.MINUTE_DIGIT_PAIR -> {
-                    lp.addRule(RelativeLayout.CENTER_VERTICAL)
-                    lp.addRule(RelativeLayout.END_OF, R.id.HOUR_DIGIT_PAIR)
-                }
-                else -> {
-                    throw Exception("cannot apply two pairs layout to view ${view.id}")
-                }
-            }
-        } else {
-            when (view.id) {
-                R.id.HOUR_DIGIT_PAIR -> {
-                    lp.addRule(RelativeLayout.CENTER_HORIZONTAL)
-                    lp.addRule(RelativeLayout.ALIGN_PARENT_TOP)
-                }
-                R.id.MINUTE_DIGIT_PAIR -> {
-                    lp.addRule(RelativeLayout.CENTER_HORIZONTAL)
-                    lp.addRule(RelativeLayout.BELOW, R.id.HOUR_DIGIT_PAIR)
-                }
-                else -> {
-                    throw Exception("cannot apply two pairs layout to view ${view.id}")
-                }
-            }
-        }
-        view.layoutParams = lp
-    }
-
-    private fun applyFourDigitsLayout(fourDigitsfaceLayout: DigitalFaceLayout) {
-        val lp = view.layoutParams as RelativeLayout.LayoutParams
-        when (fourDigitsfaceLayout) {
-            DigitalFaceLayout.FOUR_DIGITS_ALIGN_CENTER -> {
-                when (view.id) {
-                    R.id.HOUR_FIRST_DIGIT -> {
-                        lp.addRule(RelativeLayout.ALIGN_PARENT_START)
-                        lp.addRule(RelativeLayout.ALIGN_PARENT_TOP)
-                    }
-                    R.id.HOUR_SECOND_DIGIT -> {
-                        lp.addRule(RelativeLayout.END_OF, R.id.HOUR_FIRST_DIGIT)
-                        lp.addRule(RelativeLayout.ALIGN_TOP, R.id.HOUR_FIRST_DIGIT)
-                    }
-                    R.id.MINUTE_FIRST_DIGIT -> {
-                        lp.addRule(RelativeLayout.ALIGN_START, R.id.HOUR_FIRST_DIGIT)
-                        lp.addRule(RelativeLayout.BELOW, R.id.HOUR_FIRST_DIGIT)
-                    }
-                    R.id.MINUTE_SECOND_DIGIT -> {
-                        lp.addRule(RelativeLayout.ALIGN_START, R.id.HOUR_SECOND_DIGIT)
-                        lp.addRule(RelativeLayout.BELOW, R.id.HOUR_SECOND_DIGIT)
-                    }
-                    else -> {
-                        throw Exception("cannot apply four digits layout to view ${view.id}")
-                    }
-                }
-            }
-            DigitalFaceLayout.FOUR_DIGITS_HORIZONTAL -> {
-                when (view.id) {
-                    R.id.HOUR_FIRST_DIGIT -> {
-                        lp.addRule(RelativeLayout.CENTER_VERTICAL)
-                        lp.addRule(RelativeLayout.ALIGN_PARENT_START)
-                    }
-                    R.id.HOUR_SECOND_DIGIT -> {
-                        lp.addRule(RelativeLayout.CENTER_VERTICAL)
-                        lp.addRule(RelativeLayout.END_OF, R.id.HOUR_FIRST_DIGIT)
-                    }
-                    R.id.MINUTE_FIRST_DIGIT -> {
-                        lp.addRule(RelativeLayout.CENTER_VERTICAL)
-                        lp.addRule(RelativeLayout.END_OF, R.id.HOUR_SECOND_DIGIT)
-                    }
-                    R.id.MINUTE_SECOND_DIGIT -> {
-                        lp.addRule(RelativeLayout.CENTER_VERTICAL)
-                        lp.addRule(RelativeLayout.END_OF, R.id.MINUTE_FIRST_DIGIT)
-                    }
-                    else -> {
-                        throw Exception("cannot apply FOUR_DIGITS_HORIZONTAL to view ${view.id}")
-                    }
-                }
-            }
-            else -> {
-                throw IllegalArgumentException(
-                    "applyFourDigitsLayout function should not " +
-                        "have parameters as ${layer.faceLayout}"
-                )
-            }
-        }
-        if (lp == view.layoutParams) {
-            return
-        }
-        view.layoutParams = lp
-    }
-
     fun refreshTime() {
         timespec.updateTime()
         val text = timespec.getDigitString()
@@ -248,7 +154,6 @@
     override val animations =
         object : ClockAnimations {
             override fun enter() {
-                applyLayout(layer.faceLayout)
                 refreshTime()
             }
 
@@ -264,7 +169,6 @@
             }
 
             override fun fold(fraction: Float) {
-                applyLayout(layer.faceLayout)
                 refreshTime()
             }
 
@@ -283,17 +187,13 @@
         object : ClockFaceEvents {
             override fun onTimeTick() {
                 refreshTime()
-                if (
-                    layer.timespec == DigitalTimespec.TIME_FULL_FORMAT ||
-                        layer.timespec == DigitalTimespec.DATE_FORMAT
-                ) {
+                if (layerCfg.timespec == DigitalTimespec.TIME_FULL_FORMAT) {
                     view.contentDescription = timespec.getContentDescription()
                 }
             }
 
             override fun onFontSettingChanged(fontSizePx: Float) {
                 view.applyTextSize(fontSizePx)
-                applyMargin()
             }
 
             override fun onThemeChanged(theme: ThemeConfig) {
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/TimespecHandler.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/TimespecHandler.kt
index ed6a403..37db783 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/TimespecHandler.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/TimespecHandler.kt
@@ -25,9 +25,7 @@
 import java.util.Locale
 import java.util.TimeZone
 
-open class TimespecHandler(
-    val cal: Calendar,
-) {
+open class TimespecHandler(val cal: Calendar) {
     var timeZone: TimeZone
         get() = cal.timeZone
         set(value) {
@@ -82,10 +80,7 @@
     }
 
     private fun updateSimpleDateFormat(locale: Locale): DateFormat {
-        if (
-            locale.language.equals(Locale.ENGLISH.language) ||
-                timespec != DigitalTimespec.DATE_FORMAT
-        ) {
+        if (locale.language.equals(Locale.ENGLISH.language)) {
             // force date format in English, and time format to use format defined in json
             return SimpleDateFormat(timeFormat, timeFormat, ULocale.forLocale(locale))
         } else {
@@ -97,24 +92,18 @@
         return when (timespec) {
             DigitalTimespec.TIME_FULL_FORMAT ->
                 SimpleDateFormat.getInstanceForSkeleton("hh:mm", locale)
-            DigitalTimespec.DATE_FORMAT ->
-                SimpleDateFormat.getInstanceForSkeleton("EEEE MMMM d", locale)
-            else -> {
-                null
-            }
+            else -> null
         }
     }
 
     private fun applyPattern() {
         val timeFormat24Hour = timeFormat.replace("hh", "h").replace("h", "HH")
         val format = if (is24Hr) timeFormat24Hour else timeFormat
-        if (timespec != DigitalTimespec.DATE_FORMAT) {
-            (dateFormat as SimpleDateFormat).applyPattern(format)
-            (contentDescriptionFormat as? SimpleDateFormat)?.applyPattern(
-                if (is24Hr) CONTENT_DESCRIPTION_TIME_FORMAT_24_HOUR
-                else CONTENT_DESCRIPTION_TIME_FORMAT_12_HOUR
-            )
-        }
+        (dateFormat as SimpleDateFormat).applyPattern(format)
+        (contentDescriptionFormat as? SimpleDateFormat)?.applyPattern(
+            if (is24Hr) CONTENT_DESCRIPTION_TIME_FORMAT_24_HOUR
+            else CONTENT_DESCRIPTION_TIME_FORMAT_12_HOUR
+        )
     }
 
     private fun getSingleDigit(): String {
@@ -122,7 +111,7 @@
         val text = dateFormat.format(cal.time).toString()
         return text.substring(
             if (isFirstDigit) 0 else text.length - 1,
-            if (isFirstDigit) text.length - 1 else text.length
+            if (isFirstDigit) text.length - 1 else text.length,
         )
     }
 
@@ -130,27 +119,16 @@
         return when (timespec) {
             DigitalTimespec.FIRST_DIGIT,
             DigitalTimespec.SECOND_DIGIT -> getSingleDigit()
-            DigitalTimespec.DIGIT_PAIR -> {
-                dateFormat.format(cal.time).toString()
-            }
-            DigitalTimespec.TIME_FULL_FORMAT -> {
-                dateFormat.format(cal.time).toString()
-            }
-            DigitalTimespec.DATE_FORMAT -> {
-                dateFormat.format(cal.time).toString().uppercase()
-            }
+            DigitalTimespec.TIME_FULL_FORMAT -> dateFormat.format(cal.time).toString()
         }
     }
 
     fun getContentDescription(): String? {
         return when (timespec) {
-            DigitalTimespec.TIME_FULL_FORMAT,
-            DigitalTimespec.DATE_FORMAT -> {
+            DigitalTimespec.TIME_FULL_FORMAT -> {
                 contentDescriptionFormat?.format(cal.time).toString()
             }
-            else -> {
-                return null
-            }
+            else -> return null
         }
     }
 
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/DigitalClockFaceView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/DigitalClockFaceView.kt
deleted file mode 100644
index d4eb767..0000000
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/DigitalClockFaceView.kt
+++ /dev/null
@@ -1,184 +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.shared.clocks.view
-
-import android.graphics.Canvas
-import android.graphics.Point
-import android.view.View
-import android.widget.FrameLayout
-import androidx.annotation.VisibleForTesting
-import com.android.systemui.log.core.Logger
-import com.android.systemui.plugins.clocks.AlarmData
-import com.android.systemui.plugins.clocks.ClockFontAxisSetting
-import com.android.systemui.plugins.clocks.WeatherData
-import com.android.systemui.plugins.clocks.ZenData
-import com.android.systemui.shared.clocks.ClockContext
-import com.android.systemui.shared.clocks.LogUtil
-import java.util.Locale
-
-// TODO(b/364680879): Merge w/ only subclass FlexClockView
-abstract class DigitalClockFaceView(clockCtx: ClockContext) : FrameLayout(clockCtx.context) {
-    protected val logger = Logger(clockCtx.messageBuffer, this::class.simpleName!!)
-        get() = field ?: LogUtil.FALLBACK_INIT_LOGGER
-
-    abstract var digitalClockTextViewMap: MutableMap<Int, SimpleDigitalClockTextView>
-
-    @VisibleForTesting
-    var isAnimationEnabled = true
-        set(value) {
-            field = value
-            digitalClockTextViewMap.forEach { _, view -> view.isAnimationEnabled = value }
-        }
-
-    var dozeFraction: Float = 0F
-        set(value) {
-            field = value
-            digitalClockTextViewMap.forEach { _, view -> view.dozeFraction = field }
-        }
-
-    val dozeControlState = DozeControlState()
-
-    var isReactiveTouchInteractionEnabled = false
-        set(value) {
-            field = value
-        }
-
-    open val text: String?
-        get() = null
-
-    open fun refreshTime() = logger.d("refreshTime()")
-
-    override fun invalidate() {
-        logger.d("invalidate()")
-        super.invalidate()
-    }
-
-    override fun requestLayout() {
-        logger.d("requestLayout()")
-        super.requestLayout()
-    }
-
-    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
-        logger.d("onMeasure()")
-        calculateSize(widthMeasureSpec, heightMeasureSpec)?.let { setMeasuredDimension(it.x, it.y) }
-            ?: run { super.onMeasure(widthMeasureSpec, heightMeasureSpec) }
-        calculateLeftTopPosition()
-        dozeControlState.animateReady = true
-    }
-
-    override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
-        logger.d("onLayout()")
-        super.onLayout(changed, left, top, right, bottom)
-    }
-
-    override fun onDraw(canvas: Canvas) {
-        text?.let { logger.d({ "onDraw($str1)" }) { str1 = it } } ?: run { logger.d("onDraw()") }
-        super.onDraw(canvas)
-    }
-
-    /*
-     * Called in onMeasure to generate width/height overrides to the normal measuring logic. A null
-     * result causes the normal view measuring logic to execute.
-     */
-    protected open fun calculateSize(widthMeasureSpec: Int, heightMeasureSpec: Int): Point? = null
-
-    protected open fun calculateLeftTopPosition() {}
-
-    override fun addView(child: View?) {
-        if (child == null) return
-        logger.d({ "addView($str1 @$int1)" }) {
-            str1 = child::class.simpleName!!
-            int1 = child.id
-        }
-        super.addView(child)
-        if (child is SimpleDigitalClockTextView) {
-            digitalClockTextViewMap[child.id] = child
-        }
-        child.setWillNotDraw(true)
-    }
-
-    open fun animateDoze(isDozing: Boolean, isAnimated: Boolean) {
-        digitalClockTextViewMap.forEach { _, view -> view.animateDoze(isDozing, isAnimated) }
-    }
-
-    open fun animateCharge() {
-        digitalClockTextViewMap.forEach { _, view -> view.animateCharge() }
-    }
-
-    open fun onPositionUpdated(fromLeft: Int, direction: Int, fraction: Float) {}
-
-    fun updateColor(color: Int) {
-        digitalClockTextViewMap.forEach { _, view -> view.updateColor(color) }
-        invalidate()
-    }
-
-    fun updateAxes(axes: List<ClockFontAxisSetting>) {
-        digitalClockTextViewMap.forEach { _, view -> view.updateAxes(axes) }
-        requestLayout()
-    }
-
-    fun onFontSettingChanged(fontSizePx: Float) {
-        digitalClockTextViewMap.forEach { _, view -> view.applyTextSize(fontSizePx) }
-    }
-
-    open val hasCustomWeatherDataDisplay
-        get() = false
-
-    open val hasCustomPositionUpdatedAnimation
-        get() = false
-
-    /** True if it's large weather clock, will use weatherBlueprint in compose */
-    open val useCustomClockScene
-        get() = false
-
-    open fun onLocaleChanged(locale: Locale) {}
-
-    open fun onWeatherDataChanged(data: WeatherData) {}
-
-    open fun onAlarmDataChanged(data: AlarmData) {}
-
-    open fun onZenDataChanged(data: ZenData) {}
-
-    open fun onPickerCarouselSwiping(swipingFraction: Float) {}
-
-    open fun isAlignedWithScreen(): Boolean = false
-
-    /**
-     * animateDoze needs correct translate value, which is calculated in onMeasure so we need to
-     * delay this animation when we get correct values
-     */
-    class DozeControlState {
-        var animateDoze: () -> Unit = {}
-            set(value) {
-                if (animateReady) {
-                    value()
-                    field = {}
-                } else {
-                    field = value
-                }
-            }
-
-        var animateReady = false
-            set(value) {
-                if (value) {
-                    animateDoze()
-                    animateDoze = {}
-                }
-                field = value
-            }
-    }
-}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt
index faef18c..c40bb9a 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/FlexClockView.kt
@@ -22,11 +22,16 @@
 import android.util.MathUtils.constrainedMap
 import android.view.View
 import android.view.ViewGroup
+import android.widget.FrameLayout
 import android.widget.RelativeLayout
+import androidx.annotation.VisibleForTesting
 import com.android.app.animation.Interpolators
 import com.android.systemui.customization.R
+import com.android.systemui.log.core.Logger
+import com.android.systemui.plugins.clocks.ClockFontAxisSetting
 import com.android.systemui.shared.clocks.ClockContext
 import com.android.systemui.shared.clocks.DigitTranslateAnimator
+import com.android.systemui.shared.clocks.LogUtil
 import java.util.Locale
 import kotlin.math.abs
 import kotlin.math.max
@@ -34,14 +39,38 @@
 
 fun clamp(value: Float, minVal: Float, maxVal: Float): Float = max(min(value, maxVal), minVal)
 
-class FlexClockView(clockCtx: ClockContext) : DigitalClockFaceView(clockCtx) {
-    override var digitalClockTextViewMap = mutableMapOf<Int, SimpleDigitalClockTextView>()
+class FlexClockView(clockCtx: ClockContext) : FrameLayout(clockCtx.context) {
+    protected val logger = Logger(clockCtx.messageBuffer, this::class.simpleName!!)
+        get() = field ?: LogUtil.FALLBACK_INIT_LOGGER
+
+    @VisibleForTesting
+    var isAnimationEnabled = true
+        set(value) {
+            field = value
+            digitalClockTextViewMap.forEach { _, view -> view.isAnimationEnabled = value }
+        }
+
+    var dozeFraction: Float = 0F
+        set(value) {
+            field = value
+            digitalClockTextViewMap.forEach { _, view -> view.dozeFraction = field }
+        }
+
+    var isReactiveTouchInteractionEnabled = false
+        set(value) {
+            field = value
+        }
+
+    var digitalClockTextViewMap = mutableMapOf<Int, SimpleDigitalClockTextView>()
     private val digitLeftTopMap = mutableMapOf<Int, Point>()
 
     private var maxSingleDigitSize = Point(-1, -1)
     private val lockscreenTranslate = Point(0, 0)
     private var aodTranslate = Point(0, 0)
 
+    private var onAnimateDoze: (() -> Unit)? = null
+    private var isDozeReadyToAnimate = false
+
     // Does the current language have mono vertical size when displaying numerals
     private var isMonoVerticalNumericLineSpacing = true
 
@@ -57,13 +86,7 @@
 
     private val digitOffsets = mutableMapOf<Int, Float>()
 
-    override fun addView(child: View?) {
-        super.addView(child)
-        (child as SimpleDigitalClockTextView).digitTranslateAnimator =
-            DigitTranslateAnimator(::invalidate)
-    }
-
-    protected override fun calculateSize(widthMeasureSpec: Int, heightMeasureSpec: Int): Point {
+    protected fun calculateSize(widthMeasureSpec: Int, heightMeasureSpec: Int): Point? {
         maxSingleDigitSize = Point(-1, -1)
         val bottomLocation: (textView: SimpleDigitalClockTextView) -> Int = { textView ->
             if (isMonoVerticalNumericLineSpacing) {
@@ -85,7 +108,7 @@
         )
     }
 
-    protected override fun calculateLeftTopPosition() {
+    protected fun calculateLeftTopPosition() {
         digitLeftTopMap[R.id.HOUR_FIRST_DIGIT] = Point(0, 0)
         digitLeftTopMap[R.id.HOUR_SECOND_DIGIT] = Point(maxSingleDigitSize.x, 0)
         digitLeftTopMap[R.id.MINUTE_FIRST_DIGIT] = Point(0, maxSingleDigitSize.y)
@@ -96,13 +119,57 @@
         }
     }
 
-    override fun refreshTime() {
-        super.refreshTime()
+    override fun addView(child: View?) {
+        if (child == null) return
+        logger.d({ "addView($str1 @$int1)" }) {
+            str1 = child::class.simpleName!!
+            int1 = child.id
+        }
+
+        super.addView(child)
+        (child as? SimpleDigitalClockTextView)?.let {
+            it.digitTranslateAnimator = DigitTranslateAnimator(::invalidate)
+            digitalClockTextViewMap[child.id] = child
+        }
+        child.setWillNotDraw(true)
+    }
+
+    fun refreshTime() {
+        logger.d("refreshTime()")
         digitalClockTextViewMap.forEach { (_, textView) -> textView.refreshText() }
     }
 
+    override fun invalidate() {
+        logger.d("invalidate()")
+        super.invalidate()
+    }
+
+    override fun requestLayout() {
+        logger.d("requestLayout()")
+        super.requestLayout()
+    }
+
+    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+        logger.d("onMeasure()")
+        calculateSize(widthMeasureSpec, heightMeasureSpec)?.let { size ->
+            setMeasuredDimension(size.x, size.y)
+        } ?: run { super.onMeasure(widthMeasureSpec, heightMeasureSpec) }
+        calculateLeftTopPosition()
+
+        isDozeReadyToAnimate = true
+        onAnimateDoze?.invoke()
+        onAnimateDoze = null
+    }
+
+    override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
+        logger.d("onLayout()")
+        super.onLayout(changed, left, top, right, bottom)
+    }
+
     override fun onDraw(canvas: Canvas) {
+        logger.d("onDraw()")
         super.onDraw(canvas)
+
         digitalClockTextViewMap.forEach { (id, textView) ->
             // save canvas location in anticipation of restoration later
             canvas.save()
@@ -117,14 +184,30 @@
         }
     }
 
-    override fun onLocaleChanged(locale: Locale) {
+    fun isAlignedWithScreen(): Boolean = false
+
+    fun onLocaleChanged(locale: Locale) {
         updateLocale(locale)
         requestLayout()
     }
 
-    override fun animateDoze(isDozing: Boolean, isAnimated: Boolean) {
-        dozeControlState.animateDoze = {
-            super.animateDoze(isDozing, isAnimated)
+    fun updateColor(color: Int) {
+        digitalClockTextViewMap.forEach { _, view -> view.updateColor(color) }
+        invalidate()
+    }
+
+    fun updateAxes(axes: List<ClockFontAxisSetting>) {
+        digitalClockTextViewMap.forEach { _, view -> view.updateAxes(axes) }
+        requestLayout()
+    }
+
+    fun onFontSettingChanged(fontSizePx: Float) {
+        digitalClockTextViewMap.forEach { _, view -> view.applyTextSize(fontSizePx) }
+    }
+
+    fun animateDoze(isDozing: Boolean, isAnimated: Boolean) {
+        fun executeDozeAnimation() {
+            digitalClockTextViewMap.forEach { _, view -> view.animateDoze(isDozing, isAnimated) }
             if (maxSingleDigitSize.x < 0 || maxSingleDigitSize.y < 0) {
                 measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED)
             }
@@ -150,10 +233,13 @@
                 }
             }
         }
+
+        if (isDozeReadyToAnimate) executeDozeAnimation()
+        else onAnimateDoze = { executeDozeAnimation() }
     }
 
-    override fun animateCharge() {
-        super.animateCharge()
+    fun animateCharge() {
+        digitalClockTextViewMap.forEach { _, view -> view.animateCharge() }
         digitalClockTextViewMap.forEach { (id, textView) ->
             textView.digitTranslateAnimator?.let {
                 it.animatePosition(
@@ -301,7 +387,7 @@
         // Add language tags below that do not have vertically mono spaced numerals
         private val NON_MONO_VERTICAL_NUMERIC_LINE_SPACING_LANGUAGES =
             setOf(
-                "my", // Burmese
+                "my" // Burmese
             )
 
         // Use the sign of targetTranslation to control the direction of digit translation
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt
index cef24e9..2b0825f 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt
@@ -30,7 +30,6 @@
 import android.util.Log
 import android.util.MathUtils
 import android.util.TypedValue
-import android.view.View.MeasureSpec.AT_MOST
 import android.view.View.MeasureSpec.EXACTLY
 import android.view.animation.Interpolator
 import android.widget.TextView
@@ -44,19 +43,30 @@
 import com.android.systemui.shared.clocks.DimensionParser
 import com.android.systemui.shared.clocks.FontTextStyle
 import com.android.systemui.shared.clocks.LogUtil
-import com.android.systemui.shared.clocks.RenderType
-import com.android.systemui.shared.clocks.TextStyle
 import java.lang.Thread
 import kotlin.math.max
 import kotlin.math.min
 
 private val TAG = SimpleDigitalClockTextView::class.simpleName!!
 
+enum class VerticalAlignment {
+    TOP,
+    BOTTOM,
+    BASELINE, // default
+    CENTER,
+}
+
+enum class HorizontalAlignment {
+    LEFT,
+    RIGHT,
+    CENTER, // default
+}
+
 @SuppressLint("AppCompatCustomView")
 open class SimpleDigitalClockTextView(clockCtx: ClockContext, attrs: AttributeSet? = null) :
-    TextView(clockCtx.context, attrs), SimpleDigitalClockView {
+    TextView(clockCtx.context, attrs) {
     val lockScreenPaint = TextPaint()
-    override lateinit var textStyle: FontTextStyle
+    lateinit var textStyle: FontTextStyle
     lateinit var aodStyle: FontTextStyle
 
     private var lsFontVariation = ClockFontAxisSetting.toFVar(DEFAULT_LS_VARIATION)
@@ -66,7 +76,6 @@
     var maxSingleDigitWidth = -1
     var digitTranslateAnimator: DigitTranslateAnimator? = null
     var aodFontSizePx: Float = -1F
-    var isVertical: Boolean = false
 
     // Store the font size when there's no height constraint as a reference when adjusting font size
     private var lastUnconstrainedTextSize: Float = Float.MAX_VALUE
@@ -98,25 +107,20 @@
         TextAnimator(layout, typefaceCache, invalidateCb)
     }
 
-    override var verticalAlignment: VerticalAlignment = VerticalAlignment.BASELINE
-    override var horizontalAlignment: HorizontalAlignment = HorizontalAlignment.LEFT
-    override var isAnimationEnabled = true
-    override var dozeFraction: Float = 0F
+    var verticalAlignment: VerticalAlignment = VerticalAlignment.BASELINE
+    var horizontalAlignment: HorizontalAlignment = HorizontalAlignment.LEFT
+    var isAnimationEnabled = true
+    var dozeFraction: Float = 0F
         set(value) {
             field = value
             invalidate()
         }
 
-    // Have to passthrough to unify View with SimpleDigitalClockView
-    override var text: String
-        get() = super.getText().toString()
-        set(value) = super.setText(value)
-
     var textBorderWidth = 0F
     var baselineFromMeasure = 0
     var lockscreenColor = Color.WHITE
 
-    override fun updateColor(color: Int) {
+    fun updateColor(color: Int) {
         lockscreenColor = color
         lockScreenPaint.color = lockscreenColor
         if (dozeFraction < 1f) {
@@ -125,7 +129,7 @@
         invalidate()
     }
 
-    override fun updateAxes(axes: List<ClockFontAxisSetting>) {
+    fun updateAxes(axes: List<ClockFontAxisSetting>) {
         lsFontVariation = ClockFontAxisSetting.toFVar(axes + OPTICAL_SIZE_AXIS)
         lockScreenPaint.typeface = typefaceCache.getTypefaceForVariant(lsFontVariation)
         typeface = lockScreenPaint.typeface
@@ -142,16 +146,7 @@
 
     override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
         logger.d("onMeasure()")
-        if (isVertical) {
-            // use at_most to avoid apply measuredWidth from last measuring to measuredHeight
-            // cause we use max to setMeasuredDimension
-            super.onMeasure(
-                MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(widthMeasureSpec), AT_MOST),
-                MeasureSpec.makeMeasureSpec(MeasureSpec.getSize(heightMeasureSpec), AT_MOST),
-            )
-        } else {
-            super.onMeasure(widthMeasureSpec, heightMeasureSpec)
-        }
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
 
         val layout = this.layout
         if (layout != null) {
@@ -207,18 +202,10 @@
                 )
         }
 
-        if (isVertical) {
-            expectedWidth = expectedHeight.also { expectedHeight = expectedWidth }
-        }
         setMeasuredDimension(expectedWidth, expectedHeight)
     }
 
     override fun onDraw(canvas: Canvas) {
-        if (isVertical) {
-            canvas.save()
-            canvas.translate(0F, measuredHeight.toFloat())
-            canvas.rotate(-90F)
-        }
         logger.d({ "onDraw(); ls: $str1" }) { str1 = textAnimator.textInterpolator.shapedText }
         val translation = getLocalTranslation()
         canvas.translate(translation.x.toFloat(), translation.y.toFloat())
@@ -226,47 +213,26 @@
             canvas.translate(it.updatedTranslate.x.toFloat(), it.updatedTranslate.y.toFloat())
         }
 
-        if (aodStyle.renderType == RenderType.HOLLOW_TEXT) {
-            canvas.saveLayer(
-                -translation.x.toFloat(),
-                -translation.y.toFloat(),
-                (-translation.x + measuredWidth).toFloat(),
-                (-translation.y + measuredHeight).toFloat(),
-                null,
-            )
-            canvas.saveLayer(
-                -translation.x.toFloat(),
-                -translation.y.toFloat(),
-                (-translation.x + measuredWidth).toFloat(),
-                (-translation.y + measuredHeight).toFloat(),
-                PORTER_DUFF_XFER_MODE_PAINT,
-            )
-            canvas.restore()
-            canvas.restore()
-        }
         textAnimator.draw(canvas)
 
         digitTranslateAnimator?.let {
             canvas.translate(-it.updatedTranslate.x.toFloat(), -it.updatedTranslate.y.toFloat())
         }
         canvas.translate(-translation.x.toFloat(), -translation.y.toFloat())
-        if (isVertical) {
-            canvas.restore()
-        }
     }
 
     override fun invalidate() {
         logger.d("invalidate()")
         super.invalidate()
-        (parent as? DigitalClockFaceView)?.invalidate()
+        (parent as? FlexClockView)?.invalidate()
     }
 
-    override fun refreshTime() {
+    fun refreshTime() {
         logger.d("refreshTime()")
         refreshText()
     }
 
-    override fun animateDoze(isDozing: Boolean, isAnimated: Boolean) {
+    fun animateDoze(isDozing: Boolean, isAnimated: Boolean) {
         if (!this::textAnimator.isInitialized) return
         textAnimator.setTextStyle(
             animate = isAnimated && isAnimationEnabled,
@@ -279,7 +245,7 @@
         updateTextBoundsForTextAnimator()
     }
 
-    override fun animateCharge() {
+    fun animateCharge() {
         if (!this::textAnimator.isInitialized || textAnimator.isRunning()) {
             // Skip charge animation if dozing animation is already playing.
             return
@@ -365,18 +331,20 @@
     }
 
     private fun updateXtranslation(inPoint: Point, interpolatedTextBounds: Rect): Point {
-        val viewWidth = if (isVertical) measuredHeight else measuredWidth
         when (horizontalAlignment) {
             HorizontalAlignment.LEFT -> {
                 inPoint.x = lockScreenPaint.strokeWidth.toInt() - interpolatedTextBounds.left
             }
             HorizontalAlignment.RIGHT -> {
                 inPoint.x =
-                    viewWidth - interpolatedTextBounds.right - lockScreenPaint.strokeWidth.toInt()
+                    measuredWidth -
+                        interpolatedTextBounds.right -
+                        lockScreenPaint.strokeWidth.toInt()
             }
             HorizontalAlignment.CENTER -> {
                 inPoint.x =
-                    (viewWidth - interpolatedTextBounds.width()) / 2 - interpolatedTextBounds.left
+                    (measuredWidth - interpolatedTextBounds.width()) / 2 -
+                        interpolatedTextBounds.left
             }
         }
         return inPoint
@@ -385,7 +353,6 @@
     // translation of reference point of text
     // used for translation when calling textInterpolator
     private fun getLocalTranslation(): Point {
-        val viewHeight = if (isVertical) measuredWidth else measuredHeight
         val interpolatedTextBounds = updateInterpolatedTextBounds()
         val localTranslation = Point(0, 0)
         val correctedBaseline = if (baseline != -1) baseline else baselineFromMeasure
@@ -393,7 +360,7 @@
         when (verticalAlignment) {
             VerticalAlignment.CENTER -> {
                 localTranslation.y =
-                    ((viewHeight - interpolatedTextBounds.height()) / 2 -
+                    ((measuredHeight - interpolatedTextBounds.height()) / 2 -
                         interpolatedTextBounds.top -
                         correctedBaseline)
             }
@@ -404,7 +371,7 @@
             }
             VerticalAlignment.BOTTOM -> {
                 localTranslation.y =
-                    viewHeight -
+                    measuredHeight -
                         interpolatedTextBounds.bottom -
                         lockScreenPaint.strokeWidth.toInt() -
                         correctedBaseline
@@ -419,27 +386,15 @@
         return updateXtranslation(localTranslation, interpolatedTextBounds)
     }
 
-    override fun applyStyles(textStyle: TextStyle, aodStyle: TextStyle?) {
-        this.textStyle = textStyle as FontTextStyle
-        val typefaceName = "fonts/" + textStyle.fontFamily
+    fun applyStyles(textStyle: FontTextStyle, aodStyle: FontTextStyle?) {
+        this.textStyle = textStyle
         lockScreenPaint.strokeJoin = Paint.Join.ROUND
         lockScreenPaint.typeface = typefaceCache.getTypefaceForVariant(lsFontVariation)
-        textStyle.fontFeatureSettings?.let {
-            lockScreenPaint.fontFeatureSettings = it
-            fontFeatureSettings = it
-        }
         typeface = lockScreenPaint.typeface
         textStyle.lineHeight?.let { lineHeight = it.toInt() }
-        // borderWidth in textStyle and aodStyle is used to draw,
-        // strokeWidth in lockScreenPaint is used to measure and get enough space for the text
-        textStyle.borderWidth?.let { textBorderWidth = parser.convert(it) }
 
-        if (aodStyle != null && aodStyle is FontTextStyle) {
-            this.aodStyle = aodStyle
-        } else {
-            this.aodStyle = textStyle.copy()
-        }
-        this.aodStyle.transitionInterpolator?.let { aodDozingInterpolator = it.interpolator }
+        this.aodStyle = aodStyle ?: textStyle.copy()
+        this.aodStyle.transitionInterpolator?.let { aodDozingInterpolator = it }
         lockScreenPaint.strokeWidth = textBorderWidth
         measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED)
         setInterpolatorPaint()
@@ -448,7 +403,7 @@
     }
 
     // When constrainedByHeight is on, targetFontSizePx is the constrained height of textView
-    override fun applyTextSize(targetFontSizePx: Float?, constrainedByHeight: Boolean) {
+    fun applyTextSize(targetFontSizePx: Float?, constrainedByHeight: Boolean = false) {
         val adjustedFontSizePx = adjustFontSize(targetFontSizePx, constrainedByHeight)
         val fontSizePx = adjustedFontSizePx * (textStyle.fontSizeScale ?: 1f)
         aodFontSizePx =
@@ -463,7 +418,6 @@
             val lastUnconstrainedHeight = textBounds.height() + lockScreenPaint.strokeWidth * 2
             fontSizeAdjustFactor = lastUnconstrainedHeight / lastUnconstrainedTextSize
         }
-        textStyle.borderWidthScale?.let { textBorderWidth = fontSizePx * it }
 
         lockScreenPaint.strokeWidth = textBorderWidth
         recomputeMaxSingleDigitSizes()
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockView.kt
deleted file mode 100644
index e8be28f..0000000
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockView.kt
+++ /dev/null
@@ -1,57 +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.shared.clocks.view
-
-import androidx.annotation.VisibleForTesting
-import com.android.systemui.plugins.clocks.ClockFontAxisSetting
-import com.android.systemui.shared.clocks.TextStyle
-
-interface SimpleDigitalClockView {
-    var text: String
-    var verticalAlignment: VerticalAlignment
-    var horizontalAlignment: HorizontalAlignment
-    var dozeFraction: Float
-    val textStyle: TextStyle
-    @VisibleForTesting var isAnimationEnabled: Boolean
-
-    fun applyStyles(textStyle: TextStyle, aodStyle: TextStyle?)
-
-    fun applyTextSize(targetFontSizePx: Float?, constrainedByHeight: Boolean = false)
-
-    fun updateColor(color: Int)
-
-    fun updateAxes(axes: List<ClockFontAxisSetting>)
-
-    fun refreshTime()
-
-    fun animateCharge()
-
-    fun animateDoze(isDozing: Boolean, isAnimated: Boolean)
-}
-
-enum class VerticalAlignment {
-    TOP,
-    BOTTOM,
-    BASELINE, // default
-    CENTER,
-}
-
-enum class HorizontalAlignment {
-    LEFT,
-    RIGHT,
-    CENTER, // default
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
index 2c1dacd..4d2a6d9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
@@ -232,7 +232,6 @@
     @Test
     fun testOnViewAttached_withAutoPinConfirmationFailedPasswordAttemptsLessThan5() {
         val pinViewController = constructPinViewController(mockKeyguardPinView)
-        `when`(featureFlags.isEnabled(Flags.AUTO_PIN_CONFIRMATION)).thenReturn(true)
         `when`(lockPatternUtils.getPinLength(anyInt())).thenReturn(6)
         `when`(lockPatternUtils.isAutoPinConfirmEnabled(anyInt())).thenReturn(true)
         `when`(lockPatternUtils.getCurrentFailedPasswordAttempts(anyInt())).thenReturn(3)
@@ -249,7 +248,6 @@
     @Test
     fun testOnViewAttached_withAutoPinConfirmationFailedPasswordAttemptsMoreThan5() {
         val pinViewController = constructPinViewController(mockKeyguardPinView)
-        `when`(featureFlags.isEnabled(Flags.AUTO_PIN_CONFIRMATION)).thenReturn(true)
         `when`(lockPatternUtils.getPinLength(anyInt())).thenReturn(6)
         `when`(lockPatternUtils.isAutoPinConfirmEnabled(anyInt())).thenReturn(true)
         `when`(lockPatternUtils.getCurrentFailedPasswordAttempts(anyInt())).thenReturn(6)
@@ -275,7 +273,6 @@
     @Test
     fun onUserInput_autoConfirmation_attemptsUnlock() {
         val pinViewController = constructPinViewController(mockKeyguardPinView)
-        whenever(featureFlags.isEnabled(Flags.AUTO_PIN_CONFIRMATION)).thenReturn(true)
         whenever(lockPatternUtils.getPinLength(anyInt())).thenReturn(6)
         whenever(lockPatternUtils.isAutoPinConfirmEnabled(anyInt())).thenReturn(true)
         whenever(passwordTextView.text).thenReturn("000000")
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapterTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapterTest.java
index b08f97a..e94f04f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapterTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapterTest.java
@@ -16,11 +16,16 @@
 
 package com.android.systemui.accessibility.floatingmenu;
 
+import static com.android.internal.accessibility.AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.mockito.Mockito.when;
 
+import android.content.ComponentName;
 import android.graphics.drawable.Drawable;
+import android.platform.test.annotations.EnableFlags;
+import android.testing.TestableLooper;
 import android.view.LayoutInflater;
 import android.view.View;
 
@@ -28,15 +33,18 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.accessibility.dialog.AccessibilityTarget;
+import com.android.settingslib.bluetooth.HearingAidDeviceManager.ConnectionStatus;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.accessibility.floatingmenu.AccessibilityTargetAdapter.ViewHolder;
 import com.android.systemui.res.R;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -44,32 +52,35 @@
 /** Tests for {@link AccessibilityTargetAdapter}. */
 @SmallTest
 @RunWith(AndroidJUnit4.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
 public class AccessibilityTargetAdapterTest extends SysuiTestCase {
+    @Rule
+    public final MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+    private static final ComponentName TEST_NAME = new ComponentName("test.pkg", "test.activitty");
+    private static final int PAYLOAD_HEARING_STATUS_DRAWABLE = 1;
+
     @Mock
     private AccessibilityTarget mAccessibilityTarget;
-
     @Mock
     private Drawable mIcon;
-
     @Mock
     private Drawable.ConstantState mConstantState;
-
     private ViewHolder mViewHolder;
     private AccessibilityTargetAdapter mAdapter;
     private final List<AccessibilityTarget> mTargets = new ArrayList<>();
 
     @Before
     public void setUp() {
-        MockitoAnnotations.initMocks(this);
-
-        mTargets.add(mAccessibilityTarget);
-        mAdapter = new AccessibilityTargetAdapter(mTargets);
-
         final View rootView = LayoutInflater.from(mContext).inflate(
                 R.layout.accessibility_floating_menu_item, null);
         mViewHolder = new ViewHolder(rootView);
         when(mAccessibilityTarget.getIcon()).thenReturn(mIcon);
+        when(mAccessibilityTarget.getId()).thenReturn(TEST_NAME.flattenToString());
         when(mIcon.getConstantState()).thenReturn(mConstantState);
+
+        mTargets.add(mAccessibilityTarget);
+        mAdapter = new AccessibilityTargetAdapter(mTargets);
     }
 
     @Test
@@ -105,4 +116,77 @@
         assertThat(mViewHolder.itemView.getStateDescription().toString().contentEquals(
                 "testState")).isTrue();
     }
+
+    @Test
+    @EnableFlags(
+            com.android.settingslib.flags.Flags.FLAG_HEARING_DEVICE_SET_CONNECTION_STATUS_REPORT)
+    public void onHearingDeviceStatusChanged_disconnected_getExpectedStateDescription() {
+        when(mAccessibilityTarget.getId()).thenReturn(
+                ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.flattenToString());
+        int indexInTarget = 0;
+
+        mAdapter.onHearingDeviceStatusChanged(ConnectionStatus.DISCONNECTED, indexInTarget);
+        mAdapter.onBindViewHolder(mViewHolder, indexInTarget);
+
+        assertThat(mViewHolder.itemView.getStateDescription().toString().contentEquals(
+                "Disconnected")).isTrue();
+    }
+
+    @Test
+    @EnableFlags(
+            com.android.settingslib.flags.Flags.FLAG_HEARING_DEVICE_SET_CONNECTION_STATUS_REPORT)
+    public void onBindViewHolder_withPayloadDisconnected_getExpectedStateDescription() {
+        when(mAccessibilityTarget.getId()).thenReturn(
+                ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.flattenToString());
+        int indexInTarget = 0;
+
+        mAdapter.onHearingDeviceStatusChanged(ConnectionStatus.DISCONNECTED, indexInTarget);
+        mAdapter.onBindViewHolder(mViewHolder, indexInTarget,
+                List.of(PAYLOAD_HEARING_STATUS_DRAWABLE));
+
+        assertThat(mViewHolder.itemView.getStateDescription().toString().contentEquals(
+                "Disconnected")).isTrue();
+    }
+
+    @Test
+    @EnableFlags(
+            com.android.settingslib.flags.Flags.FLAG_HEARING_DEVICE_SET_CONNECTION_STATUS_REPORT)
+    public void setBadgeOnLeftSide_false_rightBadgeVisibleAndLeftBadgeInvisible() {
+        when(mAccessibilityTarget.getId())
+                .thenReturn(ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.flattenToString());
+
+        mAdapter.setBadgeOnLeftSide(false);
+        mAdapter.onBindViewHolder(mViewHolder, 0);
+
+        assertThat(mViewHolder.mRightBadgeView.getVisibility()).isEqualTo(View.VISIBLE);
+        assertThat(mViewHolder.mLeftBadgeView.getVisibility()).isEqualTo(View.INVISIBLE);
+    }
+
+    @Test
+    @EnableFlags(
+            com.android.settingslib.flags.Flags.FLAG_HEARING_DEVICE_SET_CONNECTION_STATUS_REPORT)
+    public void setBadgeOnLeftSide_rightBadgeInvisibleAndLeftBadgeVisible() {
+        when(mAccessibilityTarget.getId())
+                .thenReturn(ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.flattenToString());
+
+        mAdapter.setBadgeOnLeftSide(true);
+        mAdapter.onBindViewHolder(mViewHolder, 0);
+
+        assertThat(mViewHolder.mRightBadgeView.getVisibility()).isEqualTo(View.INVISIBLE);
+        assertThat(mViewHolder.mLeftBadgeView.getVisibility()).isEqualTo(View.VISIBLE);
+    }
+
+    @Test
+    @EnableFlags(
+            com.android.settingslib.flags.Flags.FLAG_HEARING_DEVICE_SET_CONNECTION_STATUS_REPORT)
+    public void setBadgeOnLeftSide_bindViewHolderPayloads_rightBadgeInvisibleAndLeftBadgeVisible() {
+        when(mAccessibilityTarget.getId())
+                .thenReturn(ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.flattenToString());
+
+        mAdapter.setBadgeOnLeftSide(true);
+        mAdapter.onBindViewHolder(mViewHolder, 0, List.of(PAYLOAD_HEARING_STATUS_DRAWABLE));
+
+        assertThat(mViewHolder.mRightBadgeView.getVisibility()).isEqualTo(View.INVISIBLE);
+        assertThat(mViewHolder.mLeftBadgeView.getVisibility()).isEqualTo(View.VISIBLE);
+    }
 }
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
index 80de087..2665910 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/DragToInteractAnimationControllerTest.java
@@ -29,6 +29,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
+import com.android.settingslib.bluetooth.HearingAidDeviceManager;
 import com.android.systemui.Flags;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.accessibility.utils.TestUtils;
@@ -58,13 +59,15 @@
 
     @Mock
     private AccessibilityManager mAccessibilityManager;
+    @Mock
+    private HearingAidDeviceManager mHearingAidDeviceManager;
 
     @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);
+                mockSecureSettings, mHearingAidDeviceManager);
         final MenuViewAppearance stubMenuViewAppearance = new MenuViewAppearance(mContext,
                 stubWindowManager);
         final MenuView stubMenuView = spy(new MenuView(mContext, stubMenuViewModel,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepositoryTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepositoryTest.java
index 24f3a29..785493f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepositoryTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepositoryTest.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.accessibility.floatingmenu;
 
+import static com.android.internal.accessibility.AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME;
 import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
 
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -25,11 +26,13 @@
 
 import android.content.Context;
 import android.content.res.Configuration;
+import android.platform.test.annotations.EnableFlags;
 import android.view.accessibility.AccessibilityManager;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
+import com.android.settingslib.bluetooth.HearingAidDeviceManager;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.util.settings.SecureSettings;
 
@@ -45,6 +48,7 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Locale;
+import java.util.concurrent.Executor;
 
 /** Tests for {@link MenuInfoRepository}. */
 @RunWith(AndroidJUnit4.class)
@@ -55,9 +59,10 @@
 
     @Mock
     private AccessibilityManager mAccessibilityManager;
-
     @Mock
-    private MenuInfoRepository.OnSettingsContentsChanged mMockSettingsContentsChanged;
+    private HearingAidDeviceManager mHearingAidDeviceManager;
+    @Mock
+    private MenuInfoRepository.OnContentsChanged mMockSettingsContentsChanged;
     @Mock
     private SecureSettings mSecureSettings;
 
@@ -72,7 +77,7 @@
                 anyInt());
 
         mMenuInfoRepository = new MenuInfoRepository(mContext, mAccessibilityManager,
-                mMockSettingsContentsChanged, mSecureSettings);
+                mMockSettingsContentsChanged, mSecureSettings, mHearingAidDeviceManager);
     }
 
     @After
@@ -103,4 +108,16 @@
 
         verify(mMockSettingsContentsChanged).onTargetFeaturesChanged(any());
     }
+
+    @Test
+    @EnableFlags(
+            com.android.settingslib.flags.Flags.FLAG_HEARING_DEVICE_SET_CONNECTION_STATUS_REPORT)
+    public void registerObservers_addHearingDeviceTarget_verifyRegisterConnectionStatusListener() {
+        mShortcutTargets.add(ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.flattenToString());
+        mMenuInfoRepository.registerObserversAndCallbacks();
+
+        verify(mHearingAidDeviceManager).registerConnectionStatusListener(
+                any(HearingAidDeviceManager.ConnectionStatusListener.class), any(
+                        Executor.class));
+    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java
index 157cccc..241da5f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuItemAccessibilityDelegateTest.java
@@ -41,6 +41,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
+import com.android.settingslib.bluetooth.HearingAidDeviceManager;
 import com.android.systemui.Flags;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.accessibility.utils.TestUtils;
@@ -68,6 +69,8 @@
 
     @Mock
     private AccessibilityManager mAccessibilityManager;
+    @Mock
+    private HearingAidDeviceManager mHearingAidDeviceManager;
     private final SecureSettings mSecureSettings = TestUtils.mockSecureSettings();
     private RecyclerView mStubListView;
     private MenuView mMenuView;
@@ -84,7 +87,7 @@
         final MenuViewAppearance stubMenuViewAppearance = new MenuViewAppearance(mContext,
                 stubWindowManager);
         final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, mAccessibilityManager,
-                mSecureSettings);
+                mSecureSettings, mHearingAidDeviceManager);
 
         final int halfScreenHeight =
                 stubWindowManager.getCurrentWindowMetrics().getBounds().height() / 2;
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
index 46f076a..715c40a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuListViewTouchHandlerTest.java
@@ -41,6 +41,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.accessibility.dialog.AccessibilityTarget;
+import com.android.settingslib.bluetooth.HearingAidDeviceManager;
 import com.android.systemui.Flags;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.accessibility.MotionEventHelper;
@@ -82,13 +83,15 @@
 
     @Mock
     private AccessibilityManager mAccessibilityManager;
+    @Mock
+    private HearingAidDeviceManager mHearingAidDeviceManager;
 
     @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);
+                secureSettings, mHearingAidDeviceManager);
         final MenuViewAppearance stubMenuViewAppearance = new MenuViewAppearance(mContext,
                 windowManager);
         mStubMenuView = new MenuView(mContext, stubMenuViewModel, stubMenuViewAppearance,
@@ -110,7 +113,8 @@
 
         mTouchHandler = new MenuListViewTouchHandler(mMenuAnimationController,
                 mDragToInteractAnimationController);
-        final AccessibilityTargetAdapter stubAdapter = new AccessibilityTargetAdapter(mStubTargets);
+        final AccessibilityTargetAdapter stubAdapter = new AccessibilityTargetAdapter(
+                mStubTargets);
         mStubListView = (RecyclerView) mStubMenuView.getChildAt(0);
         mStubListView.setAdapter(stubAdapter);
     }
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
index fcdeff9..4f04310 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerControllerTest.java
@@ -41,6 +41,7 @@
 
 import com.android.app.viewcapture.ViewCapture;
 import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
+import com.android.settingslib.bluetooth.HearingAidDeviceManager;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.navigationbar.NavigationModeController;
 import com.android.systemui.util.settings.SecureSettings;
@@ -68,6 +69,8 @@
 
     @Mock
     private AccessibilityManager mAccessibilityManager;
+    @Mock
+    private HearingAidDeviceManager mHearingAidDeviceManager;
 
     @Mock
     private SecureSettings mSecureSettings;
@@ -93,7 +96,7 @@
         when(mWindowMetrics.getWindowInsets()).thenReturn(stubDisplayInsets());
         mMenuViewLayerController = new MenuViewLayerController(mContext, mWindowManager,
                 viewCaptureAwareWm, mAccessibilityManager, mSecureSettings,
-                mock(NavigationModeController.class));
+                mock(NavigationModeController.class), mHearingAidDeviceManager);
     }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
index ee8ce17..cb7c205 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/floatingmenu/MenuViewTest.java
@@ -37,6 +37,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
+import com.android.settingslib.bluetooth.HearingAidDeviceManager;
 import com.android.systemui.Flags;
 import com.android.systemui.Prefs;
 import com.android.systemui.SysuiTestCase;
@@ -70,6 +71,8 @@
 
     @Mock
     private AccessibilityManager mAccessibilityManager;
+    @Mock
+    private HearingAidDeviceManager mHearingAidDeviceManager;
 
     private SysuiTestableContext mSpyContext;
 
@@ -90,7 +93,7 @@
 
         final SecureSettings secureSettings = TestUtils.mockSecureSettings();
         final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, mAccessibilityManager,
-                secureSettings);
+                secureSettings, mHearingAidDeviceManager);
         final WindowManager stubWindowManager = mContext.getSystemService(WindowManager.class);
         mStubMenuViewAppearance = new MenuViewAppearance(mSpyContext, stubWindowManager);
         mMenuView = spy(new MenuView(mSpyContext, stubMenuViewModel, mStubMenuViewAppearance,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/composable/BouncerPredictiveBackTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/composable/BouncerPredictiveBackTest.kt
index b33a83c..a654155 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/composable/BouncerPredictiveBackTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/composable/BouncerPredictiveBackTest.kt
@@ -69,6 +69,7 @@
 import com.android.systemui.scene.ui.composable.Scene
 import com.android.systemui.scene.ui.composable.SceneContainer
 import com.android.systemui.scene.ui.composable.SceneContainerTransitions
+import com.android.systemui.scene.ui.view.sceneJankMonitorFactory
 import com.android.systemui.testKosmos
 import kotlin.time.Duration.Companion.seconds
 import kotlinx.coroutines.awaitCancellation
@@ -193,6 +194,7 @@
                                 overlayByKey = emptyMap(),
                                 dataSourceDelegator = kosmos.sceneDataSourceDelegator,
                                 qsSceneAdapter = { kosmos.fakeQsSceneAdapter },
+                                sceneJankMonitorFactory = kosmos.sceneJankMonitorFactory,
                             )
                         }
                     },
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/helper/BouncerSceneLayoutTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/composable/BouncerSceneLayoutTest.kt
similarity index 95%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/helper/BouncerSceneLayoutTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/composable/BouncerSceneLayoutTest.kt
index 3ede841..b4b4178 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/helper/BouncerSceneLayoutTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/ui/composable/BouncerSceneLayoutTest.kt
@@ -14,14 +14,14 @@
  * limitations under the License.
  */
 
-package com.android.systemui.bouncer.ui.helper
+package com.android.systemui.bouncer.ui.composable
 
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.bouncer.ui.helper.BouncerSceneLayout.BELOW_USER_SWITCHER
-import com.android.systemui.bouncer.ui.helper.BouncerSceneLayout.BESIDE_USER_SWITCHER
-import com.android.systemui.bouncer.ui.helper.BouncerSceneLayout.SPLIT_BOUNCER
-import com.android.systemui.bouncer.ui.helper.BouncerSceneLayout.STANDARD_BOUNCER
+import com.android.systemui.bouncer.ui.composable.BouncerSceneLayout.BELOW_USER_SWITCHER
+import com.android.systemui.bouncer.ui.composable.BouncerSceneLayout.BESIDE_USER_SWITCHER
+import com.android.systemui.bouncer.ui.composable.BouncerSceneLayout.SPLIT_BOUNCER
+import com.android.systemui.bouncer.ui.composable.BouncerSceneLayout.STANDARD_BOUNCER
 import com.google.common.truth.Truth.assertThat
 import java.util.Locale
 import org.junit.Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepositoryImplTest.kt
index f68a1b5..eae5728 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepositoryImplTest.kt
@@ -16,7 +16,7 @@
 
 package com.android.systemui.display.data.repository
 
-import android.content.testableContext
+import android.content.Context
 import android.platform.test.annotations.EnableFlags
 import android.view.Display
 import android.view.layoutInflater
@@ -24,6 +24,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.SysuiTestableContext
 import com.android.systemui.display.shared.model.DisplayWindowProperties
 import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.kosmos.testScope
@@ -36,8 +37,12 @@
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.Mockito.doAnswer
+import org.mockito.kotlin.any
 import org.mockito.kotlin.doReturn
 import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.whenever
 
 @EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
 @RunWith(AndroidJUnit4::class)
@@ -48,7 +53,8 @@
     private val fakeDisplayRepository = kosmos.displayRepository
     private val testScope = kosmos.testScope
 
-    private val applicationContext = kosmos.testableContext
+    private val applicationContext = spy(context)
+
     private val applicationWindowManager = kosmos.mockWindowManager
     private val applicationLayoutInflater = kosmos.layoutInflater
 
@@ -64,6 +70,22 @@
     }
 
     @Before
+    fun setUpContext() {
+        doAnswer { createContextForDisplay(it.arguments[0] as Display) }
+            .whenever(applicationContext)
+            .createWindowContext(any(), any(), any())
+    }
+
+    private fun createContextForDisplay(display: Display): Context {
+        if (display.displayId == BEING_REMOVED_DISPLAY_ID) {
+            // Simulate what happens when a display is being removed.
+            // Return a context with the same display id as the original context.
+            return mContext
+        }
+        return SysuiTestableContext(mContext).also { it.display = display }
+    }
+
+    @Before
     fun start() {
         repo.start()
     }
@@ -72,6 +94,7 @@
     fun addDisplays() = runBlocking {
         fakeDisplayRepository.addDisplay(createDisplay(DEFAULT_DISPLAY_ID))
         fakeDisplayRepository.addDisplay(createDisplay(NON_DEFAULT_DISPLAY_ID))
+        fakeDisplayRepository.addDisplay(createDisplay(BEING_REMOVED_DISPLAY_ID))
     }
 
     @Test
@@ -94,7 +117,7 @@
     @Test
     fun get_nonDefaultDisplayId_returnsNewStatusBarContext() =
         testScope.runTest {
-            val displayContext = repo.get(NON_DEFAULT_DISPLAY_ID, WINDOW_TYPE_FOO)
+            val displayContext = repo.get(NON_DEFAULT_DISPLAY_ID, WINDOW_TYPE_FOO)!!
 
             assertThat(displayContext.context).isNotSameInstanceAs(applicationContext)
         }
@@ -102,7 +125,7 @@
     @Test
     fun get_nonDefaultDisplayId_returnsNewWindowManager() =
         testScope.runTest {
-            val displayContext = repo.get(NON_DEFAULT_DISPLAY_ID, WINDOW_TYPE_FOO)
+            val displayContext = repo.get(NON_DEFAULT_DISPLAY_ID, WINDOW_TYPE_FOO)!!
 
             assertThat(displayContext.windowManager).isNotSameInstanceAs(applicationWindowManager)
         }
@@ -110,7 +133,7 @@
     @Test
     fun get_nonDefaultDisplayId_returnsNewLayoutInflater() =
         testScope.runTest {
-            val displayContext = repo.get(NON_DEFAULT_DISPLAY_ID, WINDOW_TYPE_FOO)
+            val displayContext = repo.get(NON_DEFAULT_DISPLAY_ID, WINDOW_TYPE_FOO)!!
 
             assertThat(displayContext.layoutInflater).isNotSameInstanceAs(applicationLayoutInflater)
         }
@@ -154,17 +177,26 @@
                 .isNotSameInstanceAs(displayContext)
         }
 
-    @Test(expected = IllegalArgumentException::class)
-    fun get_nonExistingDisplayId_throws() =
-        testScope.runTest { repo.get(NON_EXISTING_DISPLAY_ID, WINDOW_TYPE_FOO) }
+    @Test
+    fun get_nonExistingDisplayId_returnsNull() =
+        testScope.runTest {
+            assertThat(repo.get(NON_EXISTING_DISPLAY_ID, WINDOW_TYPE_FOO)).isNull()
+        }
+
+    @Test
+    fun get_displayBeingRemoved_returnsNull() =
+        testScope.runTest {
+            assertThat(repo.get(BEING_REMOVED_DISPLAY_ID, WINDOW_TYPE_FOO)).isNull()
+        }
 
     private fun createDisplay(displayId: Int) =
-        mock<Display> { on { getDisplayId() } doReturn displayId }
+        mock<Display> { on { getDisplayId() } doReturn (displayId) }
 
     companion object {
         private const val DEFAULT_DISPLAY_ID = Display.DEFAULT_DISPLAY
         private const val NON_DEFAULT_DISPLAY_ID = DEFAULT_DISPLAY_ID + 1
         private const val NON_EXISTING_DISPLAY_ID = DEFAULT_DISPLAY_ID + 2
+        private const val BEING_REMOVED_DISPLAY_ID = DEFAULT_DISPLAY_ID + 4
         private const val WINDOW_TYPE_FOO = 123
         private const val WINDOW_TYPE_BAR = 321
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayStoreImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayStoreImplTest.kt
index 6a0781b..73957eb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayStoreImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayStoreImplTest.kt
@@ -80,9 +80,9 @@
             assertThat(store.forDisplay(NON_DEFAULT_DISPLAY_ID)).isNotSameInstanceAs(instance)
         }
 
-    @Test(expected = IllegalArgumentException::class)
-    fun forDisplay_nonExistingDisplayId_throws() =
-        testScope.runTest { store.forDisplay(NON_EXISTING_DISPLAY_ID) }
+    @Test
+    fun forDisplay_nonExistingDisplayId_returnsNull() =
+        testScope.runTest { assertThat(store.forDisplay(NON_EXISTING_DISPLAY_ID)).isNull() }
 
     @Test
     fun forDisplay_afterDisplayRemoved_onDisplayRemovalActionInvoked() =
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/data/repository/TutorialSchedulerRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/data/repository/TutorialSchedulerRepositoryTest.kt
index 4630674..b9e8613 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/data/repository/TutorialSchedulerRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/inputdevice/data/repository/TutorialSchedulerRepositoryTest.kt
@@ -84,9 +84,11 @@
 
     @Test
     fun notifyKeyboard() = runTestAndClear {
-        underTest.setNotified(KEYBOARD)
+        val now = Instant.now()
+        underTest.setNotifiedTime(KEYBOARD, now)
 
         assertThat(underTest.isNotified(KEYBOARD)).isTrue()
+        assertThat(underTest.getNotifiedTime(KEYBOARD)!!.epochSecond).isEqualTo(now.epochSecond)
         assertThat(underTest.isNotified(TOUCHPAD)).isFalse()
     }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ShortcutHelperCoreStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ShortcutHelperCoreStartableTest.kt
new file mode 100644
index 0000000..b417616
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ShortcutHelperCoreStartableTest.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyboard.shortcut
+
+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.keyboard.shortcut.shared.model.ShortcutHelperState
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.plugins.activityStarter
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runTest
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doNothing
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@OptIn(ExperimentalCoroutinesApi::class)
+class ShortcutHelperCoreStartableTest : SysuiTestCase() {
+    private val kosmos = testKosmos()
+    private val repo = kosmos.shortcutHelperStateRepository
+    private val helper = kosmos.shortcutHelperTestHelper
+    private val testScope = kosmos.testScope
+    private val activityStarter = kosmos.activityStarter
+
+    @Test
+    fun shortcutHelperState_whenToggled_doesNotBecomeActive_ifDeviceIsLocked() {
+        testScope.runTest {
+            assumedKeyguardIsNotDismissed()
+
+            val state by collectLastValue(repo.state)
+            helper.toggle(deviceId = 456)
+
+            assertThat(state).isEqualTo(ShortcutHelperState.Inactive)
+        }
+    }
+
+    @Test
+    fun shortcutHelperState_whenToggled_becomesActive_ifDeviceIsUnlocked() {
+        testScope.runTest {
+            assumeKeyguardIsDismissed()
+
+            val state by collectLastValue(repo.state)
+            helper.toggle(deviceId = 456)
+
+            assertThat(state).isEqualTo(ShortcutHelperState.Active(deviceId = 456))
+        }
+    }
+
+    private fun assumeKeyguardIsDismissed(){
+        whenever(activityStarter.dismissKeyguardThenExecute(any(), any(), eq(true))).then {
+            (it.arguments[0] as ActivityStarter.OnDismissAction).onDismiss()
+        }
+    }
+
+    private fun assumedKeyguardIsNotDismissed(){
+        // Do nothing, simulating keyguard not being dismissed and action not being not executed
+        doNothing().whenever(activityStarter).dismissKeyguardThenExecute(any(), any(), eq(true))
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomInputGesturesRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomInputGesturesRepositoryTest.kt
index e659ef2..698fac1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomInputGesturesRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/repository/CustomInputGesturesRepositoryTest.kt
@@ -18,7 +18,9 @@
 
 import android.content.Context
 import android.content.Context.INPUT_SERVICE
+import android.content.Intent
 import android.hardware.input.InputGestureData
+import android.hardware.input.InputManager
 import android.hardware.input.InputManager.CUSTOM_INPUT_GESTURE_RESULT_SUCCESS
 import android.hardware.input.fakeInputManager
 import android.platform.test.annotations.EnableFlags
@@ -27,9 +29,12 @@
 import com.android.hardware.input.Flags.FLAG_ENABLE_CUSTOMIZABLE_INPUT_GESTURES
 import com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.broadcast.broadcastDispatcher
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.keyboard.shortcut.customInputGesturesRepository
 import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.allAppsInputGestureData
+import com.android.systemui.keyboard.shortcut.data.source.TestShortcuts.goHomeInputGestureData
+import com.android.systemui.keyboard.shortcut.shortcutHelperTestHelper
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.settings.FakeUserTracker
 import com.android.systemui.settings.userTracker
@@ -48,18 +53,41 @@
 @EnableFlags(FLAG_ENABLE_CUSTOMIZABLE_INPUT_GESTURES, FLAG_USE_KEY_GESTURE_EVENT_HANDLER)
 class CustomInputGesturesRepositoryTest : SysuiTestCase() {
 
-    private val mockUserContext: Context = mock()
+    private val primaryUserContext: Context = mock()
+    private val secondaryUserContext: Context = mock()
+    private var activeUserContext: Context = primaryUserContext
+
     private val kosmos = testKosmos().also {
-        it.userTracker = FakeUserTracker(onCreateCurrentUserContext = { mockUserContext })
+        it.userTracker = FakeUserTracker(onCreateCurrentUserContext = { activeUserContext })
     }
 
     private val inputManager = kosmos.fakeInputManager.inputManager
+    private val broadcastDispatcher = kosmos.broadcastDispatcher
+    private val inputManagerForSecondaryUser: InputManager = mock()
     private val testScope = kosmos.testScope
+    private val testHelper = kosmos.shortcutHelperTestHelper
     private val customInputGesturesRepository = kosmos.customInputGesturesRepository
 
     @Before
-    fun setup(){
-        whenever(mockUserContext.getSystemService(INPUT_SERVICE)).thenReturn(inputManager)
+    fun setup() {
+        activeUserContext = primaryUserContext
+        whenever(primaryUserContext.getSystemService(INPUT_SERVICE)).thenReturn(inputManager)
+        whenever(secondaryUserContext.getSystemService(INPUT_SERVICE))
+            .thenReturn(inputManagerForSecondaryUser)
+    }
+
+    @Test
+    fun customInputGestures_emitsNewUsersInputGesturesWhenUserIsSwitch() {
+        testScope.runTest {
+            setCustomInputGesturesForPrimaryUser(allAppsInputGestureData)
+            setCustomInputGesturesForSecondaryUser(goHomeInputGestureData)
+
+            val inputGestures by collectLastValue(customInputGesturesRepository.customInputGestures)
+            assertThat(inputGestures).containsExactly(allAppsInputGestureData)
+
+            switchToSecondaryUser()
+            assertThat(inputGestures).containsExactly(goHomeInputGestureData)
+        }
     }
 
     @Test
@@ -115,4 +143,24 @@
         }
     }
 
+    private fun setCustomInputGesturesForPrimaryUser(vararg inputGesture: InputGestureData) {
+        whenever(
+            inputManager.getCustomInputGestures(/* filter= */ InputGestureData.Filter.KEY)
+        ).thenReturn(inputGesture.toList())
+    }
+
+    private fun setCustomInputGesturesForSecondaryUser(vararg inputGesture: InputGestureData) {
+        whenever(
+            inputManagerForSecondaryUser.getCustomInputGestures(/* filter= */ InputGestureData.Filter.KEY)
+        ).thenReturn(inputGesture.toList())
+    }
+
+    private fun switchToSecondaryUser() {
+        activeUserContext = secondaryUserContext
+        broadcastDispatcher.sendIntentToMatchingReceiversOnly(
+            context,
+            Intent(Intent.ACTION_USER_SWITCHED)
+        )
+    }
+
 }
\ No newline at end of file
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt
index 7c88d76..183e4d6 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/data/source/TestShortcuts.kt
@@ -24,6 +24,7 @@
 import android.os.SystemClock
 import android.view.KeyEvent
 import android.view.KeyEvent.ACTION_DOWN
+import android.view.KeyEvent.ACTION_UP
 import android.view.KeyEvent.KEYCODE_A
 import android.view.KeyEvent.META_ALT_ON
 import android.view.KeyEvent.META_CTRL_ON
@@ -540,11 +541,7 @@
             simpleShortcutCategory(System, "System apps", "Take a note"),
             simpleShortcutCategory(System, "System controls", "Take screenshot"),
             simpleShortcutCategory(System, "System controls", "Go back"),
-            simpleShortcutCategory(
-                MultiTasking,
-                "Split screen",
-                "Switch to full screen",
-            ),
+            simpleShortcutCategory(MultiTasking, "Split screen", "Switch to full screen"),
             simpleShortcutCategory(
                 MultiTasking,
                 "Split screen",
@@ -704,7 +701,7 @@
             android.view.KeyEvent(
                 /* downTime = */ SystemClock.uptimeMillis(),
                 /* eventTime = */ SystemClock.uptimeMillis(),
-                /* action = */ ACTION_DOWN,
+                /* action = */ ACTION_UP,
                 /* code = */ KEYCODE_A,
                 /* repeat = */ 0,
                 /* metaState = */ 0,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModelTest.kt
index 755c218..d9d34f5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModelTest.kt
@@ -92,13 +92,14 @@
             viewModel.onShortcutCustomizationRequested(standardAddShortcutRequest)
             val uiState by collectLastValue(viewModel.shortcutCustomizationUiState)
 
-            assertThat(uiState).isEqualTo(
-                AddShortcutDialog(
-                    shortcutLabel = "Standard shortcut",
-                    defaultCustomShortcutModifierKey =
-                    ShortcutKey.Icon.ResIdIcon(R.drawable.ic_ksh_key_meta),
+            assertThat(uiState)
+                .isEqualTo(
+                    AddShortcutDialog(
+                        shortcutLabel = "Standard shortcut",
+                        defaultCustomShortcutModifierKey =
+                            ShortcutKey.Icon.ResIdIcon(R.drawable.ic_ksh_key_meta),
+                    )
                 )
-            )
         }
     }
 
@@ -137,8 +138,7 @@
         testScope.runTest {
             val uiState by collectLastValue(viewModel.shortcutCustomizationUiState)
             viewModel.onShortcutCustomizationRequested(standardAddShortcutRequest)
-            assertThat((uiState as AddShortcutDialog).pressedKeys)
-                .isEmpty()
+            assertThat((uiState as AddShortcutDialog).pressedKeys).isEmpty()
         }
     }
 
@@ -161,8 +161,7 @@
             val uiState by collectLastValue(viewModel.shortcutCustomizationUiState)
             viewModel.onShortcutCustomizationRequested(allAppsShortcutAddRequest)
 
-            assertThat((uiState as AddShortcutDialog).errorMessage)
-                .isEmpty()
+            assertThat((uiState as AddShortcutDialog).errorMessage).isEmpty()
         }
     }
 
@@ -244,32 +243,34 @@
     }
 
     @Test
-    fun onKeyPressed_handlesKeyEvents_whereActionKeyIsAlsoPressed() {
+    fun onShortcutKeyCombinationSelected_handlesKeyEvents_whereActionKeyIsAlsoPressed() {
         testScope.runTest {
             viewModel.onShortcutCustomizationRequested(standardAddShortcutRequest)
-            val isHandled = viewModel.onKeyPressed(keyDownEventWithActionKeyPressed)
+            val isHandled =
+                viewModel.onShortcutKeyCombinationSelected(keyDownEventWithActionKeyPressed)
 
             assertThat(isHandled).isTrue()
         }
     }
 
     @Test
-    fun onKeyPressed_doesNotHandleKeyEvents_whenActionKeyIsNotAlsoPressed() {
+    fun onShortcutKeyCombinationSelected_doesNotHandleKeyEvents_whenActionKeyIsNotAlsoPressed() {
         testScope.runTest {
             viewModel.onShortcutCustomizationRequested(standardAddShortcutRequest)
-            val isHandled = viewModel.onKeyPressed(keyDownEventWithoutActionKeyPressed)
+            val isHandled =
+                viewModel.onShortcutKeyCombinationSelected(keyDownEventWithoutActionKeyPressed)
 
             assertThat(isHandled).isFalse()
         }
     }
 
     @Test
-    fun onKeyPressed_convertsKeyEventsAndUpdatesUiStatesPressedKey() {
+    fun onShortcutKeyCombinationSelected_convertsKeyEventsAndUpdatesUiStatesPressedKey() {
         testScope.runTest {
             val uiState by collectLastValue(viewModel.shortcutCustomizationUiState)
             viewModel.onShortcutCustomizationRequested(standardAddShortcutRequest)
-            viewModel.onKeyPressed(keyDownEventWithActionKeyPressed)
-            viewModel.onKeyPressed(keyUpEventWithActionKeyPressed)
+            viewModel.onShortcutKeyCombinationSelected(keyDownEventWithActionKeyPressed)
+            viewModel.onShortcutKeyCombinationSelected(keyUpEventWithActionKeyPressed)
 
             // Note that Action Key is excluded as it's already displayed on the UI
             assertThat((uiState as AddShortcutDialog).pressedKeys)
@@ -282,8 +283,8 @@
         testScope.runTest {
             val uiState by collectLastValue(viewModel.shortcutCustomizationUiState)
             viewModel.onShortcutCustomizationRequested(standardAddShortcutRequest)
-            viewModel.onKeyPressed(keyDownEventWithActionKeyPressed)
-            viewModel.onKeyPressed(keyUpEventWithActionKeyPressed)
+            viewModel.onShortcutKeyCombinationSelected(keyDownEventWithActionKeyPressed)
+            viewModel.onShortcutKeyCombinationSelected(keyUpEventWithActionKeyPressed)
 
             // Note that Action Key is excluded as it's already displayed on the UI
             assertThat((uiState as AddShortcutDialog).pressedKeys)
@@ -292,16 +293,15 @@
             // Close the dialog and show it again
             viewModel.onDialogDismissed()
             viewModel.onShortcutCustomizationRequested(standardAddShortcutRequest)
-            assertThat((uiState as AddShortcutDialog).pressedKeys)
-                .isEmpty()
+            assertThat((uiState as AddShortcutDialog).pressedKeys).isEmpty()
         }
     }
 
     private suspend fun openAddShortcutDialogAndSetShortcut() {
         viewModel.onShortcutCustomizationRequested(allAppsShortcutAddRequest)
 
-        viewModel.onKeyPressed(keyDownEventWithActionKeyPressed)
-        viewModel.onKeyPressed(keyUpEventWithActionKeyPressed)
+        viewModel.onShortcutKeyCombinationSelected(keyDownEventWithActionKeyPressed)
+        viewModel.onShortcutKeyCombinationSelected(keyUpEventWithActionKeyPressed)
 
         viewModel.onSetShortcut()
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt
index 78fce27..3fc46b9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutHelperViewModelTest.kt
@@ -55,15 +55,15 @@
 import com.android.systemui.keyboard.shortcut.ui.model.IconSource
 import com.android.systemui.keyboard.shortcut.ui.model.ShortcutCategoryUi
 import com.android.systemui.keyboard.shortcut.ui.model.ShortcutsUiState
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.kosmos.testCase
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
 import com.android.systemui.model.sysUiState
 import com.android.systemui.settings.FakeUserTracker
 import com.android.systemui.settings.fakeUserTracker
 import com.android.systemui.settings.userTracker
 import com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_SHORTCUT_HELPER_SHOWING
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.test.UnconfinedTestDispatcher
@@ -89,8 +89,7 @@
     private val mockApplicationInfo: ApplicationInfo = mock()
 
     private val kosmos =
-        Kosmos().also {
-            it.testCase = this
+        testKosmos().useUnconfinedTestDispatcher().also {
             it.testDispatcher = UnconfinedTestDispatcher()
             it.shortcutHelperSystemShortcutsSource = fakeSystemSource
             it.shortcutHelperMultiTaskingShortcutsSource = fakeMultiTaskingSource
@@ -108,7 +107,6 @@
     private val inputManager = kosmos.fakeInputManager.inputManager
     private val viewModel = kosmos.shortcutHelperViewModel
 
-
     @Before
     fun setUp() {
         fakeSystemSource.setGroups(TestShortcuts.systemGroups)
@@ -433,6 +431,28 @@
             assertThat(activeUiState.shouldShowResetButton).isTrue()
         }
 
+    @Test
+    fun shortcutsUiState_searchQuery_isResetAfterHelperIsClosedAndReOpened() =
+        testScope.runTest{
+            val uiState by collectLastValue(viewModel.shortcutsUiState)
+
+            openHelperAndSearchForFooString()
+            assertThat((uiState as? ShortcutsUiState.Active)?.searchQuery).isEqualTo("foo")
+
+            closeAndReopenShortcutHelper()
+            assertThat((uiState as? ShortcutsUiState.Active)?.searchQuery).isEqualTo("")
+        }
+
+    private fun openHelperAndSearchForFooString(){
+        testHelper.showFromActivity()
+        viewModel.onSearchQueryChanged("foo")
+    }
+
+    private fun closeAndReopenShortcutHelper() {
+        viewModel.onViewClosed()
+        testHelper.showFromActivity()
+    }
+
     private fun groupWithShortcutLabels(
         vararg shortcutLabels: String,
         groupLabel: String = FIRST_SIMPLE_GROUP_LABEL,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt
index b29a5f4..9e8713b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorTest.kt
@@ -34,6 +34,7 @@
 import com.android.systemui.keyguard.shared.model.StatusBarState.KEYGUARD
 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 as assertThatRepository
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.shade.data.repository.FlingInfo
 import com.android.systemui.shade.data.repository.fakeShadeRepository
@@ -47,7 +48,6 @@
 import org.junit.runner.RunWith
 import org.mockito.Mockito.reset
 import org.mockito.Mockito.spy
-import com.android.systemui.keyguard.util.KeyguardTransitionRepositorySpySubject.Companion.assertThat as assertThatRepository
 
 @OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
@@ -55,9 +55,8 @@
 class FromLockscreenTransitionInteractorTest : SysuiTestCase() {
     private val kosmos =
         testKosmos().apply {
-            this.fakeKeyguardTransitionRepository = spy(FakeKeyguardTransitionRepository(
-                testScope = testScope,
-            ))
+            this.fakeKeyguardTransitionRepository =
+                spy(FakeKeyguardTransitionRepository(testScope = testScope))
         }
 
     private val testScope = kosmos.testScope
@@ -181,6 +180,12 @@
             underTest.start()
             assertThatRepository(transitionRepository).noTransitionsStarted()
 
+            transitionRepository.sendTransitionSteps(
+                from = KeyguardState.DOZING,
+                to = KeyguardState.LOCKSCREEN,
+                testScope = testScope,
+            )
+
             keyguardRepository.setKeyguardDismissible(true)
             runCurrent()
             shadeRepository.setCurrentFling(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractorTest.kt
index e60d971..282bebc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractorTest.kt
@@ -25,13 +25,14 @@
 import com.android.systemui.flags.Flags
 import com.android.systemui.flags.fakeFeatureFlagsClassic
 import com.android.systemui.keyguard.data.repository.fakeKeyguardClockRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.data.repository.keyguardClockRepository
 import com.android.systemui.keyguard.data.repository.keyguardRepository
 import com.android.systemui.keyguard.shared.model.ClockSize
+import com.android.systemui.keyguard.shared.model.DozeStateModel
+import com.android.systemui.keyguard.shared.model.DozeTransitionModel
 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.Kosmos
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.media.controls.data.repository.mediaFilterRepository
@@ -75,25 +76,16 @@
         }
 
     @Test
-    @DisableSceneContainer
-    fun clockShouldBeCentered_sceneContainerFlagOff_basedOnRepository() =
-        testScope.runTest {
-            val value by collectLastValue(underTest.clockShouldBeCentered)
-            kosmos.keyguardInteractor.setClockShouldBeCentered(true)
-            assertThat(value).isTrue()
-
-            kosmos.keyguardInteractor.setClockShouldBeCentered(false)
-            assertThat(value).isFalse()
-        }
-
-    @Test
     @EnableSceneContainer
     fun clockSize_forceSmallClock_SMALL() =
         testScope.runTest {
             val value by collectLastValue(underTest.clockSize)
             kosmos.fakeKeyguardClockRepository.setShouldForceSmallClock(true)
             kosmos.fakeFeatureFlagsClassic.set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, true)
-            transitionTo(KeyguardState.AOD, KeyguardState.LOCKSCREEN)
+            kosmos.fakeKeyguardTransitionRepository.transitionTo(
+                KeyguardState.AOD,
+                KeyguardState.LOCKSCREEN,
+            )
             assertThat(value).isEqualTo(ClockSize.SMALL)
         }
 
@@ -190,7 +182,10 @@
             val value by collectLastValue(underTest.clockShouldBeCentered)
             kosmos.shadeRepository.setShadeLayoutWide(true)
             kosmos.activeNotificationListRepository.setActiveNotifs(1)
-            transitionTo(KeyguardState.LOCKSCREEN, KeyguardState.AOD)
+            kosmos.fakeKeyguardTransitionRepository.transitionTo(
+                KeyguardState.LOCKSCREEN,
+                KeyguardState.AOD,
+            )
             assertThat(value).isTrue()
         }
 
@@ -201,15 +196,187 @@
             val value by collectLastValue(underTest.clockShouldBeCentered)
             kosmos.shadeRepository.setShadeLayoutWide(true)
             kosmos.activeNotificationListRepository.setActiveNotifs(1)
-            transitionTo(KeyguardState.AOD, KeyguardState.LOCKSCREEN)
+            kosmos.fakeKeyguardTransitionRepository.transitionTo(
+                KeyguardState.AOD,
+                KeyguardState.LOCKSCREEN,
+            )
             assertThat(value).isFalse()
         }
 
-    private suspend fun transitionTo(from: KeyguardState, to: KeyguardState) {
-        with(kosmos.fakeKeyguardTransitionRepository) {
-            sendTransitionStep(TransitionStep(from, to, 0f, TransitionState.STARTED))
-            sendTransitionStep(TransitionStep(from, to, 0.5f, TransitionState.RUNNING))
-            sendTransitionStep(TransitionStep(from, to, 1f, TransitionState.FINISHED))
+    @Test
+    @DisableSceneContainer
+    fun clockShouldBeCentered_sceneContainerFlagOff_notSplitMode_true() =
+        testScope.runTest {
+            val value by collectLastValue(underTest.clockShouldBeCentered)
+            kosmos.shadeRepository.setShadeLayoutWide(false)
+            assertThat(value).isTrue()
         }
-    }
+
+    @Test
+    @DisableSceneContainer
+    fun clockShouldBeCentered_sceneContainerFlagOff_splitMode_lockscreen_withNotifs_false() =
+        testScope.runTest {
+            val value by collectLastValue(underTest.clockShouldBeCentered)
+            kosmos.shadeRepository.setShadeLayoutWide(true)
+            kosmos.activeNotificationListRepository.setActiveNotifs(1)
+            kosmos.fakeKeyguardTransitionRepository.transitionTo(
+                KeyguardState.AOD,
+                KeyguardState.LOCKSCREEN,
+            )
+            assertThat(value).isFalse()
+        }
+
+    @Test
+    @DisableSceneContainer
+    fun clockShouldBeCentered_sceneContainerFlagOff_splitMode_lockscreen_withoutNotifs_true() =
+        testScope.runTest {
+            val value by collectLastValue(underTest.clockShouldBeCentered)
+            kosmos.shadeRepository.setShadeLayoutWide(true)
+            kosmos.activeNotificationListRepository.setActiveNotifs(0)
+            kosmos.fakeKeyguardTransitionRepository.transitionTo(
+                KeyguardState.AOD,
+                KeyguardState.LOCKSCREEN,
+            )
+            assertThat(value).isTrue()
+        }
+
+    @Test
+    @DisableSceneContainer
+    fun clockShouldBeCentered_sceneContainerFlagOff_splitMode_LsToAod_withNotifs_true() =
+        testScope.runTest {
+            val value by collectLastValue(underTest.clockShouldBeCentered)
+            kosmos.shadeRepository.setShadeLayoutWide(true)
+            kosmos.activeNotificationListRepository.setActiveNotifs(1)
+            kosmos.fakeKeyguardTransitionRepository.transitionTo(
+                KeyguardState.OFF,
+                KeyguardState.LOCKSCREEN,
+            )
+            assertThat(value).isFalse()
+            kosmos.fakeKeyguardTransitionRepository.transitionTo(
+                KeyguardState.LOCKSCREEN,
+                KeyguardState.AOD,
+            )
+            assertThat(value).isTrue()
+        }
+
+    @Test
+    @DisableSceneContainer
+    fun clockShouldBeCentered_sceneContainerFlagOff_splitMode_AodToLs_withNotifs_false() =
+        testScope.runTest {
+            val value by collectLastValue(underTest.clockShouldBeCentered)
+            kosmos.shadeRepository.setShadeLayoutWide(true)
+            kosmos.activeNotificationListRepository.setActiveNotifs(1)
+            kosmos.fakeKeyguardTransitionRepository.transitionTo(
+                KeyguardState.LOCKSCREEN,
+                KeyguardState.AOD,
+            )
+            assertThat(value).isTrue()
+            kosmos.fakeKeyguardTransitionRepository.transitionTo(
+                KeyguardState.AOD,
+                KeyguardState.LOCKSCREEN,
+            )
+            assertThat(value).isFalse()
+        }
+
+    @Test
+    @DisableSceneContainer
+    fun clockShouldBeCentered_sceneContainerFlagOff_splitMode_Aod_withPulsingNotifs_false() =
+        testScope.runTest {
+            val value by collectLastValue(underTest.clockShouldBeCentered)
+            kosmos.shadeRepository.setShadeLayoutWide(true)
+            kosmos.fakeKeyguardTransitionRepository.transitionTo(
+                KeyguardState.LOCKSCREEN,
+                KeyguardState.AOD,
+            )
+            assertThat(value).isTrue()
+            kosmos.fakeKeyguardRepository.setDozeTransitionModel(
+                DozeTransitionModel(
+                    from = DozeStateModel.DOZE_AOD,
+                    to = DozeStateModel.DOZE_PULSING,
+                )
+            )
+            assertThat(value).isFalse()
+        }
+
+    @Test
+    @DisableSceneContainer
+    fun clockShouldBeCentered_sceneContainerFlagOff_splitMode_LStoGone_withoutNotifs_true() =
+        testScope.runTest {
+            val value by collectLastValue(underTest.clockShouldBeCentered)
+            kosmos.shadeRepository.setShadeLayoutWide(true)
+            kosmos.activeNotificationListRepository.setActiveNotifs(0)
+            kosmos.fakeKeyguardTransitionRepository.transitionTo(
+                KeyguardState.OFF,
+                KeyguardState.LOCKSCREEN,
+            )
+            assertThat(value).isTrue()
+            kosmos.fakeKeyguardTransitionRepository.transitionTo(
+                KeyguardState.LOCKSCREEN,
+                KeyguardState.GONE,
+            )
+            kosmos.activeNotificationListRepository.setActiveNotifs(1)
+            assertThat(value).isTrue()
+        }
+
+    @Test
+    @DisableSceneContainer
+    fun clockShouldBeCentered_sceneContainerFlagOff_splitMode_AodOn_GoneToAOD() =
+        testScope.runTest {
+            val value by collectLastValue(underTest.clockShouldBeCentered)
+            kosmos.fakeKeyguardTransitionRepository.transitionTo(
+                KeyguardState.AOD,
+                KeyguardState.LOCKSCREEN,
+            )
+            kosmos.shadeRepository.setShadeLayoutWide(true)
+            kosmos.activeNotificationListRepository.setActiveNotifs(0)
+            assertThat(value).isTrue()
+
+            kosmos.fakeKeyguardTransitionRepository.transitionTo(
+                KeyguardState.LOCKSCREEN,
+                KeyguardState.GONE,
+            )
+            kosmos.activeNotificationListRepository.setActiveNotifs(1)
+            assertThat(value).isTrue()
+
+            kosmos.fakeKeyguardTransitionRepository.transitionTo(
+                KeyguardState.GONE,
+                KeyguardState.AOD,
+            )
+            assertThat(value).isTrue()
+        }
+
+    @Test
+    @DisableSceneContainer
+    fun clockShouldBeCentered_sceneContainerFlagOff_splitMode_AodOff_GoneToDoze() =
+        testScope.runTest {
+            val value by collectLastValue(underTest.clockShouldBeCentered)
+            kosmos.shadeRepository.setShadeLayoutWide(true)
+            kosmos.fakeKeyguardTransitionRepository.transitionTo(
+                KeyguardState.DOZING,
+                KeyguardState.LOCKSCREEN,
+            )
+            kosmos.activeNotificationListRepository.setActiveNotifs(0)
+            assertThat(value).isTrue()
+
+            kosmos.fakeKeyguardTransitionRepository.transitionTo(
+                KeyguardState.LOCKSCREEN,
+                KeyguardState.GONE,
+            )
+            kosmos.activeNotificationListRepository.setActiveNotifs(1)
+            assertThat(value).isTrue()
+
+            kosmos.fakeKeyguardTransitionRepository.transitionTo(
+                KeyguardState.GONE,
+                KeyguardState.DOZING,
+            )
+            kosmos.activeNotificationListRepository.setActiveNotifs(1)
+            assertThat(value).isTrue()
+
+            kosmos.fakeKeyguardTransitionRepository.transitionTo(
+                KeyguardState.DOZING,
+                KeyguardState.LOCKSCREEN,
+            )
+            kosmos.activeNotificationListRepository.setActiveNotifs(0)
+            assertThat(value).isTrue()
+        }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
index b3417b9..c44f27e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
@@ -46,8 +46,6 @@
 import com.android.systemui.keyguard.shared.model.TransitionState.STARTED
 import com.android.systemui.keyguard.shared.model.TransitionStep
 import com.android.systemui.kosmos.testScope
-import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
-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.model.Scenes
@@ -76,7 +74,6 @@
     private val configRepository by lazy { kosmos.fakeConfigurationRepository }
     private val bouncerRepository by lazy { kosmos.keyguardBouncerRepository }
     private val shadeRepository by lazy { kosmos.shadeRepository }
-    private val powerInteractor by lazy { kosmos.powerInteractor }
     private val keyguardRepository by lazy { kosmos.keyguardRepository }
     private val keyguardTransitionRepository by lazy { kosmos.fakeKeyguardTransitionRepository }
 
@@ -444,7 +441,6 @@
             repository.setDozeTransitionModel(
                 DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH)
             )
-            powerInteractor.setAwakeForTest()
             advanceTimeBy(1000L)
 
             assertThat(isAbleToDream).isEqualTo(false)
@@ -460,9 +456,6 @@
             repository.setDozeTransitionModel(
                 DozeTransitionModel(from = DozeStateModel.DOZE, to = DozeStateModel.FINISH)
             )
-            powerInteractor.setAwakeForTest()
-            runCurrent()
-
             // After some delay, still false
             advanceTimeBy(300L)
             assertThat(isAbleToDream).isEqualTo(false)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorTest.kt
index b069855..98e3c68 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardWakeDirectlyToGoneInteractorTest.kt
@@ -36,6 +36,7 @@
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.shared.model.BiometricUnlockMode
 import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest
 import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest
@@ -406,4 +407,48 @@
             // It should not have any effect.
             assertEquals(listOf(false, true, false, true), canWake)
         }
+
+    @Test
+    @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
+    fun testCanWakeDirectlyToGone_falseAsSoonAsTransitionsAwayFromGone() =
+        testScope.runTest {
+            val canWake by collectValues(underTest.canWakeDirectlyToGone)
+
+            assertEquals(
+                listOf(
+                    false // Defaults to false.
+                ),
+                canWake,
+            )
+
+            transitionRepository.sendTransitionSteps(
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.GONE,
+                testScope,
+            )
+
+            assertEquals(
+                listOf(
+                    false,
+                    true, // Because we're GONE.
+                ),
+                canWake,
+            )
+
+            transitionRepository.sendTransitionSteps(
+                from = KeyguardState.LOCKSCREEN,
+                to = KeyguardState.GONE,
+                testScope = testScope,
+                throughTransitionState = TransitionState.RUNNING,
+            )
+
+            assertEquals(
+                listOf(
+                    false,
+                    true,
+                    false, // False as soon as we start a transition away from GONE.
+                ),
+                canWake,
+            )
+        }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
index ea2b3cd..605a5d2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/binder/WindowManagerLockscreenVisibilityManagerTest.kt
@@ -21,6 +21,7 @@
 import android.platform.test.annotations.RequiresFlagsEnabled
 import android.platform.test.flag.junit.CheckFlagsRule
 import android.platform.test.flag.junit.DeviceFlagsValueProvider
+import android.view.IRemoteAnimationFinishedCallback
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
@@ -38,10 +39,13 @@
 import org.mockito.ArgumentMatchers.eq
 import org.mockito.Mock
 import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.mock
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.verifyNoMoreInteractions
 import org.mockito.MockitoAnnotations
 import org.mockito.kotlin.any
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
@@ -222,4 +226,22 @@
         underTest.setSurfaceBehindVisibility(false)
         verify(keyguardTransitions).startKeyguardTransition(eq(true), any())
     }
+
+    @Test
+    fun remoteAnimationInstantlyFinished_ifDismissTransitionNotStarted() {
+        val mockedCallback = mock<IRemoteAnimationFinishedCallback>()
+        whenever(keyguardDismissTransitionInteractor.startDismissKeyguardTransition(any()))
+            .thenReturn(false)
+
+        underTest.onKeyguardGoingAwayRemoteAnimationStart(
+            transit = 0,
+            apps = emptyArray(),
+            wallpapers = emptyArray(),
+            nonApps = emptyArray(),
+            finishedCallback = mockedCallback,
+        )
+
+        verify(mockedCallback).onAnimationFinished()
+        verifyNoMoreInteractions(mockedCallback)
+    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt
index 87ab3c8..1cf45f8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt
@@ -27,7 +27,6 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.customization.R as customR
-import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
 import com.android.systemui.keyguard.domain.interactor.keyguardBlueprintInteractor
 import com.android.systemui.keyguard.domain.interactor.keyguardClockInteractor
 import com.android.systemui.keyguard.domain.interactor.keyguardSmartspaceInteractor
@@ -156,7 +155,6 @@
 
                 shadeRepository.setShadeLayoutWide(false)
                 keyguardClockInteractor.setClockSize(ClockSize.LARGE)
-                fakeKeyguardRepository.setClockShouldBeCentered(true)
                 notificationsKeyguardInteractor.setNotificationsFullyHidden(true)
                 keyguardSmartspaceInteractor.setBcSmartspaceVisibility(VISIBLE)
                 fakeConfigurationController.notifyConfigurationChanged()
@@ -181,7 +179,6 @@
 
                 shadeRepository.setShadeLayoutWide(true)
                 keyguardClockInteractor.setClockSize(ClockSize.LARGE)
-                fakeKeyguardRepository.setClockShouldBeCentered(true)
                 notificationsKeyguardInteractor.setNotificationsFullyHidden(true)
                 keyguardSmartspaceInteractor.setBcSmartspaceVisibility(VISIBLE)
                 fakeConfigurationController.notifyConfigurationChanged()
@@ -206,7 +203,6 @@
 
                 shadeRepository.setShadeLayoutWide(false)
                 keyguardClockInteractor.setClockSize(ClockSize.LARGE)
-                fakeKeyguardRepository.setClockShouldBeCentered(true)
                 notificationsKeyguardInteractor.setNotificationsFullyHidden(true)
                 keyguardSmartspaceInteractor.setBcSmartspaceVisibility(VISIBLE)
                 fakeConfigurationController.notifyConfigurationChanged()
@@ -230,7 +226,6 @@
 
                 shadeRepository.setShadeLayoutWide(true)
                 keyguardClockInteractor.setClockSize(ClockSize.SMALL)
-                fakeKeyguardRepository.setClockShouldBeCentered(true)
                 notificationsKeyguardInteractor.setNotificationsFullyHidden(true)
                 keyguardSmartspaceInteractor.setBcSmartspaceVisibility(VISIBLE)
                 fakeConfigurationController.notifyConfigurationChanged()
@@ -254,7 +249,6 @@
 
                 shadeRepository.setShadeLayoutWide(false)
                 keyguardClockInteractor.setClockSize(ClockSize.SMALL)
-                fakeKeyguardRepository.setClockShouldBeCentered(true)
                 notificationsKeyguardInteractor.setNotificationsFullyHidden(true)
                 keyguardSmartspaceInteractor.setBcSmartspaceVisibility(VISIBLE)
                 fakeConfigurationController.notifyConfigurationChanged()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModelTest.kt
index feaf06a..ade7614 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModelTest.kt
@@ -16,10 +16,13 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
+import android.platform.test.annotations.EnableFlags
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
+import com.android.systemui.Flags.FLAG_BOUNCER_UI_REVAMP
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectValues
+import com.android.systemui.flags.BrokenWithSceneContainer
 import com.android.systemui.flags.Flags
 import com.android.systemui.flags.fakeFeatureFlagsClassic
 import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
@@ -72,6 +75,28 @@
         }
 
     @Test
+    @EnableFlags(FLAG_BOUNCER_UI_REVAMP)
+    @BrokenWithSceneContainer(388068805)
+    fun notifications_areFullyVisible_whenShadeIsOpen() =
+        testScope.runTest {
+            val values by collectValues(underTest.notificationAlpha)
+            kosmos.bouncerWindowBlurTestUtil.shadeExpanded(true)
+
+            keyguardTransitionRepository.sendTransitionSteps(
+                listOf(
+                    step(0f, TransitionState.STARTED),
+                    step(0.1f),
+                    step(0.2f),
+                    step(0.3f),
+                    step(1f),
+                ),
+                testScope,
+            )
+
+            values.forEach { assertThat(it).isEqualTo(1f) }
+        }
+
+    @Test
     fun blurRadiusGoesToMaximumWhenShadeIsExpanded() =
         testScope.runTest {
             val values by collectValues(underTest.windowBlurRadius)
@@ -88,6 +113,25 @@
         }
 
     @Test
+    @EnableFlags(FLAG_BOUNCER_UI_REVAMP)
+    @BrokenWithSceneContainer(388068805)
+    fun notificationBlur_isNonZero_whenShadeIsExpanded() =
+        testScope.runTest {
+            val values by collectValues(underTest.notificationBlurRadius)
+
+            kosmos.bouncerWindowBlurTestUtil.shadeExpanded(true)
+
+            kosmos.bouncerWindowBlurTestUtil.assertTransitionToBlurRadius(
+                transitionProgress = listOf(0f, 0f, 0.1f, 0.2f, 0.3f, 1f),
+                startValue = kosmos.blurConfig.maxBlurRadiusPx / 3.0f,
+                endValue = kosmos.blurConfig.maxBlurRadiusPx / 3.0f,
+                transitionFactory = ::step,
+                actualValuesProvider = { values },
+                checkInterpolatedValues = false,
+            )
+        }
+
+    @Test
     fun blurRadiusGoesFromMinToMaxWhenShadeIsNotExpanded() =
         testScope.runTest {
             val values by collectValues(underTest.windowBlurRadius)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelTest.kt
index 05a6b87..8a599a1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelTest.kt
@@ -20,15 +20,15 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.flags.BrokenWithSceneContainer
 import com.android.systemui.flags.DisableSceneContainer
 import com.android.systemui.flags.EnableSceneContainer
 import com.android.systemui.flags.andSceneContainer
 import com.android.systemui.keyguard.data.repository.fakeKeyguardClockRepository
+import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
 import com.android.systemui.keyguard.data.repository.keyguardClockRepository
-import com.android.systemui.keyguard.data.repository.keyguardRepository
 import com.android.systemui.keyguard.shared.model.ClockSize
 import com.android.systemui.keyguard.shared.model.ClockSizeSetting
+import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel.ClockLayout
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.plugins.clocks.ClockConfig
@@ -37,6 +37,8 @@
 import com.android.systemui.plugins.clocks.ClockFaceController
 import com.android.systemui.res.R
 import com.android.systemui.shade.data.repository.shadeRepository
+import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
+import com.android.systemui.statusbar.notification.data.repository.setActiveNotifs
 import com.android.systemui.statusbar.ui.fakeSystemBarUtilsProxy
 import com.android.systemui.testKosmos
 import com.android.systemui.util.mockito.whenever
@@ -87,7 +89,11 @@
 
             with(kosmos) {
                 shadeRepository.setShadeLayoutWide(true)
-                keyguardRepository.setClockShouldBeCentered(true)
+                kosmos.activeNotificationListRepository.setActiveNotifs(0)
+                fakeKeyguardTransitionRepository.transitionTo(
+                    KeyguardState.AOD,
+                    KeyguardState.LOCKSCREEN,
+                )
                 keyguardClockRepository.setClockSize(ClockSize.LARGE)
             }
 
@@ -95,14 +101,18 @@
         }
 
     @Test
-    @BrokenWithSceneContainer(339465026)
+    @EnableSceneContainer
     fun currentClockLayout_splitShadeOn_clockNotCentered_largeClock_splitShadeLargeClock() =
         testScope.runTest {
             val currentClockLayout by collectLastValue(underTest.currentClockLayout)
 
             with(kosmos) {
                 shadeRepository.setShadeLayoutWide(true)
-                keyguardRepository.setClockShouldBeCentered(false)
+                activeNotificationListRepository.setActiveNotifs(1)
+                fakeKeyguardTransitionRepository.transitionTo(
+                    KeyguardState.AOD,
+                    KeyguardState.LOCKSCREEN,
+                )
                 keyguardClockRepository.setClockSize(ClockSize.LARGE)
             }
 
@@ -110,42 +120,46 @@
         }
 
     @Test
-    @BrokenWithSceneContainer(339465026)
-    fun currentClockLayout_splitShadeOn_clockNotCentered_smallClock_splitShadeSmallClock() =
+    @EnableSceneContainer
+    fun currentClockLayout_splitShadeOn_clockNotCentered_forceSmallClock_splitShadeSmallClock() =
         testScope.runTest {
             val currentClockLayout by collectLastValue(underTest.currentClockLayout)
 
             with(kosmos) {
                 shadeRepository.setShadeLayoutWide(true)
-                keyguardRepository.setClockShouldBeCentered(false)
-                keyguardClockRepository.setClockSize(ClockSize.SMALL)
+                activeNotificationListRepository.setActiveNotifs(1)
+                fakeKeyguardTransitionRepository.transitionTo(
+                    KeyguardState.AOD,
+                    KeyguardState.LOCKSCREEN,
+                )
+                fakeKeyguardClockRepository.setShouldForceSmallClock(true)
             }
 
             assertThat(currentClockLayout).isEqualTo(ClockLayout.SPLIT_SHADE_SMALL_CLOCK)
         }
 
     @Test
-    @BrokenWithSceneContainer(339465026)
-    fun currentClockLayout_singleShade_smallClock_smallClock() =
+    @EnableSceneContainer
+    fun currentClockLayout_singleShade_withNotifs_smallClock() =
         testScope.runTest {
             val currentClockLayout by collectLastValue(underTest.currentClockLayout)
 
             with(kosmos) {
                 shadeRepository.setShadeLayoutWide(false)
-                keyguardClockRepository.setClockSize(ClockSize.SMALL)
+                activeNotificationListRepository.setActiveNotifs(1)
             }
 
             assertThat(currentClockLayout).isEqualTo(ClockLayout.SMALL_CLOCK)
         }
 
     @Test
-    fun currentClockLayout_singleShade_largeClock_largeClock() =
+    fun currentClockLayout_singleShade_withoutNotifs_largeClock() =
         testScope.runTest {
             val currentClockLayout by collectLastValue(underTest.currentClockLayout)
 
             with(kosmos) {
                 shadeRepository.setShadeLayoutWide(false)
-                keyguardClockRepository.setClockSize(ClockSize.LARGE)
+                activeNotificationListRepository.setActiveNotifs(0)
             }
 
             assertThat(currentClockLayout).isEqualTo(ClockLayout.LARGE_CLOCK)
@@ -195,7 +209,7 @@
         }
 
     @Test
-    @BrokenWithSceneContainer(339465026)
+    @DisableSceneContainer
     fun testClockSize_dynamicClockSize() =
         testScope.runTest {
             with(kosmos) {
@@ -219,7 +233,7 @@
         }
 
     @Test
-    @BrokenWithSceneContainer(339465026)
+    @DisableSceneContainer
     fun isLargeClockVisible_whenSmallClockSize_isFalse() =
         testScope.runTest {
             val value by collectLastValue(underTest.isLargeClockVisible)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
index d909c5a..914094f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModelTest.kt
@@ -16,9 +16,11 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
+import android.platform.test.annotations.EnableFlags
 import android.platform.test.flag.junit.FlagsParameterization
 import androidx.test.filters.SmallTest
 import com.android.compose.animation.scene.ObservableTransitionState
+import com.android.systemui.Flags.FLAG_BOUNCER_UI_REVAMP
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.coroutines.collectValues
@@ -153,7 +155,7 @@
         }
 
     @Test
-    @BrokenWithSceneContainer(330311871)
+    @BrokenWithSceneContainer(388068805)
     fun blurRadiusIsMaxWhenShadeIsExpanded() =
         testScope.runTest {
             val values by collectValues(underTest.windowBlurRadius)
@@ -170,7 +172,7 @@
         }
 
     @Test
-    @BrokenWithSceneContainer(330311871)
+    @BrokenWithSceneContainer(388068805)
     fun blurRadiusGoesFromMinToMaxWhenShadeIsNotExpanded() =
         testScope.runTest {
             val values by collectValues(underTest.windowBlurRadius)
@@ -185,6 +187,44 @@
             )
         }
 
+    @Test
+    @EnableFlags(FLAG_BOUNCER_UI_REVAMP)
+    @BrokenWithSceneContainer(388068805)
+    fun notificationBlur_isNonZero_whenShadeIsExpanded() =
+        testScope.runTest {
+            val values by collectValues(underTest.notificationBlurRadius)
+            kosmos.bouncerWindowBlurTestUtil.shadeExpanded(true)
+            runCurrent()
+
+            kosmos.bouncerWindowBlurTestUtil.assertTransitionToBlurRadius(
+                transitionProgress = listOf(0f, 0f, 0.1f, 0.2f, 0.3f, 1f),
+                startValue = kosmos.blurConfig.maxBlurRadiusPx / 3.0f,
+                endValue = kosmos.blurConfig.maxBlurRadiusPx / 3.0f,
+                transitionFactory = ::step,
+                actualValuesProvider = { values },
+                checkInterpolatedValues = false,
+            )
+        }
+
+    @Test
+    @EnableFlags(FLAG_BOUNCER_UI_REVAMP)
+    @BrokenWithSceneContainer(388068805)
+    fun notifications_areFullyVisible_whenShadeIsExpanded() =
+        testScope.runTest {
+            val values by collectValues(underTest.notificationAlpha)
+            kosmos.bouncerWindowBlurTestUtil.shadeExpanded(true)
+            runCurrent()
+
+            kosmos.bouncerWindowBlurTestUtil.assertTransitionToBlurRadius(
+                transitionProgress = listOf(0f, 0f, 0.1f, 0.2f, 0.3f, 1f),
+                startValue = 1.0f,
+                endValue = 1.0f,
+                transitionFactory = ::step,
+                actualValuesProvider = { values },
+                checkInterpolatedValues = false,
+            )
+        }
+
     private fun step(
         value: Float,
         state: TransitionState = TransitionState.RUNNING,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/util/KeyguardTransitionRunner.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/util/KeyguardTransitionRunner.kt
index 5798e07..338b068 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/util/KeyguardTransitionRunner.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/util/KeyguardTransitionRunner.kt
@@ -24,8 +24,9 @@
 import java.util.function.Consumer
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.Job
 import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableSharedFlow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.collect
 import kotlinx.coroutines.launch
@@ -36,12 +37,10 @@
  * Gives direct control over ValueAnimator, in order to make transition tests deterministic. See
  * [AnimationHandler]. Animators are required to be run on the main thread, so dispatch accordingly.
  */
-class KeyguardTransitionRunner(val repository: KeyguardTransitionRepository) :
-    AnimationFrameCallbackProvider {
-
-    private var frameCount = 1L
-    private var frames = MutableStateFlow(Pair<Long, FrameCallback?>(0L, null))
-    private var job: Job? = null
+class KeyguardTransitionRunner(
+    val frames: Flow<Long>,
+    val repository: KeyguardTransitionRepository,
+) {
     @Volatile private var isTerminated = false
 
     /**
@@ -54,21 +53,12 @@
         maxFrames: Int = 100,
         frameCallback: Consumer<Long>? = null,
     ) {
-        // AnimationHandler uses ThreadLocal storage, and ValueAnimators MUST start from main
-        // thread
-        withContext(Dispatchers.Main) {
-            info.animator!!.getAnimationHandler().setProvider(this@KeyguardTransitionRunner)
-        }
-
-        job =
+        val job =
             scope.launch {
-                frames.collect {
-                    val (frameNumber, callback) = it
-
+                frames.collect { frameNumber ->
                     isTerminated = frameNumber >= maxFrames
                     if (!isTerminated) {
                         try {
-                            withContext(Dispatchers.Main) { callback?.doFrame(frameNumber) }
                             frameCallback?.accept(frameNumber)
                         } catch (e: IllegalStateException) {
                             e.printStackTrace()
@@ -78,27 +68,46 @@
             }
         withContext(Dispatchers.Main) { repository.startTransition(info) }
 
-        waitUntilComplete(info.animator!!)
+        waitUntilComplete(info, info.animator!!)
+        job.cancel()
     }
 
-    private suspend fun waitUntilComplete(animator: ValueAnimator) {
+    private suspend fun waitUntilComplete(info: TransitionInfo, animator: ValueAnimator) {
         withContext(Dispatchers.Main) {
             val startTime = System.currentTimeMillis()
             while (!isTerminated && animator.isRunning()) {
                 delay(1)
                 if (System.currentTimeMillis() - startTime > MAX_TEST_DURATION) {
-                    fail("Failed test due to excessive runtime of: $MAX_TEST_DURATION")
+                    fail("Failed due to excessive runtime of: $MAX_TEST_DURATION, info: $info")
                 }
             }
-
-            animator.getAnimationHandler().setProvider(null)
         }
+    }
 
-        job?.cancel()
+    companion object {
+        private const val MAX_TEST_DURATION = 300L
+    }
+}
+
+class FrameCallbackProvider(val scope: CoroutineScope) : AnimationFrameCallbackProvider {
+    private val callback = MutableSharedFlow<FrameCallback?>(replay = 2)
+    private var frameCount = 0L
+    val frames = MutableStateFlow(frameCount)
+
+    init {
+        scope.launch {
+            callback.collect {
+                withContext(Dispatchers.Main) {
+                    delay(1)
+                    it?.doFrame(frameCount)
+                }
+            }
+        }
     }
 
     override fun postFrameCallback(cb: FrameCallback) {
-        frames.value = Pair(frameCount++, cb)
+        frames.value = ++frameCount
+        callback.tryEmit(cb)
     }
 
     override fun postCommitCallback(runnable: Runnable) {}
@@ -108,8 +117,4 @@
     override fun getFrameDelay() = 1L
 
     override fun setFrameDelay(delay: Long) {}
-
-    companion object {
-        private const val MAX_TEST_DURATION = 200L
-    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/TaskbarDelegateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/TaskbarDelegateTest.kt
index a36e0ea..9bae7bd 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/TaskbarDelegateTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/navigationbar/TaskbarDelegateTest.kt
@@ -1,6 +1,7 @@
 package com.android.systemui.navigationbar
 
 import android.app.ActivityManager
+import android.os.Handler
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
@@ -61,6 +62,7 @@
     @Mock lateinit var mStatusBarKeyguardViewManager: StatusBarKeyguardViewManager
     @Mock lateinit var mStatusBarStateController: StatusBarStateController
     @Mock lateinit var mDisplayTracker: DisplayTracker
+    @Mock lateinit var mHandler: Handler
 
     @Before
     fun setup() {
@@ -69,6 +71,11 @@
         `when`(mLightBarControllerFactory.create(any())).thenReturn(mLightBarTransitionController)
         `when`(mNavBarHelper.currentSysuiState).thenReturn(mCurrentSysUiState)
         `when`(mSysUiState.setFlag(anyLong(), anyBoolean())).thenReturn(mSysUiState)
+        `when`(mHandler.post(any())).thenAnswer {
+            (it.arguments[0] as Runnable).run()
+            true
+        }
+
         mTaskStackChangeListeners = TaskStackChangeListeners.getTestInstance()
         mTaskbarDelegate =
             TaskbarDelegate(
@@ -76,6 +83,7 @@
                 mLightBarControllerFactory,
                 mStatusBarKeyguardViewManager,
                 mStatusBarStateController,
+                mHandler,
             )
         mTaskbarDelegate.setDependencies(
             mCommandQueue,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/shared/QSSettingsPackageRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/shared/QSSettingsPackageRepositoryTest.kt
new file mode 100644
index 0000000..765c02a
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/shared/QSSettingsPackageRepositoryTest.kt
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.shared
+
+import android.content.Context
+import android.content.Intent
+import android.content.pm.ActivityInfo
+import android.content.pm.PackageManager
+import android.content.pm.ResolveInfo
+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.android.systemui.user.data.repository.fakeUserRepository
+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.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.any
+import org.mockito.Mockito.anyInt
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+import org.mockito.kotlin.whenever
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class QSSettingsPackageRepositoryTest : SysuiTestCase() {
+
+    @get:Rule val mockito: MockitoRule = MockitoJUnit.rule()
+
+    @Mock private lateinit var context: Context
+    @Mock private lateinit var packageManager: PackageManager
+    @Mock private lateinit var resolveInfo: ResolveInfo
+    @Mock private lateinit var activityInfo: ActivityInfo
+
+    private val kosmos = testKosmos()
+    private val scope = kosmos.testScope
+    private val userRepository = kosmos.fakeUserRepository
+
+    private lateinit var underTest: QSSettingsPackageRepository
+
+    @Before
+    fun setUp() {
+        whenever(context.createContextAsUser(any(), anyInt())).thenReturn(context)
+        whenever(context.packageManager).thenReturn(packageManager)
+        whenever(packageManager.queryIntentActivities(any(Intent::class.java), anyInt()))
+            .thenReturn(listOf(resolveInfo))
+        resolveInfo.activityInfo = activityInfo
+
+        underTest = QSSettingsPackageRepository(context, scope, userRepository)
+    }
+
+    @Test
+    fun getSettingsPackageName_noInit_returnsDefaultPackageName() {
+        assertThat(underTest.getSettingsPackageName()).isEqualTo(DEFAULT_SETTINGS_PACKAGE_NAME)
+    }
+
+    @Test
+    fun getSettingsPackageName_repositoryWithCustomPackage_returnsCustomPackageName() {
+        scope.runTest {
+            activityInfo.packageName = CUSTOM_SETTINGS_PACKAGE_NAME
+
+            underTest.init()
+            runCurrent()
+
+            assertThat(underTest.getSettingsPackageName()).isEqualTo(CUSTOM_SETTINGS_PACKAGE_NAME)
+        }
+    }
+
+    @Test
+    fun getSettingsPackageName_noMatchingActivity_returnsDefaultPackageName() {
+        scope.runTest {
+            whenever(packageManager.queryIntentActivities(any(Intent::class.java), anyInt()))
+                .thenReturn(emptyList())
+
+            underTest.init()
+            runCurrent()
+
+            assertThat(underTest.getSettingsPackageName()).isEqualTo(DEFAULT_SETTINGS_PACKAGE_NAME)
+        }
+    }
+
+    @Test
+    fun getSettingsPackageName_nullActivityInfo_returnsDefaultPackageName() {
+        scope.runTest {
+            resolveInfo.activityInfo = null
+
+            underTest.init()
+            runCurrent()
+
+            assertThat(underTest.getSettingsPackageName()).isEqualTo(DEFAULT_SETTINGS_PACKAGE_NAME)
+        }
+    }
+
+    companion object {
+        private const val DEFAULT_SETTINGS_PACKAGE_NAME = "com.android.settings"
+        private const val CUSTOM_SETTINGS_PACKAGE_NAME = "com.android.test.settings"
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ColorCorrectionTileTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ColorCorrectionTileTest.java
index 028beb5..e5e8d4a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ColorCorrectionTileTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ColorCorrectionTileTest.java
@@ -39,6 +39,7 @@
 import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.QsEventLogger;
 import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.qs.shared.QSSettingsPackageRepository;
 import com.android.systemui.settings.UserTracker;
 import com.android.systemui.util.settings.FakeSettings;
 import com.android.systemui.util.settings.SecureSettings;
@@ -55,6 +56,7 @@
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 @SmallTest
 public class ColorCorrectionTileTest extends SysuiTestCase {
+    private static final String SETTINGS_PACKAGE_NAME = "com.android.settings";
 
     @Mock
     private QSHost mHost;
@@ -70,6 +72,8 @@
     private QsEventLogger mUiEventLogger;
     @Mock
     private UserTracker mUserTracker;
+    @Mock
+    private QSSettingsPackageRepository mQSSettingsPackageRepository;
 
     private TestableLooper mTestableLooper;
     private SecureSettings mSecureSettings;
@@ -83,6 +87,8 @@
         mTestableLooper = TestableLooper.get(this);
 
         when(mHost.getContext()).thenReturn(mContext);
+        when(mQSSettingsPackageRepository.getSettingsPackageName())
+                .thenReturn(SETTINGS_PACKAGE_NAME);
 
         mTile = new ColorCorrectionTile(
                 mHost,
@@ -95,7 +101,8 @@
                 mActivityStarter,
                 mQSLogger,
                 mUserTracker,
-                mSecureSettings
+                mSecureSettings,
+                mQSSettingsPackageRepository
         );
 
         mTile.initialize();
@@ -119,5 +126,6 @@
                 anyInt(), any());
         assertThat(IntentCaptor.getValue().getAction()).isEqualTo(
                 Settings.ACTION_COLOR_CORRECTION_SETTINGS);
+        assertThat(IntentCaptor.getValue().getPackage()).isEqualTo(SETTINGS_PACKAGE_NAME);
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ColorInversionTileTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ColorInversionTileTest.java
index a58dd63..cbde998 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ColorInversionTileTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ColorInversionTileTest.java
@@ -45,6 +45,7 @@
 import com.android.systemui.qs.QsEventLogger;
 import com.android.systemui.qs.flags.QsInCompose;
 import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.qs.shared.QSSettingsPackageRepository;
 import com.android.systemui.qs.tileimpl.QSTileImpl;
 import com.android.systemui.res.R;
 import com.android.systemui.settings.UserTracker;
@@ -59,15 +60,16 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-import java.util.List;
-
 import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
 import platform.test.runner.parameterized.Parameters;
 
+import java.util.List;
+
 @RunWith(ParameterizedAndroidJunit4.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 @SmallTest
 public class ColorInversionTileTest extends SysuiTestCase {
+    private static final String SETTINGS_PACKAGE_NAME = "com.android.settings";
     private static final Integer COLOR_INVERSION_DISABLED = 0;
     private static final Integer COLOR_INVERSION_ENABLED = 1;
 
@@ -90,6 +92,8 @@
     private QsEventLogger mUiEventLogger;
     @Mock
     private UserTracker mUserTracker;
+    @Mock
+    private QSSettingsPackageRepository mQSSettingsPackageRepository;
 
     private TestableLooper mTestableLooper;
     private SecureSettings mSecureSettings;
@@ -108,6 +112,8 @@
         mTestableLooper = TestableLooper.get(this);
 
         when(mHost.getContext()).thenReturn(mContext);
+        when(mQSSettingsPackageRepository.getSettingsPackageName())
+                .thenReturn(SETTINGS_PACKAGE_NAME);
 
         mTile = new ColorInversionTile(
                 mHost,
@@ -120,7 +126,8 @@
                 mActivityStarter,
                 mQSLogger,
                 mUserTracker,
-                mSecureSettings
+                mSecureSettings,
+                mQSSettingsPackageRepository
         );
 
         mTile.initialize();
@@ -144,6 +151,7 @@
                 anyInt(), any());
         assertThat(IntentCaptor.getValue().getAction()).isEqualTo(
                 Settings.ACTION_COLOR_INVERSION_SETTINGS);
+        assertThat(IntentCaptor.getValue().getPackage()).isEqualTo(SETTINGS_PACKAGE_NAME);
     }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/FontScalingTileTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/FontScalingTileTest.kt
index c854920..ae4da9d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/FontScalingTileTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/FontScalingTileTest.kt
@@ -32,11 +32,10 @@
 import com.android.systemui.qs.QSHost
 import com.android.systemui.qs.QsEventLogger
 import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.qs.shared.QSSettingsPackageRepository
 import com.android.systemui.statusbar.phone.SystemUIDialog
 import com.android.systemui.statusbar.policy.KeyguardStateController
 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.common.truth.Truth.assertThat
 import org.junit.After
@@ -49,8 +48,10 @@
 import org.mockito.Mock
 import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when`
 import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.whenever
 
 @RunWith(AndroidJUnit4::class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
@@ -68,21 +69,24 @@
     @Mock private lateinit var dialog: SystemUIDialog
     @Mock private lateinit var expandable: Expandable
     @Mock private lateinit var controller: DialogTransitionAnimator.Controller
+    @Mock private lateinit var settingsPackageRepository: QSSettingsPackageRepository
+
+    @Captor private lateinit var argumentCaptor: ArgumentCaptor<Runnable>
 
     private lateinit var testableLooper: TestableLooper
     private lateinit var systemClock: FakeSystemClock
     private lateinit var backgroundDelayableExecutor: FakeExecutor
     private lateinit var fontScalingTile: FontScalingTile
 
-    @Captor private lateinit var argumentCaptor: ArgumentCaptor<Runnable>
-
     @Before
     fun setUp() {
         MockitoAnnotations.initMocks(this)
         testableLooper = TestableLooper.get(this)
-        `when`(qsHost.getContext()).thenReturn(mContext)
-        `when`(fontScalingDialogDelegate.createDialog()).thenReturn(dialog)
-        `when`(expandable.dialogTransitionController(any())).thenReturn(controller)
+        whenever(qsHost.getContext()).thenReturn(mContext)
+        whenever(fontScalingDialogDelegate.createDialog()).thenReturn(dialog)
+        whenever(expandable.dialogTransitionController(any())).thenReturn(controller)
+        whenever(settingsPackageRepository.getSettingsPackageName())
+            .thenReturn(SETTINGS_PACKAGE_NAME)
         systemClock = FakeSystemClock()
         backgroundDelayableExecutor = FakeExecutor(systemClock)
 
@@ -100,6 +104,7 @@
                 keyguardStateController,
                 mDialogTransitionAnimator,
                 { fontScalingDialogDelegate },
+                settingsPackageRepository,
             )
         fontScalingTile.initialize()
         testableLooper.processAllMessages()
@@ -120,7 +125,7 @@
 
     @Test
     fun clickTile_screenUnlocked_showDialogAnimationFromView() {
-        `when`(keyguardStateController.isShowing).thenReturn(false)
+        whenever(keyguardStateController.isShowing).thenReturn(false)
         fontScalingTile.click(expandable)
         testableLooper.processAllMessages()
 
@@ -130,7 +135,7 @@
                 eq(null),
                 eq(true),
                 eq(true),
-                eq(false)
+                eq(false),
             )
         argumentCaptor.value.run()
         verify(mDialogTransitionAnimator).show(any(), any(), anyBoolean())
@@ -138,7 +143,7 @@
 
     @Test
     fun clickTile_onLockScreen_neverShowDialogAnimationFromView() {
-        `when`(keyguardStateController.isShowing).thenReturn(true)
+        whenever(keyguardStateController.isShowing).thenReturn(true)
         fontScalingTile.click(expandable)
         testableLooper.processAllMessages()
 
@@ -148,7 +153,7 @@
                 eq(null),
                 eq(true),
                 eq(true),
-                eq(false)
+                eq(false),
             )
         argumentCaptor.value.run()
         verify(mDialogTransitionAnimator, never()).show(any(), any(), anyBoolean())
@@ -159,5 +164,10 @@
         val intent: Intent? = fontScalingTile.getLongClickIntent()
 
         assertThat(intent!!.action).isEqualTo(Settings.ACTION_TEXT_READING_SETTINGS)
+        assertThat(intent.getPackage()).isEqualTo(SETTINGS_PACKAGE_NAME)
+    }
+
+    companion object {
+        private const val SETTINGS_PACKAGE_NAME = "com.android.settings"
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
index fc1d73b..3a3f537 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/ScreenRecordTileTest.java
@@ -35,6 +35,7 @@
 import android.app.Dialog;
 import android.media.projection.StopReason;
 import android.os.Handler;
+import android.platform.test.annotations.EnableFlags;
 import android.platform.test.flag.junit.FlagsParameterization;
 import android.service.quicksettings.Tile;
 import android.testing.TestableLooper;
@@ -52,6 +53,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.flags.QsDetailedView;
 import com.android.systemui.qs.flags.QsInCompose;
 import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor;
@@ -63,6 +65,7 @@
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 
 import org.junit.After;
+import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -70,11 +73,11 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-import java.util.List;
-
 import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
 import platform.test.runner.parameterized.Parameters;
 
+import java.util.List;
+
 @RunWith(ParameterizedAndroidJunit4.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
 @SmallTest
@@ -82,7 +85,8 @@
 
     @Parameters(name = "{0}")
     public static List<FlagsParameterization> getParams() {
-        return allCombinationsOf(FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX);
+        return allCombinationsOf(FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX,
+                QsDetailedView.FLAG_NAME);
     }
 
     @Mock
@@ -336,6 +340,30 @@
                 .notifyPermissionRequestDisplayed(mContext.getUserId());
     }
 
+    @Test
+    @EnableFlags(QsDetailedView.FLAG_NAME)
+    public void testNotStartingAndRecording_returnDetailsViewModel() {
+        when(mController.isStarting()).thenReturn(false);
+        when(mController.isRecording()).thenReturn(false);
+        mTile.getDetailsViewModel(Assert::assertNotNull);
+    }
+
+    @Test
+    @EnableFlags(QsDetailedView.FLAG_NAME)
+    public void testStarting_notReturnDetailsViewModel() {
+        when(mController.isStarting()).thenReturn(true);
+        when(mController.isRecording()).thenReturn(false);
+        mTile.getDetailsViewModel(Assert::assertNull);
+    }
+
+    @Test
+    @EnableFlags(QsDetailedView.FLAG_NAME)
+    public void testRecording_notReturnDetailsViewModel() {
+        when(mController.isStarting()).thenReturn(false);
+        when(mController.isRecording()).thenReturn(true);
+        mTile.getDetailsViewModel(Assert::assertNull);
+    }
+
     private QSTile.Icon createExpectedIcon(int resId) {
         if (QsInCompose.isEnabled()) {
             return new QSTileImpl.DrawableIconWithRes(mContext.getDrawable(resId), resId);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java
index 5c6657b..cfbc812 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/dialog/InternetAdapterTest.java
@@ -1,6 +1,6 @@
 package com.android.systemui.qs.tiles.dialog;
 
-import static com.android.systemui.qs.tiles.dialog.InternetDialogController.MAX_WIFI_ENTRY_COUNT;
+import static com.android.systemui.qs.tiles.dialog.InternetDetailsContentController.MAX_WIFI_ENTRY_COUNT;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -63,7 +63,7 @@
     @Mock
     private WifiEntry mWifiEntry;
     @Mock
-    private InternetDialogController mInternetDialogController;
+    private InternetDetailsContentController mInternetDetailsContentController;
     @Mock
     private Drawable mWifiDrawable;
     @Mock
@@ -86,7 +86,7 @@
         when(mWifiEntry.getTitle()).thenReturn(WIFI_TITLE);
         when(mWifiEntry.getSummary(false)).thenReturn(WIFI_SUMMARY);
 
-        mInternetAdapter = new InternetAdapter(mInternetDialogController, mScope);
+        mInternetAdapter = new InternetAdapter(mInternetDetailsContentController, mScope);
         mViewHolder = mInternetAdapter.onCreateViewHolder(new LinearLayout(mContext), 0);
         mInternetAdapter.setWifiEntries(Arrays.asList(mWifiEntry), 1 /* wifiEntriesCount */);
     }
@@ -124,7 +124,7 @@
 
     @Test
     public void onBindViewHolder_getWifiDrawableNull_noCrash() {
-        when(mInternetDialogController.getWifiDrawable(any())).thenReturn(null);
+        when(mInternetDetailsContentController.getWifiDrawable(any())).thenReturn(null);
 
         mInternetAdapter.onBindViewHolder(mViewHolder, 0);
 
@@ -133,7 +133,7 @@
 
     @Test
     public void onBindViewHolder_getWifiDrawableNotNull_setWifiIconDrawable() {
-        when(mInternetDialogController.getWifiDrawable(any())).thenReturn(mWifiDrawable);
+        when(mInternetDetailsContentController.getWifiDrawable(any())).thenReturn(mWifiDrawable);
 
         mInternetAdapter.onBindViewHolder(mViewHolder, 0);
 
@@ -232,7 +232,7 @@
 
         mViewHolder.onWifiClick(mWifiEntry, mock(View.class));
 
-        verify(mInternetDialogController).startActivityForDialog(any());
+        verify(mInternetDetailsContentController).startActivityForDialog(any());
         verify(mSpyContext, never()).startActivity(any());
     }
 
@@ -242,7 +242,7 @@
 
         mViewHolder.onWifiClick(mWifiEntry, mock(View.class));
 
-        verify(mInternetDialogController).connect(mWifiEntry);
+        verify(mInternetDetailsContentController).connect(mWifiEntry);
     }
 
     @Test
@@ -252,7 +252,7 @@
 
         mViewHolder.onWifiClick(mWifiEntry, mock(View.class));
 
-        verify(mInternetDialogController).launchWifiDetailsSetting(anyString(), any());
+        verify(mInternetDetailsContentController).launchWifiDetailsSetting(anyString(), any());
     }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileUserActionInteractorTest.kt
index 3bc53b27..0cf3734 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/colorcorrection/domain/interactor/ColorCorrectionTileUserActionInteractorTest.kt
@@ -23,29 +23,45 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.accessibility.data.repository.FakeColorCorrectionRepository
+import com.android.systemui.qs.shared.QSSettingsPackageRepository
 import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
 import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
 import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
 import com.android.systemui.qs.tiles.impl.colorcorrection.domain.model.ColorCorrectionTileModel
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+import org.mockito.kotlin.whenever
 
 @SmallTest
 @EnabledOnRavenwood
 @RunWith(AndroidJUnit4::class)
 class ColorCorrectionTileUserActionInteractorTest : SysuiTestCase() {
 
+    @get:Rule val mockito: MockitoRule = MockitoJUnit.rule()
+
+    @Mock private lateinit var settingsPackageRepository: QSSettingsPackageRepository
+
     private val testUser = UserHandle.CURRENT
     private val repository = FakeColorCorrectionRepository()
     private val inputHandler = FakeQSTileIntentUserInputHandler()
 
-    private val underTest =
-        ColorCorrectionUserActionInteractor(
-            repository,
-            inputHandler,
-        )
+    private lateinit var underTest: ColorCorrectionUserActionInteractor
+
+    @Before
+    fun setUp() {
+        whenever(settingsPackageRepository.getSettingsPackageName())
+            .thenReturn(SETTINGS_PACKAGE_NAME)
+
+        underTest =
+            ColorCorrectionUserActionInteractor(repository, inputHandler, settingsPackageRepository)
+    }
 
     @Test
     fun handleClickWhenEnabled() = runTest {
@@ -86,6 +102,11 @@
 
         QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput {
             assertThat(it.intent.action).isEqualTo(Settings.ACTION_COLOR_CORRECTION_SETTINGS)
+            assertThat(it.intent.getPackage()).isEqualTo(SETTINGS_PACKAGE_NAME)
         }
     }
+
+    companion object {
+        private const val SETTINGS_PACKAGE_NAME = "com.android.settings"
+    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingUserActionInteractorTest.kt
index d309554..9bd4895 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/fontscaling/domain/interactor/FontScalingUserActionInteractorTest.kt
@@ -29,6 +29,7 @@
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.qs.shared.QSSettingsPackageRepository
 import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
 import com.android.systemui.qs.tiles.base.actions.intentInputs
 import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
@@ -36,11 +37,7 @@
 import com.android.systemui.qs.tiles.impl.fontscaling.domain.model.FontScalingTileModel
 import com.android.systemui.statusbar.phone.FakeKeyguardStateController
 import com.android.systemui.statusbar.phone.SystemUIDialog
-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.google.common.truth.Truth
+import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Test
@@ -51,15 +48,14 @@
 import org.mockito.Mock
 import org.mockito.Mockito.never
 import org.mockito.Mockito.verify
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class FontScalingUserActionInteractorTest : SysuiTestCase() {
-    private val kosmos = Kosmos()
-    private val qsTileIntentUserActionHandler = FakeQSTileIntentUserInputHandler()
-    private val keyguardStateController = FakeKeyguardStateController()
-
-    private lateinit var underTest: FontScalingTileUserActionInteractor
 
     @Mock private lateinit var fontScalingDialogDelegate: FontScalingDialogDelegate
     @Mock private lateinit var mDialogTransitionAnimator: DialogTransitionAnimator
@@ -67,35 +63,47 @@
     @Mock private lateinit var activityStarter: ActivityStarter
     @Mock private lateinit var expandable: Expandable
     @Mock private lateinit var controller: DialogTransitionAnimator.Controller
+    @Mock private lateinit var settingsPackageRepository: QSSettingsPackageRepository
 
     @Captor private lateinit var argumentCaptor: ArgumentCaptor<Runnable>
 
+    private val kosmos = Kosmos()
+    private val scope = kosmos.testScope
+    private val qsTileIntentUserActionHandler = FakeQSTileIntentUserInputHandler()
+    private val keyguardStateController = FakeKeyguardStateController()
+
+    private lateinit var underTest: FontScalingTileUserActionInteractor
+
     @Before
     fun setup() {
         activityStarter = mock<ActivityStarter>()
         mDialogTransitionAnimator = mock<DialogTransitionAnimator>()
         dialog = mock<SystemUIDialog>()
-        fontScalingDialogDelegate =
-            mock<FontScalingDialogDelegate> { whenever(createDialog()).thenReturn(dialog) }
+        fontScalingDialogDelegate = mock<FontScalingDialogDelegate>()
+        whenever(fontScalingDialogDelegate.createDialog()).thenReturn(dialog)
         controller = mock<DialogTransitionAnimator.Controller>()
-        expandable =
-            mock<Expandable> { whenever(dialogTransitionController(any())).thenReturn(controller) }
+        expandable = mock<Expandable>()
+        whenever(expandable.dialogTransitionController(any())).thenReturn(controller)
+        settingsPackageRepository = mock<QSSettingsPackageRepository>()
+        whenever(settingsPackageRepository.getSettingsPackageName())
+            .thenReturn(SETTINGS_PACKAGE_NAME)
         argumentCaptor = ArgumentCaptor.forClass(Runnable::class.java)
 
         underTest =
             FontScalingTileUserActionInteractor(
-                kosmos.testScope.coroutineContext,
+                scope.coroutineContext,
                 qsTileIntentUserActionHandler,
                 { fontScalingDialogDelegate },
                 keyguardStateController,
                 mDialogTransitionAnimator,
-                activityStarter
+                activityStarter,
+                settingsPackageRepository,
             )
     }
 
     @Test
     fun clickTile_screenUnlocked_showDialogAnimationFromView() =
-        kosmos.testScope.runTest {
+        scope.runTest {
             keyguardStateController.isShowing = false
 
             underTest.handleInput(click(FontScalingTileModel, expandable = expandable))
@@ -106,7 +114,7 @@
                     eq(null),
                     eq(true),
                     eq(true),
-                    eq(false)
+                    eq(false),
                 )
             argumentCaptor.value.run()
             verify(mDialogTransitionAnimator).show(any(), any(), anyBoolean())
@@ -114,7 +122,7 @@
 
     @Test
     fun clickTile_onLockScreen_neverShowDialogAnimationFromView_butShowsDialog() =
-        kosmos.testScope.runTest {
+        scope.runTest {
             keyguardStateController.isShowing = true
 
             underTest.handleInput(click(FontScalingTileModel, expandable = expandable))
@@ -125,7 +133,7 @@
                     eq(null),
                     eq(true),
                     eq(true),
-                    eq(false)
+                    eq(false),
                 )
             argumentCaptor.value.run()
             verify(mDialogTransitionAnimator, never()).show(any(), any(), anyBoolean())
@@ -134,17 +142,20 @@
 
     @Test
     fun handleLongClick() =
-        kosmos.testScope.runTest {
+        scope.runTest {
             underTest.handleInput(QSTileInputTestKtx.longClick(FontScalingTileModel))
 
-            Truth.assertThat(qsTileIntentUserActionHandler.handledInputs).hasSize(1)
-            val intentInput = qsTileIntentUserActionHandler.intentInputs.last()
-            val actualIntentAction = intentInput.intent.action
-            val expectedIntentAction = Settings.ACTION_TEXT_READING_SETTINGS
-            Truth.assertThat(actualIntentAction).isEqualTo(expectedIntentAction)
+            assertThat(qsTileIntentUserActionHandler.handledInputs).hasSize(1)
+            val it = qsTileIntentUserActionHandler.intentInputs.last()
+            assertThat(it.intent.action).isEqualTo(Settings.ACTION_TEXT_READING_SETTINGS)
+            assertThat(it.intent.getPackage()).isEqualTo(SETTINGS_PACKAGE_NAME)
         }
 
     private class FontScalingTileTestView(context: Context) : View(context), LaunchableView {
         override fun setShouldBlockVisibilityChanges(block: Boolean) {}
     }
+
+    companion object {
+        private const val SETTINGS_PACKAGE_NAME = "com.android.settings"
+    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionUserActionInteractorTest.kt
index f574f79..3f77b86 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/inversion/domain/interactor/ColorInversionUserActionInteractorTest.kt
@@ -23,29 +23,45 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.accessibility.data.repository.FakeColorInversionRepository
+import com.android.systemui.qs.shared.QSSettingsPackageRepository
 import com.android.systemui.qs.tiles.base.actions.FakeQSTileIntentUserInputHandler
 import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandlerSubject
 import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx
 import com.android.systemui.qs.tiles.impl.inversion.domain.model.ColorInversionTileModel
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.junit.MockitoJUnit
+import org.mockito.junit.MockitoRule
+import org.mockito.kotlin.whenever
 
 @SmallTest
 @EnabledOnRavenwood
 @RunWith(AndroidJUnit4::class)
 class ColorInversionUserActionInteractorTest : SysuiTestCase() {
 
+    @get:Rule val mockito: MockitoRule = MockitoJUnit.rule()
+
+    @Mock private lateinit var settingsPackageRepository: QSSettingsPackageRepository
+
     private val testUser = UserHandle.CURRENT
     private val repository = FakeColorInversionRepository()
     private val inputHandler = FakeQSTileIntentUserInputHandler()
 
-    private val underTest =
-        ColorInversionUserActionInteractor(
-            repository,
-            inputHandler,
-        )
+    private lateinit var underTest: ColorInversionUserActionInteractor
+
+    @Before
+    fun setUp() {
+        whenever(settingsPackageRepository.getSettingsPackageName())
+            .thenReturn(SETTINGS_PACKAGE_NAME)
+
+        underTest =
+            ColorInversionUserActionInteractor(repository, inputHandler, settingsPackageRepository)
+    }
 
     @Test
     fun handleClickWhenEnabled() = runTest {
@@ -86,6 +102,11 @@
 
         QSTileIntentUserInputHandlerSubject.assertThat(inputHandler).handledOneIntentInput {
             assertThat(it.intent.action).isEqualTo(Settings.ACTION_COLOR_INVERSION_SETTINGS)
+            assertThat(it.intent.getPackage()).isEqualTo(SETTINGS_PACKAGE_NAME)
         }
     }
+
+    companion object {
+        private const val SETTINGS_PACKAGE_NAME = "com.android.settings"
+    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/view/SceneJankMonitorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/view/SceneJankMonitorTest.kt
new file mode 100644
index 0000000..984f8fd
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/ui/view/SceneJankMonitorTest.kt
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.view
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.internal.jank.Cuj
+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.deviceentry.domain.interactor.deviceUnlockedInteractor
+import com.android.systemui.jank.interactionJankMonitor
+import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository
+import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.runCurrent
+import com.android.systemui.kosmos.runTest
+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 org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.kotlin.any
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class SceneJankMonitorTest : SysuiTestCase() {
+
+    private val kosmos = testKosmos()
+    private val underTest: SceneJankMonitor = kosmos.sceneJankMonitorFactory.create()
+
+    @Before
+    fun setUp() {
+        underTest.activateIn(kosmos.testScope)
+    }
+
+    @Test
+    fun onTransitionStart_withProvidedCuj_beginsThatCuj() =
+        kosmos.runTest {
+            val cuj = 1337
+            underTest.onTransitionStart(
+                view = mock(),
+                from = Scenes.Communal,
+                to = Scenes.Dream,
+                cuj = cuj,
+            )
+            verify(interactionJankMonitor).begin(any(), eq(cuj))
+            verify(interactionJankMonitor, never()).end(anyInt())
+        }
+
+    @Test
+    fun onTransitionEnd_withProvidedCuj_endsThatCuj() =
+        kosmos.runTest {
+            val cuj = 1337
+            underTest.onTransitionEnd(from = Scenes.Communal, to = Scenes.Dream, cuj = cuj)
+            verify(interactionJankMonitor, never()).begin(any(), anyInt())
+            verify(interactionJankMonitor).end(cuj)
+        }
+
+    @Test
+    fun bouncer_authMethodPin() =
+        kosmos.runTest {
+            bouncer(
+                authenticationMethod = AuthenticationMethodModel.Pin,
+                appearCuj = Cuj.CUJ_LOCKSCREEN_PIN_APPEAR,
+                disappearCuj = Cuj.CUJ_LOCKSCREEN_PIN_DISAPPEAR,
+            )
+        }
+
+    @Test
+    fun bouncer_authMethodSim() =
+        kosmos.runTest {
+            bouncer(
+                authenticationMethod = AuthenticationMethodModel.Sim,
+                appearCuj = Cuj.CUJ_LOCKSCREEN_PIN_APPEAR,
+                disappearCuj = Cuj.CUJ_LOCKSCREEN_PIN_DISAPPEAR,
+                // When the auth method is SIM, unlocking doesn't work like normal. Instead of
+                // leaving the bouncer, the bouncer is switched over to the real authentication
+                // method when the SIM is unlocked.
+                //
+                // Therefore, there's no point in testing this code path and it will, in fact, fail
+                // to unlock.
+                testUnlockedDisappearance = false,
+            )
+        }
+
+    @Test
+    fun bouncer_authMethodPattern() =
+        kosmos.runTest {
+            bouncer(
+                authenticationMethod = AuthenticationMethodModel.Pattern,
+                appearCuj = Cuj.CUJ_LOCKSCREEN_PATTERN_APPEAR,
+                disappearCuj = Cuj.CUJ_LOCKSCREEN_PATTERN_DISAPPEAR,
+            )
+        }
+
+    @Test
+    fun bouncer_authMethodPassword() =
+        kosmos.runTest {
+            bouncer(
+                authenticationMethod = AuthenticationMethodModel.Password,
+                appearCuj = Cuj.CUJ_LOCKSCREEN_PASSWORD_APPEAR,
+                disappearCuj = Cuj.CUJ_LOCKSCREEN_PASSWORD_DISAPPEAR,
+            )
+        }
+
+    private fun Kosmos.bouncer(
+        authenticationMethod: AuthenticationMethodModel,
+        appearCuj: Int,
+        disappearCuj: Int,
+        testUnlockedDisappearance: Boolean = true,
+    ) {
+        // Set up state:
+        fakeAuthenticationRepository.setAuthenticationMethod(authenticationMethod)
+        runCurrent()
+
+        fun verifyCujCounts(
+            beginAppearCount: Int = 0,
+            beginDisappearCount: Int = 0,
+            endAppearCount: Int = 0,
+            endDisappearCount: Int = 0,
+        ) {
+            verify(interactionJankMonitor, times(beginAppearCount)).begin(any(), eq(appearCuj))
+            verify(interactionJankMonitor, times(beginDisappearCount))
+                .begin(any(), eq(disappearCuj))
+            verify(interactionJankMonitor, times(endAppearCount)).end(appearCuj)
+            verify(interactionJankMonitor, times(endDisappearCount)).end(disappearCuj)
+        }
+
+        // Precondition checks:
+        assertThat(deviceUnlockedInteractor.deviceUnlockStatus.value.isUnlocked).isFalse()
+        verifyCujCounts()
+
+        // Bouncer appears CUJ:
+        underTest.onTransitionStart(
+            view = mock(),
+            from = Scenes.Lockscreen,
+            to = Scenes.Bouncer,
+            cuj = null,
+        )
+        verifyCujCounts(beginAppearCount = 1)
+        underTest.onTransitionEnd(from = Scenes.Lockscreen, to = Scenes.Bouncer, cuj = null)
+        verifyCujCounts(beginAppearCount = 1, endAppearCount = 1)
+
+        // Bouncer disappear CUJ but it doesn't log because the device isn't unlocked.
+        underTest.onTransitionStart(
+            view = mock(),
+            from = Scenes.Bouncer,
+            to = Scenes.Lockscreen,
+            cuj = null,
+        )
+        verifyCujCounts(beginAppearCount = 1, endAppearCount = 1)
+        underTest.onTransitionEnd(from = Scenes.Bouncer, to = Scenes.Lockscreen, cuj = null)
+        verifyCujCounts(beginAppearCount = 1, endAppearCount = 1)
+
+        if (!testUnlockedDisappearance) {
+            return
+        }
+
+        // Unlock the device and transition away from the bouncer.
+        fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
+            SuccessFingerprintAuthenticationStatus(0, true)
+        )
+        runCurrent()
+        assertThat(deviceUnlockedInteractor.deviceUnlockStatus.value.isUnlocked).isTrue()
+
+        // Bouncer disappear CUJ and it doeslog because the device is unlocked.
+        underTest.onTransitionStart(
+            view = mock(),
+            from = Scenes.Bouncer,
+            to = Scenes.Gone,
+            cuj = null,
+        )
+        verifyCujCounts(beginAppearCount = 1, endAppearCount = 1, beginDisappearCount = 1)
+        underTest.onTransitionEnd(from = Scenes.Bouncer, to = Scenes.Gone, cuj = null)
+        verifyCujCounts(
+            beginAppearCount = 1,
+            endAppearCount = 1,
+            beginDisappearCount = 1,
+            endDisappearCount = 1,
+        )
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/DefaultScreenshotActionsProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/DefaultScreenshotActionsProviderTest.kt
index ba6518f..cd30edd 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/DefaultScreenshotActionsProviderTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/screenshot/DefaultScreenshotActionsProviderTest.kt
@@ -24,9 +24,9 @@
 import android.platform.test.annotations.EnableFlags
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.android.internal.logging.UiEventLogger
-import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.screenshot.ui.viewmodel.PreviewAction
+import com.android.systemui.shared.Flags
 import com.google.common.truth.Truth.assertThat
 import java.util.UUID
 import kotlin.test.Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index 0e93167..62c3604 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -85,7 +85,6 @@
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository;
 import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory;
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
 import com.android.systemui.keyguard.domain.interactor.NaturalScrollingSettingObserver;
 import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel;
@@ -335,16 +334,14 @@
         mFeatureFlags.set(Flags.QS_USER_DETAIL_SHORTCUT, false);
 
         mMainDispatcher = getMainDispatcher();
-        KeyguardInteractorFactory.WithDependencies keyguardInteractorDeps =
-                KeyguardInteractorFactory.create();
-        mFakeKeyguardRepository = keyguardInteractorDeps.getRepository();
+        mFakeKeyguardRepository = mKosmos.getKeyguardRepository();
         mFakeKeyguardClockRepository = new FakeKeyguardClockRepository();
         mKeyguardClockInteractor = mKosmos.getKeyguardClockInteractor();
-        mKeyguardInteractor = keyguardInteractorDeps.getKeyguardInteractor();
+        mKeyguardInteractor = mKosmos.getKeyguardInteractor();
         mShadeRepository = new FakeShadeRepository();
         mShadeAnimationInteractor = new ShadeAnimationInteractorLegacyImpl(
                 new ShadeAnimationRepository(), mShadeRepository);
-        mPowerInteractor = keyguardInteractorDeps.getPowerInteractor();
+        mPowerInteractor = mKosmos.getPowerInteractor();
         when(mKeyguardTransitionInteractor.isInTransitionWhere(any(), any())).thenReturn(
                 MutableStateFlow(false));
         when(mKeyguardTransitionInteractor.isInTransition(any(), any()))
@@ -531,9 +528,6 @@
 
         mNotificationPanelViewController = new NotificationPanelViewController(
                 mView,
-                mMainHandler,
-                mLayoutInflater,
-                mFeatureFlags,
                 coordinator, expansionHandler, mDynamicPrivacyController, mKeyguardBypassController,
                 mFalsingManager, new FalsingCollectorFake(),
                 mKeyguardStateController,
@@ -553,7 +547,6 @@
                 mKeyguardStatusBarViewComponentFactory,
                 mLockscreenShadeTransitionController,
                 mScrimController,
-                mUserManager,
                 mMediaDataManager,
                 mNotificationShadeDepthController,
                 mAmbientState,
@@ -564,7 +557,6 @@
                 mQsController,
                 mFragmentService,
                 mStatusBarService,
-                mContentResolver,
                 mShadeHeaderController,
                 mScreenOffAnimationController,
                 mLockscreenGestureLogger,
@@ -575,7 +567,6 @@
                 mKeyguardUnlockAnimationController,
                 mKeyguardIndicationController,
                 mNotificationListContainer,
-                mNotificationStackSizeCalculator,
                 mUnlockedScreenOffAnimationController,
                 systemClock,
                 mKeyguardClockInteractor,
@@ -594,7 +585,6 @@
                 new ResourcesSplitShadeStateController(),
                 mPowerInteractor,
                 mKeyguardClockPositionAlgorithm,
-                mNaturalScrollingSettingObserver,
                 mMSDLPlayer,
                 mBrightnessMirrorShowingInteractor);
         mNotificationPanelViewController.initDependencies(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QuickSettingsControllerImplTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QuickSettingsControllerImplTest.java
index 2e9d6e8..49cbb5a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QuickSettingsControllerImplTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/QuickSettingsControllerImplTest.java
@@ -53,7 +53,6 @@
 import com.android.systemui.plugins.qs.QS;
 import com.android.systemui.qs.flags.QSComposeFragment;
 import com.android.systemui.res.R;
-import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -365,7 +364,6 @@
     }
 
     @Test
-    @EnableFlags(FooterViewRefactor.FLAG_NAME)
     public void updateExpansion_partiallyExpanded_fullscreenFalse() {
         // WHEN QS are only partially expanded
         mQsController.setExpanded(true);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
index b5043ce..fe44c3e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/data/repository/ShadeRepositoryImplTest.kt
@@ -39,7 +39,7 @@
 
     @Before
     fun setUp() {
-        underTest = ShadeRepositoryImpl()
+        underTest = ShadeRepositoryImpl(testScope)
     }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/ConditionExtensionsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/ConditionExtensionsTest.kt
index 83fb14a..6b2c4b2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/ConditionExtensionsTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shared/condition/ConditionExtensionsTest.kt
@@ -9,9 +9,8 @@
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.emptyFlow
 import kotlinx.coroutines.flow.flowOf
-import kotlinx.coroutines.test.StandardTestDispatcher
 import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
 import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.Test
@@ -25,7 +24,7 @@
 
     @Before
     fun setUp() {
-        testScope = TestScope(StandardTestDispatcher())
+        testScope = TestScope(UnconfinedTestDispatcher())
     }
 
     @Test
@@ -34,11 +33,9 @@
             val flow = flowOf(true)
             val condition = flow.toCondition(scope = this, Condition.START_EAGERLY)
 
-            runCurrent()
             assertThat(condition.isConditionSet).isFalse()
 
             condition.start()
-            runCurrent()
             assertThat(condition.isConditionSet).isTrue()
             assertThat(condition.isConditionMet).isTrue()
         }
@@ -49,11 +46,9 @@
             val flow = flowOf(false)
             val condition = flow.toCondition(scope = this, Condition.START_EAGERLY)
 
-            runCurrent()
             assertThat(condition.isConditionSet).isFalse()
 
             condition.start()
-            runCurrent()
             assertThat(condition.isConditionSet).isTrue()
             assertThat(condition.isConditionMet).isFalse()
         }
@@ -65,7 +60,6 @@
             val condition = flow.toCondition(scope = this, Condition.START_EAGERLY)
             condition.start()
 
-            runCurrent()
             assertThat(condition.isConditionSet).isFalse()
             assertThat(condition.isConditionMet).isFalse()
         }
@@ -78,11 +72,10 @@
                 flow.toCondition(
                     scope = this,
                     strategy = Condition.START_EAGERLY,
-                    initialValue = true
+                    initialValue = true,
                 )
             condition.start()
 
-            runCurrent()
             assertThat(condition.isConditionSet).isTrue()
             assertThat(condition.isConditionMet).isTrue()
         }
@@ -95,11 +88,10 @@
                 flow.toCondition(
                     scope = this,
                     strategy = Condition.START_EAGERLY,
-                    initialValue = false
+                    initialValue = false,
                 )
             condition.start()
 
-            runCurrent()
             assertThat(condition.isConditionSet).isTrue()
             assertThat(condition.isConditionMet).isFalse()
         }
@@ -111,16 +103,13 @@
             val condition = flow.toCondition(scope = this, strategy = Condition.START_EAGERLY)
             condition.start()
 
-            runCurrent()
             assertThat(condition.isConditionSet).isTrue()
             assertThat(condition.isConditionMet).isFalse()
 
             flow.value = true
-            runCurrent()
             assertThat(condition.isConditionMet).isTrue()
 
             flow.value = false
-            runCurrent()
             assertThat(condition.isConditionMet).isFalse()
 
             condition.stop()
@@ -131,15 +120,12 @@
         testScope.runTest {
             val flow = MutableSharedFlow<Boolean>()
             val condition = flow.toCondition(scope = this, strategy = Condition.START_EAGERLY)
-            runCurrent()
             assertThat(flow.subscriptionCount.value).isEqualTo(0)
 
             condition.start()
-            runCurrent()
             assertThat(flow.subscriptionCount.value).isEqualTo(1)
 
             condition.stop()
-            runCurrent()
             assertThat(flow.subscriptionCount.value).isEqualTo(0)
         }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/CommandQueueTest.java
index b730b37..2020d0d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -18,6 +18,8 @@
 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.service.quickaccesswallet.Flags.FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP;
+import static android.service.quickaccesswallet.Flags.FLAG_LAUNCH_WALLET_VIA_SYSUI_CALLBACKS;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowInsetsController.BEHAVIOR_DEFAULT;
 
@@ -367,6 +369,15 @@
     }
 
     @Test
+    @EnableFlags({FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP,
+            FLAG_LAUNCH_WALLET_VIA_SYSUI_CALLBACKS})
+    public void testWalletLaunchGesture() {
+        mCommandQueue.onWalletLaunchGestureDetected();
+        waitForIdleSync();
+        verify(mCallbacks).onWalletLaunchGestureDetected();
+    }
+
+    @Test
     public void testShowPipMenu() {
         mCommandQueue.showPictureInPictureMenu();
         waitForIdleSync();
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/KeyguardIndicationControllerBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/KeyguardIndicationControllerBaseTest.java
index 33a0803..1938f42 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/KeyguardIndicationControllerBaseTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/KeyguardIndicationControllerBaseTest.java
@@ -264,7 +264,9 @@
 
     @After
     public void tearDown() throws Exception {
-        mTextView.setAnimationsEnabled(true);
+        if (mTextView != null) {
+            mTextView.setAnimationsEnabled(true);
+        }
         if (mController != null) {
             mController.destroy();
             mController = null;
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
index 4a3be44..20474c8 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
@@ -81,6 +81,7 @@
 import com.android.systemui.flags.DisableSceneContainer;
 import com.android.systemui.flags.EnableSceneContainer;
 import com.android.systemui.flags.FakeFeatureFlagsClassic;
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
 import com.android.systemui.log.LogWtfHandlerRule;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.recents.OverviewProxyService;
@@ -90,6 +91,7 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
 import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
+import com.android.systemui.statusbar.notification.row.shared.LockscreenOtpRedaction;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.util.concurrency.FakeExecutor;
@@ -115,6 +117,7 @@
 import java.util.Collection;
 import java.util.List;
 import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
 
 import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
 import platform.test.runner.parameterized.Parameters;
@@ -169,6 +172,10 @@
     @Mock
     private DeviceUnlockedInteractor mDeviceUnlockedInteractor;
     @Mock
+    private Lazy<KeyguardInteractor> mKeyguardInteractorLazy;
+    @Mock
+    private KeyguardInteractor mKeyguardInteractor;
+    @Mock
     private StateFlow<DeviceUnlockStatus> mDeviceUnlockStatusStateFlow;
 
     private UserInfo mCurrentUser;
@@ -181,6 +188,7 @@
     private NotificationEntry mSecondaryUserNotif;
     private NotificationEntry mWorkProfileNotif;
     private NotificationEntry mSensitiveContentNotif;
+    private long mSensitiveNotifPostTime;
     private final FakeFeatureFlagsClassic mFakeFeatureFlags = new FakeFeatureFlagsClassic();
     private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
     private final FakeExecutor mBackgroundExecutor = new FakeExecutor(mFakeSystemClock);
@@ -246,13 +254,17 @@
         mSensitiveContentNotif = new NotificationEntryBuilder()
                 .setNotification(notifWithPrivateVisibility)
                 .setUser(new UserHandle(mCurrentUser.id))
+                .setPostTime(System.currentTimeMillis())
                 .build();
         mSensitiveContentNotif.setRanking(new RankingBuilder(mCurrentUserNotif.getRanking())
                 .setChannel(channel)
                 .setSensitiveContent(true)
                 .setVisibilityOverride(VISIBILITY_NO_OVERRIDE).build());
+        mSensitiveNotifPostTime = mSensitiveContentNotif.getSbn().getPostTime();
         when(mNotifCollection.getEntry(mWorkProfileNotif.getKey())).thenReturn(mWorkProfileNotif);
-
+        when(mKeyguardInteractorLazy.get()).thenReturn(mKeyguardInteractor);
+        when(mKeyguardInteractor.isKeyguardDismissible())
+                .thenReturn(mock(StateFlow.class));
         mLockscreenUserManager = new TestNotificationLockscreenUserManager(mContext);
         mLockscreenUserManager.setUpWithPresenter(mPresenter);
 
@@ -504,11 +516,85 @@
     }
 
     @Test
+    @EnableFlags(LockscreenOtpRedaction.FLAG_NAME)
+    public void testHasSensitiveContent_notRedactedIfNotLocked() {
+        // Allow private notifications for this user
+        mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
+                mCurrentUser.id);
+        changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
+        // Claim the device was last locked 1 day ago
+        mLockscreenUserManager.mLastLockTime
+                .set(mSensitiveNotifPostTime - TimeUnit.DAYS.toMillis(1));
+        // Device is not currently locked
+        when(mKeyguardManager.isDeviceLocked()).thenReturn(false);
+
+        // Sensitive Content notifications are always redacted
+        assertEquals(REDACTION_TYPE_NONE,
+                mLockscreenUserManager.getRedactionType(mSensitiveContentNotif));
+    }
+
+    @Test
+    @EnableFlags(LockscreenOtpRedaction.FLAG_NAME)
+    public void testHasSensitiveContent_notRedactedIfUnlockedSinceReceipt() {
+        // Allow private notifications for this user
+        mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
+                mCurrentUser.id);
+        changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
+        when(mKeyguardManager.isDeviceLocked()).thenReturn(true);
+        // Device was locked after this notification arrived
+        mLockscreenUserManager.mLastLockTime
+                .set(mSensitiveNotifPostTime + TimeUnit.DAYS.toMillis(1));
+
+        // Sensitive Content notifications are always redacted
+        assertEquals(REDACTION_TYPE_NONE,
+                mLockscreenUserManager.getRedactionType(mSensitiveContentNotif));
+    }
+
+    @Test
+    @EnableFlags(LockscreenOtpRedaction.FLAG_NAME)
+    public void testHasSensitiveContent_notRedactedIfNotLockedForLongEnough() {
+        // Allow private notifications for this user
+        mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
+                mCurrentUser.id);
+        changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
+        // Device has been locked for 1 second before the notification came in, which is too short
+        mLockscreenUserManager.mLastLockTime
+                .set(mSensitiveNotifPostTime - TimeUnit.SECONDS.toMillis(1));
+        when(mKeyguardManager.isDeviceLocked()).thenReturn(true);
+
+        // Sensitive Content notifications are always redacted
+        assertEquals(REDACTION_TYPE_NONE,
+                mLockscreenUserManager.getRedactionType(mSensitiveContentNotif));
+    }
+
+    @Test
+    @DisableFlags(LockscreenOtpRedaction.FLAG_NAME)
+    public void testHasSensitiveContent_notRedactedFlagDisabled() {
+        // Allow private notifications for this user
+        mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
+                mCurrentUser.id);
+        changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
+        // Claim the device was last locked 1 day ago
+        mLockscreenUserManager.mLastLockTime
+                .set(mSensitiveNotifPostTime - TimeUnit.DAYS.toMillis(1));
+        when(mKeyguardManager.isDeviceLocked()).thenReturn(true);
+
+        // Sensitive Content notifications are always redacted
+        assertEquals(REDACTION_TYPE_NONE,
+                mLockscreenUserManager.getRedactionType(mSensitiveContentNotif));
+    }
+
+    @Test
+    @EnableFlags(LockscreenOtpRedaction.FLAG_NAME)
     public void testHasSensitiveContent_redacted() {
         // Allow private notifications for this user
         mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
                 mCurrentUser.id);
         changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
+        when(mKeyguardManager.isDeviceLocked()).thenReturn(true);
+        // Claim the device was last unlocked 1 day ago
+        mLockscreenUserManager.mLastLockTime
+                .set(mSensitiveNotifPostTime - TimeUnit.DAYS.toMillis(1));
 
         // Sensitive Content notifications are always redacted
         assertEquals(REDACTION_TYPE_SENSITIVE_CONTENT,
@@ -1066,7 +1152,9 @@
                     mock(DumpManager.class),
                     mock(LockPatternUtils.class),
                     mFakeFeatureFlags,
-                    mDeviceUnlockedInteractorLazy
+                    mDeviceUnlockedInteractorLazy,
+                    mKeyguardInteractorLazy,
+                    null //CoroutineScope
             );
         }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/StatusBarSignalPolicyTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/StatusBarSignalPolicyTest.kt
index 1730553..5f73ac4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/StatusBarSignalPolicyTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/StatusBarSignalPolicyTest.kt
@@ -25,104 +25,100 @@
 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.kosmos.runTest
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
 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.pipeline.ethernet.domain.ethernetInteractor
+import com.android.systemui.statusbar.pipeline.ethernet.shared.StatusBarSignalPolicyRefactorEthernet
+import com.android.systemui.statusbar.pipeline.shared.data.repository.connectivityRepository
+import com.android.systemui.statusbar.pipeline.shared.data.repository.fake
 import com.android.systemui.statusbar.policy.SecurityController
-import com.android.systemui.tuner.TunerService
-import com.android.systemui.util.CarrierConfigTracker
+import com.android.systemui.testKosmos
+import com.android.systemui.tuner.tunerService
 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 kotlin.test.Test
 import org.junit.Before
 import org.junit.runner.RunWith
 import org.mockito.Mockito.verify
+import org.mockito.kotlin.any
 import org.mockito.kotlin.clearInvocations
+import org.mockito.kotlin.eq
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.never
 import org.mockito.kotlin.verifyNoMoreInteractions
-import kotlin.test.Test
 
-@OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class StatusBarSignalPolicyTest : SysuiTestCase() {
-    private val kosmos = Kosmos().also { it.testCase = this }
+    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
 
-    private lateinit var underTest: StatusBarSignalPolicy
-
-    private val testScope = TestScope()
-
-    private val javaAdapter = JavaAdapter(testScope.backgroundScope)
-    private val airplaneModeInteractor = kosmos.airplaneModeInteractor
-
+    private val javaAdapter = JavaAdapter(kosmos.testScope.backgroundScope)
     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(
+    private val Kosmos.underTest by
+        Kosmos.Fixture {
+            StatusBarSignalPolicy(
                 mContext,
                 statusBarIconController,
-                carrierConfigTracker,
                 networkController,
                 securityController,
                 tunerService,
                 javaAdapter,
                 airplaneModeInteractor,
+                ethernetInteractor,
             )
+        }
 
+    private lateinit var slotAirplane: String
+    private lateinit var slotEthernet: String
+
+    @Before
+    fun setup() {
         slotAirplane = mContext.getString(R.string.status_bar_airplane)
+        slotEthernet = mContext.getString(R.string.status_bar_ethernet)
     }
 
     @Test
     @EnableFlags(FLAG_STATUS_BAR_SIGNAL_POLICY_REFACTOR)
     fun airplaneModeViaInteractor_statusBarSignalPolicyRefactorFlagEnabled_iconUpdated() =
-        testScope.runTest {
+        kosmos.runTest {
             underTest.start()
+            clearInvocations(statusBarIconController)
+
             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 {
+        kosmos.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()
-            verifyNoMoreInteractions(statusBarIconController)
+            verify(statusBarIconController, never()).setIconVisibility(eq(slotAirplane), any())
 
             underTest.setIsAirplaneMode(IconState(false, TelephonyIcons.FLIGHT_MODE_ICON, ""))
-            runCurrent()
-            verifyNoMoreInteractions(statusBarIconController)
+            verify(statusBarIconController, never()).setIconVisibility(eq(slotAirplane), any())
         }
 
     @Test
     @EnableFlags(FLAG_STATUS_BAR_SIGNAL_POLICY_REFACTOR)
     fun statusBarSignalPolicyInitialization_statusBarSignalPolicyRefactorFlagEnabled_initNoOp() =
-        testScope.runTest {
+        kosmos.runTest {
             // Make sure StatusBarSignalPolicy.init does no initialization when
             // the refactor flag is disabled.
             underTest.init()
@@ -132,42 +128,126 @@
     @Test
     @DisableFlags(FLAG_STATUS_BAR_SIGNAL_POLICY_REFACTOR)
     fun airplaneModeViaSignalCallback_statusBarSignalPolicyRefactorFlagDisabled_iconUpdated() =
-        testScope.runTest {
+        kosmos.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 {
+        kosmos.runTest {
             underTest.init()
 
             // Make sure changing airplane mode from airplaneModeRepository does nothing
             // if the StatusBarSignalPolicyRefactor is not enabled.
             airplaneModeInteractor.setIsAirplaneMode(true)
-            runCurrent()
-            verifyNoMoreInteractions(statusBarIconController)
+            verify(statusBarIconController, never()).setIconVisibility(eq(slotAirplane), any())
 
             airplaneModeInteractor.setIsAirplaneMode(false)
-            runCurrent()
-            verifyNoMoreInteractions(statusBarIconController)
+            verify(statusBarIconController, never()).setIconVisibility(eq(slotAirplane), any())
         }
 
     @Test
     @DisableFlags(FLAG_STATUS_BAR_SIGNAL_POLICY_REFACTOR)
     fun statusBarSignalPolicyInitialization_statusBarSignalPolicyRefactorFlagDisabled_startNoOp() =
-        testScope.runTest {
+        kosmos.runTest {
             // Make sure StatusBarSignalPolicy.start does no initialization when
             // the refactor flag is disabled.
             underTest.start()
             verifyNoMoreInteractions(securityController, networkController, tunerService)
         }
+
+    @Test
+    @EnableFlags(FLAG_STATUS_BAR_SIGNAL_POLICY_REFACTOR)
+    @DisableFlags(StatusBarSignalPolicyRefactorEthernet.FLAG_NAME)
+    fun ethernetIconViaSignalCallback_refactorFlagDisabled_iconUpdated() =
+        kosmos.runTest {
+            underTest.start()
+            clearInvocations(statusBarIconController)
+
+            underTest.setEthernetIndicators(
+                IconState(/* visible= */ true, /* icon= */ 1, /* contentDescription= */ "Ethernet")
+            )
+            verify(statusBarIconController).setIconVisibility(slotEthernet, true)
+
+            underTest.setEthernetIndicators(
+                IconState(
+                    /* visible= */ false,
+                    /* icon= */ 0,
+                    /* contentDescription= */ "No ethernet",
+                )
+            )
+            verify(statusBarIconController).setIconVisibility(slotEthernet, false)
+        }
+
+    @Test
+    @EnableFlags(
+        FLAG_STATUS_BAR_SIGNAL_POLICY_REFACTOR,
+        StatusBarSignalPolicyRefactorEthernet.FLAG_NAME,
+    )
+    fun ethernetIconViaSignalCallback_refactorFlagEnabled_iconNotUpdated() =
+        kosmos.runTest {
+            underTest.start()
+            clearInvocations(statusBarIconController)
+
+            underTest.setEthernetIndicators(
+                IconState(/* visible= */ true, /* icon= */ 1, /* contentDescription= */ "Ethernet")
+            )
+            verify(statusBarIconController, never()).setIconVisibility(eq(slotEthernet), any())
+
+            underTest.setEthernetIndicators(
+                IconState(
+                    /* visible= */ false,
+                    /* icon= */ 0,
+                    /* contentDescription= */ "No ethernet",
+                )
+            )
+            verify(statusBarIconController, never()).setIconVisibility(eq(slotEthernet), any())
+        }
+
+    @Test
+    @EnableFlags(FLAG_STATUS_BAR_SIGNAL_POLICY_REFACTOR)
+    @DisableFlags(StatusBarSignalPolicyRefactorEthernet.FLAG_NAME)
+    fun ethernetIconViaInteractor_refactorFlagDisabled_iconNotUpdated() =
+        kosmos.runTest {
+            underTest.start()
+            clearInvocations(statusBarIconController)
+
+            connectivityRepository.fake.setEthernetConnected(default = true, validated = true)
+            verify(statusBarIconController, never()).setIconVisibility(eq(slotEthernet), any())
+
+            connectivityRepository.fake.setEthernetConnected(default = false, validated = false)
+            verify(statusBarIconController, never()).setIconVisibility(eq(slotEthernet), any())
+
+            connectivityRepository.fake.setEthernetConnected(default = true, validated = false)
+            verify(statusBarIconController, never()).setIconVisibility(eq(slotEthernet), any())
+        }
+
+    @Test
+    @EnableFlags(
+        FLAG_STATUS_BAR_SIGNAL_POLICY_REFACTOR,
+        StatusBarSignalPolicyRefactorEthernet.FLAG_NAME,
+    )
+    fun ethernetIconViaInteractor_refactorFlagEnabled_iconUpdated() =
+        kosmos.runTest {
+            underTest.start()
+            clearInvocations(statusBarIconController)
+
+            connectivityRepository.fake.setEthernetConnected(default = true, validated = true)
+            verify(statusBarIconController).setIconVisibility(slotEthernet, true)
+
+            connectivityRepository.fake.setEthernetConnected(default = false, validated = false)
+            verify(statusBarIconController).setIconVisibility(slotEthernet, false)
+
+            clearInvocations(statusBarIconController)
+
+            connectivityRepository.fake.setEthernetConnected(default = true, validated = false)
+            verify(statusBarIconController).setIconVisibility(slotEthernet, true)
+        }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
index a62d9d5..0061c41 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt
@@ -132,7 +132,7 @@
             val latest by collectLastValue(underTest.chip)
 
             repo.setOngoingCallState(
-                inCallModel(startTimeMs = 1000, notificationIcon = mock<StatusBarIconView>())
+                inCallModel(startTimeMs = 1000, notificationIcon = createStatusBarIconViewOrNull())
             )
 
             assertThat((latest as OngoingActivityChipModel.Shown).icon)
@@ -147,11 +147,12 @@
 
     @Test
     @EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON)
+    @DisableFlags(StatusBarConnectedDisplays.FLAG_NAME)
     fun chip_positiveStartTime_notifIconFlagOn_iconIsNotifIcon() =
         testScope.runTest {
             val latest by collectLastValue(underTest.chip)
 
-            val notifIcon = mock<StatusBarIconView>()
+            val notifIcon = createStatusBarIconViewOrNull()
             repo.setOngoingCallState(inCallModel(startTimeMs = 1000, notificationIcon = notifIcon))
 
             assertThat((latest as OngoingActivityChipModel.Shown).icon)
@@ -165,6 +166,24 @@
 
     @Test
     @EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON, StatusBarConnectedDisplays.FLAG_NAME)
+    fun chip_positiveStartTime_notifIconFlagOn_cdFlagOn_iconIsNotifKeyIcon() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chip)
+
+            repo.setOngoingCallState(
+                inCallModel(
+                    startTimeMs = 1000,
+                    notificationIcon = createStatusBarIconViewOrNull(),
+                    notificationKey = "notifKey",
+                )
+            )
+
+            assertThat((latest as OngoingActivityChipModel.Shown).icon)
+                .isEqualTo(OngoingActivityChipModel.ChipIcon.StatusBarNotificationIcon("notifKey"))
+        }
+
+    @Test
+    @EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON, StatusBarConnectedDisplays.FLAG_NAME)
     fun chip_positiveStartTime_notifIconAndConnectedDisplaysFlagOn_iconIsNotifIcon() =
         testScope.runTest {
             val latest by collectLastValue(underTest.chip)
@@ -192,7 +211,7 @@
             val latest by collectLastValue(underTest.chip)
 
             repo.setOngoingCallState(
-                inCallModel(startTimeMs = 0, notificationIcon = mock<StatusBarIconView>())
+                inCallModel(startTimeMs = 0, notificationIcon = createStatusBarIconViewOrNull())
             )
 
             assertThat((latest as OngoingActivityChipModel.Shown).icon)
@@ -207,11 +226,12 @@
 
     @Test
     @EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON)
-    fun chip_zeroStartTime_notifIconFlagOn_iconIsNotifIcon() =
+    @DisableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+    fun chip_zeroStartTime_notifIconFlagOn_cdFlagOff_iconIsNotifIcon() =
         testScope.runTest {
             val latest by collectLastValue(underTest.chip)
 
-            val notifIcon = mock<StatusBarIconView>()
+            val notifIcon = createStatusBarIconViewOrNull()
             repo.setOngoingCallState(inCallModel(startTimeMs = 0, notificationIcon = notifIcon))
 
             assertThat((latest as OngoingActivityChipModel.Shown).icon)
@@ -224,8 +244,27 @@
         }
 
     @Test
+    @EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON, StatusBarConnectedDisplays.FLAG_NAME)
+    fun chip_zeroStartTime_notifIconFlagOn_cdFlagOn_iconIsNotifKeyIcon() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chip)
+
+            repo.setOngoingCallState(
+                inCallModel(
+                    startTimeMs = 0,
+                    notificationIcon = createStatusBarIconViewOrNull(),
+                    notificationKey = "notifKey",
+                )
+            )
+
+            assertThat((latest as OngoingActivityChipModel.Shown).icon)
+                .isEqualTo(OngoingActivityChipModel.ChipIcon.StatusBarNotificationIcon("notifKey"))
+        }
+
+    @Test
     @EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON)
-    fun chip_notifIconFlagOn_butNullNotifIcon_iconIsPhone() =
+    @DisableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+    fun chip_notifIconFlagOn_butNullNotifIcon_cdFlagOff_iconIsPhone() =
         testScope.runTest {
             val latest by collectLastValue(underTest.chip)
 
@@ -242,6 +281,24 @@
         }
 
     @Test
+    @EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON, StatusBarConnectedDisplays.FLAG_NAME)
+    fun chip_notifIconFlagOn_butNullNotifIcon_iconNotifKey() =
+        testScope.runTest {
+            val latest by collectLastValue(underTest.chip)
+
+            repo.setOngoingCallState(
+                inCallModel(
+                    startTimeMs = 1000,
+                    notificationIcon = null,
+                    notificationKey = "notifKey",
+                )
+            )
+
+            assertThat((latest as OngoingActivityChipModel.Shown).icon)
+                .isEqualTo(OngoingActivityChipModel.ChipIcon.StatusBarNotificationIcon("notifKey"))
+        }
+
+    @Test
     fun chip_positiveStartTime_colorsAreThemed() =
         testScope.runTest {
             val latest by collectLastValue(underTest.chip)
@@ -330,4 +387,13 @@
 
             verify(kosmos.activityStarter).postStartActivityDismissingKeyguard(intent, null)
         }
+
+    companion object {
+        fun createStatusBarIconViewOrNull(): StatusBarIconView? =
+            if (StatusBarConnectedDisplays.isEnabled) {
+                null
+            } else {
+                mock<StatusBarIconView>()
+            }
+    }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModelTest.kt
deleted file mode 100644
index 69a7627..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModelTest.kt
+++ /dev/null
@@ -1,146 +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.chips.notification.demo.ui.viewmodel
-
-import android.content.packageManager
-import android.graphics.drawable.BitmapDrawable
-import android.platform.test.annotations.DisableFlags
-import android.platform.test.annotations.EnableFlags
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.kosmos.testScope
-import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
-import com.android.systemui.statusbar.chips.ui.model.ColorsModel
-import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
-import com.android.systemui.statusbar.commandline.CommandRegistry
-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 DemoNotifChipViewModelTest : SysuiTestCase() {
-    private val kosmos = testKosmos()
-    private val testScope = kosmos.testScope
-    private val commandRegistry = kosmos.commandRegistry
-    private val pw = PrintWriter(StringWriter())
-
-    private val underTest = kosmos.demoNotifChipViewModel
-
-    @Before
-    fun setUp() {
-        underTest.start()
-        whenever(kosmos.packageManager.getApplicationIcon(any<String>()))
-            .thenReturn(BitmapDrawable())
-    }
-
-    @Test
-    @DisableFlags(StatusBarNotifChips.FLAG_NAME)
-    fun chip_flagOff_hidden() =
-        testScope.runTest {
-            val latest by collectLastValue(underTest.chip)
-
-            addDemoNotifChip()
-
-            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
-        }
-
-    @Test
-    @EnableFlags(StatusBarNotifChips.FLAG_NAME)
-    fun chip_noPackage_hidden() =
-        testScope.runTest {
-            val latest by collectLastValue(underTest.chip)
-
-            commandRegistry.onShellCommand(pw, arrayOf("demo-notif"))
-
-            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
-        }
-
-    @Test
-    @EnableFlags(StatusBarNotifChips.FLAG_NAME)
-    fun chip_hasPackage_shown() =
-        testScope.runTest {
-            val latest by collectLastValue(underTest.chip)
-
-            commandRegistry.onShellCommand(pw, arrayOf("demo-notif", "-p", "com.android.systemui"))
-
-            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
-        }
-
-    @Test
-    @EnableFlags(StatusBarNotifChips.FLAG_NAME)
-    fun chip_hasText_shownWithText() =
-        testScope.runTest {
-            val latest by collectLastValue(underTest.chip)
-
-            commandRegistry.onShellCommand(
-                pw,
-                arrayOf("demo-notif", "-p", "com.android.systemui", "-t", "test"),
-            )
-
-            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Text::class.java)
-        }
-
-    @Test
-    @EnableFlags(StatusBarNotifChips.FLAG_NAME)
-    fun chip_supportsColor() =
-        testScope.runTest {
-            val latest by collectLastValue(underTest.chip)
-
-            commandRegistry.onShellCommand(
-                pw,
-                arrayOf("demo-notif", "-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(StatusBarNotifChips.FLAG_NAME)
-    fun chip_hasHideArg_hidden() =
-        testScope.runTest {
-            val latest by collectLastValue(underTest.chip)
-
-            // First, show a chip
-            addDemoNotifChip()
-            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
-
-            // Then, hide the chip
-            commandRegistry.onShellCommand(pw, arrayOf("demo-notif", "--hide"))
-
-            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
-        }
-
-    private fun addDemoNotifChip() {
-        addDemoNotifChip(commandRegistry, pw)
-    }
-
-    companion object {
-        fun addDemoNotifChip(commandRegistry: CommandRegistry, pw: PrintWriter) {
-            commandRegistry.onShellCommand(pw, arrayOf("demo-notif", "-p", "com.android.systemui"))
-        }
-    }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractorTest.kt
index 9ad1f40..fe15eac 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractorTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.chips.notification.domain.interactor
 
+import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
@@ -52,7 +53,7 @@
                     promotedContent = PROMOTED_CONTENT,
                 )
 
-            val underTest = factory.create(startingNotif)
+            val underTest = factory.create(startingNotif, creationTime = 1)
 
             val latest by collectLastValue(underTest.notificationChip)
 
@@ -71,7 +72,8 @@
                         key = "notif1",
                         statusBarChipIcon = originalIconView,
                         promotedContent = PROMOTED_CONTENT,
-                    )
+                    ),
+                    creationTime = 1,
                 )
 
             val latest by collectLastValue(underTest.notificationChip)
@@ -99,7 +101,8 @@
                         key = "notif1",
                         statusBarChipIcon = originalIconView,
                         promotedContent = PROMOTED_CONTENT,
-                    )
+                    ),
+                    creationTime = 1,
                 )
 
             val latest by collectLastValue(underTest.notificationChip)
@@ -127,7 +130,8 @@
                         key = "notif1",
                         statusBarChipIcon = originalIconView,
                         promotedContent = PROMOTED_CONTENT,
-                    )
+                    ),
+                    creationTime = 1,
                 )
 
             val latest by collectLastValue(underTest.notificationChip)
@@ -145,7 +149,8 @@
         }
 
     @Test
-    fun notificationChip_missingStatusBarIconChipView_inConstructor_emitsNull() =
+    @DisableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+    fun notificationChip_missingStatusBarIconChipView_cdFlagDisabled_inConstructor_emitsNull() =
         kosmos.runTest {
             val underTest =
                 factory.create(
@@ -153,7 +158,8 @@
                         key = "notif1",
                         statusBarChipIcon = null,
                         promotedContent = PROMOTED_CONTENT,
-                    )
+                    ),
+                    creationTime = 1,
                 )
 
             val latest by collectLastValue(underTest.notificationChip)
@@ -163,6 +169,25 @@
 
     @Test
     @EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+    fun notificationChip_missingStatusBarIconChipView_cdFlagEnabled_inConstructor_emitsNotNull() =
+        kosmos.runTest {
+            val underTest =
+                factory.create(
+                    activeNotificationModel(
+                        key = "notif1",
+                        statusBarChipIcon = null,
+                        promotedContent = PROMOTED_CONTENT,
+                    ),
+                    32L,
+                )
+
+            val latest by collectLastValue(underTest.notificationChip)
+
+            assertThat(latest).isNotNull()
+        }
+
+    @Test
+    @EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
     fun notificationChip_cdEnabled_missingStatusBarIconChipView_inConstructor_emitsNotNull() =
         kosmos.runTest {
             val underTest =
@@ -171,7 +196,8 @@
                         key = "notif1",
                         statusBarChipIcon = null,
                         promotedContent = PROMOTED_CONTENT,
-                    )
+                    ),
+                    creationTime = 1,
                 )
 
             val latest by collectLastValue(underTest.notificationChip)
@@ -181,7 +207,8 @@
         }
 
     @Test
-    fun notificationChip_missingStatusBarIconChipView_inSet_emitsNull() =
+    @DisableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+    fun notificationChip_cdFlagDisabled_missingStatusBarIconChipView_inSet_emitsNull() =
         kosmos.runTest {
             val startingNotif =
                 activeNotificationModel(
@@ -189,7 +216,7 @@
                     statusBarChipIcon = mock(),
                     promotedContent = PROMOTED_CONTENT,
                 )
-            val underTest = factory.create(startingNotif)
+            val underTest = factory.create(startingNotif, creationTime = 1)
             val latest by collectLastValue(underTest.notificationChip)
             assertThat(latest).isNotNull()
 
@@ -206,6 +233,31 @@
 
     @Test
     @EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+    fun notificationChip_cdFlagEnabled_missingStatusBarIconChipView_inSet_emitsNotNull() =
+        kosmos.runTest {
+            val startingNotif =
+                activeNotificationModel(
+                    key = "notif1",
+                    statusBarChipIcon = mock(),
+                    promotedContent = PROMOTED_CONTENT,
+                )
+            val underTest = factory.create(startingNotif, 123L)
+            val latest by collectLastValue(underTest.notificationChip)
+            assertThat(latest).isNotNull()
+
+            underTest.setNotification(
+                activeNotificationModel(
+                    key = "notif1",
+                    statusBarChipIcon = null,
+                    promotedContent = PROMOTED_CONTENT,
+                )
+            )
+
+            assertThat(latest).isNotNull()
+        }
+
+    @Test
+    @EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
     fun notificationChip_missingStatusBarIconChipView_inSet_cdEnabled_emitsNotNull() =
         kosmos.runTest {
             val startingNotif =
@@ -214,7 +266,7 @@
                     statusBarChipIcon = mock(),
                     promotedContent = PROMOTED_CONTENT,
                 )
-            val underTest = factory.create(startingNotif)
+            val underTest = factory.create(startingNotif, creationTime = 1)
             val latest by collectLastValue(underTest.notificationChip)
             assertThat(latest).isNotNull()
 
@@ -239,7 +291,8 @@
                         key = "notif1",
                         statusBarChipIcon = mock(),
                         promotedContent = null,
-                    )
+                    ),
+                    creationTime = 1,
                 )
 
             val latest by collectLastValue(underTest.notificationChip)
@@ -259,7 +312,8 @@
                         uid = UID,
                         statusBarChipIcon = mock(),
                         promotedContent = PROMOTED_CONTENT,
-                    )
+                    ),
+                    creationTime = 1,
                 )
 
             val latest by collectLastValue(underTest.notificationChip)
@@ -279,7 +333,8 @@
                         uid = UID,
                         statusBarChipIcon = mock(),
                         promotedContent = PROMOTED_CONTENT,
-                    )
+                    ),
+                    creationTime = 1,
                 )
 
             val latest by collectLastValue(underTest.notificationChip)
@@ -297,7 +352,8 @@
                         uid = UID,
                         statusBarChipIcon = mock(),
                         promotedContent = PROMOTED_CONTENT,
-                    )
+                    ),
+                    creationTime = 1,
                 )
 
             val latest by collectLastValue(underTest.notificationChip)
@@ -330,7 +386,8 @@
                         uid = hiddenUid,
                         statusBarChipIcon = mock(),
                         promotedContent = PROMOTED_CONTENT,
-                    )
+                    ),
+                    creationTime = 1,
                 )
             val latest by collectLastValue(underTest.notificationChip)
             assertThat(latest).isNotNull()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorTest.kt
index 5a894ca..ee4a52d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorTest.kt
@@ -23,16 +23,21 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.coroutines.collectValues
-import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.collectValues
+import com.android.systemui.kosmos.runTest
 import com.android.systemui.kosmos.useUnconfinedTestDispatcher
 import com.android.systemui.statusbar.StatusBarIconView
 import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
+import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
 import com.android.systemui.statusbar.notification.data.model.activeNotificationModel
 import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
 import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
 import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel
 import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
 import com.android.systemui.testKosmos
+import com.android.systemui.util.time.fakeSystemClock
 import com.google.common.truth.Truth.assertThat
 import kotlin.test.Test
 import kotlinx.coroutines.test.runTest
@@ -43,17 +48,14 @@
 @RunWith(AndroidJUnit4::class)
 class StatusBarNotificationChipsInteractorTest : SysuiTestCase() {
     private val kosmos = testKosmos().useUnconfinedTestDispatcher()
-    private val testScope = kosmos.testScope
-    private val activeNotificationListRepository = kosmos.activeNotificationListRepository
 
-    private val underTest by lazy {
-        kosmos.statusBarNotificationChipsInteractor.also { it.start() }
-    }
+    private val Kosmos.underTest by
+        Kosmos.Fixture { statusBarNotificationChipsInteractor.also { it.start() } }
 
     @Test
     @DisableFlags(StatusBarNotifChips.FLAG_NAME)
     fun notificationChips_flagOff_noNotifs() =
-        testScope.runTest {
+        kosmos.runTest {
             val latest by collectLastValue(underTest.notificationChips)
 
             setNotifs(
@@ -72,7 +74,7 @@
     @Test
     @EnableFlags(StatusBarNotifChips.FLAG_NAME)
     fun notificationChips_noNotifs_empty() =
-        testScope.runTest {
+        kosmos.runTest {
             val latest by collectLastValue(underTest.notificationChips)
 
             setNotifs(emptyList())
@@ -82,8 +84,9 @@
 
     @Test
     @EnableFlags(StatusBarNotifChips.FLAG_NAME)
-    fun notificationChips_notifMissingStatusBarChipIconView_empty() =
-        testScope.runTest {
+    @DisableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+    fun notificationChips_notifMissingStatusBarChipIconView_cdFlagOff_empty() =
+        kosmos.runTest {
             val latest by collectLastValue(underTest.notificationChips)
 
             setNotifs(
@@ -100,9 +103,28 @@
         }
 
     @Test
+    @EnableFlags(StatusBarNotifChips.FLAG_NAME, StatusBarConnectedDisplays.FLAG_NAME)
+    fun notificationChips_notifMissingStatusBarChipIconView_cdFlagOn_notEmpty() =
+        kosmos.runTest {
+            val latest by collectLastValue(underTest.notificationChips)
+
+            setNotifs(
+                listOf(
+                    activeNotificationModel(
+                        key = "notif",
+                        statusBarChipIcon = null,
+                        promotedContent = PromotedNotificationContentModel.Builder("notif").build(),
+                    )
+                )
+            )
+
+            assertThat(latest).isNotEmpty()
+        }
+
+    @Test
     @EnableFlags(StatusBarNotifChips.FLAG_NAME)
     fun notificationChips_onePromotedNotif_statusBarIconViewMatches() =
-        testScope.runTest {
+        kosmos.runTest {
             val latest by collectLastValue(underTest.notificationChips)
 
             val icon = mock<StatusBarIconView>()
@@ -124,7 +146,7 @@
     @Test
     @EnableFlags(StatusBarNotifChips.FLAG_NAME)
     fun notificationChips_onlyForPromotedNotifs() =
-        testScope.runTest {
+        kosmos.runTest {
             val latest by collectLastValue(underTest.notificationChips)
 
             val firstIcon = mock<StatusBarIconView>()
@@ -159,7 +181,7 @@
     @Test
     @EnableFlags(StatusBarNotifChips.FLAG_NAME)
     fun notificationChips_notifUpdatesGoThrough() =
-        testScope.runTest {
+        kosmos.runTest {
             val latest by collectLastValue(underTest.notificationChips)
 
             val firstIcon = mock<StatusBarIconView>()
@@ -209,7 +231,7 @@
     @Test
     @EnableFlags(StatusBarNotifChips.FLAG_NAME)
     fun notificationChips_promotedNotifDisappearsThenReappears() =
-        testScope.runTest {
+        kosmos.runTest {
             val latest by collectLastValue(underTest.notificationChips)
 
             setNotifs(
@@ -250,8 +272,95 @@
 
     @Test
     @EnableFlags(StatusBarNotifChips.FLAG_NAME)
+    fun notificationChips_sortedBasedOnFirstAppearanceTime() =
+        kosmos.runTest {
+            val latest by collectLastValue(underTest.notificationChips)
+
+            val firstIcon = mock<StatusBarIconView>()
+            val secondIcon = mock<StatusBarIconView>()
+
+            // First, add notif1 at t=1000
+            fakeSystemClock.setCurrentTimeMillis(1000)
+            val notif1 =
+                activeNotificationModel(
+                    key = "notif1",
+                    statusBarChipIcon = firstIcon,
+                    promotedContent = PromotedNotificationContentModel.Builder("notif1").build(),
+                )
+            setNotifs(listOf(notif1))
+            assertThat(latest).hasSize(1)
+            assertThat(latest!![0].key).isEqualTo("notif1")
+
+            // WHEN we add notif2 at t=2000
+            fakeSystemClock.advanceTime(1000)
+            val notif2 =
+                activeNotificationModel(
+                    key = "notif2",
+                    statusBarChipIcon = secondIcon,
+                    promotedContent = PromotedNotificationContentModel.Builder("notif2").build(),
+                )
+            setNotifs(listOf(notif1, notif2))
+
+            // THEN notif2 is ranked above notif1 because it appeared later
+            assertThat(latest).hasSize(2)
+            assertThat(latest!![0].key).isEqualTo("notif2")
+            assertThat(latest!![1].key).isEqualTo("notif1")
+
+            // WHEN notif1 and notif2 swap places
+            setNotifs(listOf(notif2, notif1))
+
+            // THEN notif2 is still ranked above notif1 to preserve chip ordering
+            assertThat(latest).hasSize(2)
+            assertThat(latest!![0].key).isEqualTo("notif2")
+            assertThat(latest!![1].key).isEqualTo("notif1")
+
+            // WHEN notif1 and notif2 swap places again
+            setNotifs(listOf(notif1, notif2))
+
+            // THEN notif2 is still ranked above notif1 to preserve chip ordering
+            assertThat(latest).hasSize(2)
+            assertThat(latest!![0].key).isEqualTo("notif2")
+            assertThat(latest!![1].key).isEqualTo("notif1")
+
+            // WHEN notif1 gets an update
+            val notif1NewPromotedContent =
+                PromotedNotificationContentModel.Builder("notif1").apply {
+                    this.shortCriticalText = "Arrived"
+                }
+            setNotifs(
+                listOf(
+                    activeNotificationModel(
+                        key = "notif1",
+                        statusBarChipIcon = firstIcon,
+                        promotedContent = notif1NewPromotedContent.build(),
+                    ),
+                    notif2,
+                )
+            )
+
+            // THEN notif2 is still ranked above notif1 to preserve chip ordering
+            assertThat(latest).hasSize(2)
+            assertThat(latest!![0].key).isEqualTo("notif2")
+            assertThat(latest!![1].key).isEqualTo("notif1")
+
+            // WHEN notif1 disappears and then reappears
+            fakeSystemClock.advanceTime(1000)
+            setNotifs(listOf(notif2))
+            assertThat(latest).hasSize(1)
+
+            fakeSystemClock.advanceTime(1000)
+            setNotifs(listOf(notif2, notif1))
+
+            // THEN notif1 is now ranked first
+            assertThat(latest).hasSize(2)
+            assertThat(latest!![0].key).isEqualTo("notif1")
+            assertThat(latest!![1].key).isEqualTo("notif2")
+        }
+
+    @Test
+    @EnableFlags(StatusBarNotifChips.FLAG_NAME)
     fun notificationChips_notifChangesKey() =
-        testScope.runTest {
+        kosmos.runTest {
             val latest by collectLastValue(underTest.notificationChips)
 
             val firstIcon = mock<StatusBarIconView>()
@@ -291,7 +400,7 @@
     @Test
     @EnableFlags(StatusBarNotifChips.FLAG_NAME)
     fun onPromotedNotificationChipTapped_emitsKeys() =
-        testScope.runTest {
+        kosmos.runTest {
             val latest by collectValues(underTest.promotedNotificationChipTapEvent)
 
             underTest.onPromotedNotificationChipTapped("fakeKey")
@@ -308,7 +417,7 @@
     @Test
     @EnableFlags(StatusBarNotifChips.FLAG_NAME)
     fun onPromotedNotificationChipTapped_sameKeyTwice_emitsTwice() =
-        testScope.runTest {
+        kosmos.runTest {
             val latest by collectValues(underTest.promotedNotificationChipTapEvent)
 
             underTest.onPromotedNotificationChipTapped("fakeKey")
@@ -319,7 +428,7 @@
             assertThat(latest[1]).isEqualTo("fakeKey")
         }
 
-    private fun setNotifs(notifs: List<ActiveNotificationModel>) {
+    private fun Kosmos.setNotifs(notifs: List<ActiveNotificationModel>) {
         activeNotificationListRepository.activeNotifications.value =
             ActiveNotificationsStore.Builder()
                 .apply { notifs.forEach { addIndividualNotif(it) } }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt
index 17076b4..e561e3e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt
@@ -23,7 +23,6 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.Flags.FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY
 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.fakeKeyguardTransitionRepository
 import com.android.systemui.kosmos.collectLastValue
@@ -31,6 +30,7 @@
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.kosmos.useUnconfinedTestDispatcher
 import com.android.systemui.statusbar.StatusBarIconView
+import com.android.systemui.statusbar.chips.call.ui.viewmodel.CallChipViewModelTest.Companion.createStatusBarIconViewOrNull
 import com.android.systemui.statusbar.chips.notification.domain.interactor.statusBarNotificationChipsInteractor
 import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
 import com.android.systemui.statusbar.chips.ui.model.ColorsModel
@@ -48,7 +48,6 @@
 import com.google.common.truth.Truth.assertThat
 import kotlin.test.Test
 import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.test.runTest
 import org.junit.Before
 import org.junit.runner.RunWith
 import org.mockito.kotlin.mock
@@ -84,8 +83,8 @@
         }
 
     @Test
-    @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
-    fun chips_notifMissingStatusBarChipIconView_empty() =
+    @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY, StatusBarConnectedDisplays.FLAG_NAME)
+    fun chips_notifMissingStatusBarChipIconView_cdFlagDisabled_empty() =
         kosmos.runTest {
             val latest by collectLastValue(underTest.chips)
 
@@ -104,11 +103,31 @@
 
     @Test
     @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
+    @EnableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+    fun chips_notifMissingStatusBarChipIconView_cdFlagEnabled_notEmpty() =
+        kosmos.runTest {
+            val latest by collectLastValue(underTest.chips)
+
+            setNotifs(
+                listOf(
+                    activeNotificationModel(
+                        key = "notif",
+                        statusBarChipIcon = null,
+                        promotedContent = PromotedNotificationContentModel.Builder("notif").build(),
+                    )
+                )
+            )
+
+            assertThat(latest).isNotEmpty()
+        }
+
+    @Test
+    @DisableFlags(FLAG_PROMOTE_NOTIFICATIONS_AUTOMATICALLY)
     fun chips_onePromotedNotif_statusBarIconViewMatches() =
         kosmos.runTest {
             val latest by collectLastValue(underTest.chips)
 
-            val icon = mock<StatusBarIconView>()
+            val icon = createStatusBarIconViewOrNull()
             setNotifs(
                 listOf(
                     activeNotificationModel(
@@ -121,8 +140,7 @@
 
             assertThat(latest).hasSize(1)
             val chip = latest!![0]
-            assertThat(chip).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
-            assertThat(chip.icon).isEqualTo(OngoingActivityChipModel.ChipIcon.StatusBarView(icon))
+            assertIsNotifChip(chip, icon, "notif")
         }
 
     @Test
@@ -168,7 +186,7 @@
                 listOf(
                     activeNotificationModel(
                         key = "notif",
-                        statusBarChipIcon = mock<StatusBarIconView>(),
+                        statusBarChipIcon = createStatusBarIconViewOrNull(),
                         promotedContent = promotedContentBuilder.build(),
                     )
                 )
@@ -187,8 +205,8 @@
         kosmos.runTest {
             val latest by collectLastValue(underTest.chips)
 
-            val firstIcon = mock<StatusBarIconView>()
-            val secondIcon = mock<StatusBarIconView>()
+            val firstIcon = createStatusBarIconViewOrNull()
+            val secondIcon = createStatusBarIconViewOrNull()
             setNotifs(
                 listOf(
                     activeNotificationModel(
@@ -203,15 +221,15 @@
                     ),
                     activeNotificationModel(
                         key = "notif3",
-                        statusBarChipIcon = mock<StatusBarIconView>(),
+                        statusBarChipIcon = createStatusBarIconViewOrNull(),
                         promotedContent = null,
                     ),
                 )
             )
 
             assertThat(latest).hasSize(2)
-            assertIsNotifChip(latest!![0], firstIcon)
-            assertIsNotifChip(latest!![1], secondIcon)
+            assertIsNotifChip(latest!![0], firstIcon, "notif1")
+            assertIsNotifChip(latest!![1], secondIcon, "notif2")
         }
 
     @Test
@@ -269,7 +287,7 @@
                 listOf(
                     activeNotificationModel(
                         key = "notif",
-                        statusBarChipIcon = mock<StatusBarIconView>(),
+                        statusBarChipIcon = createStatusBarIconViewOrNull(),
                         promotedContent = promotedContentBuilder.build(),
                     )
                 )
@@ -293,7 +311,7 @@
                 listOf(
                     activeNotificationModel(
                         key = "notif",
-                        statusBarChipIcon = mock<StatusBarIconView>(),
+                        statusBarChipIcon = createStatusBarIconViewOrNull(),
                         promotedContent = promotedContentBuilder.build(),
                     )
                 )
@@ -323,7 +341,7 @@
                 listOf(
                     activeNotificationModel(
                         key = "notif",
-                        statusBarChipIcon = mock<StatusBarIconView>(),
+                        statusBarChipIcon = createStatusBarIconViewOrNull(),
                         promotedContent = promotedContentBuilder.build(),
                     )
                 )
@@ -353,7 +371,7 @@
                 listOf(
                     activeNotificationModel(
                         key = "notif",
-                        statusBarChipIcon = mock<StatusBarIconView>(),
+                        statusBarChipIcon = createStatusBarIconViewOrNull(),
                         promotedContent = promotedContentBuilder.build(),
                     )
                 )
@@ -382,7 +400,7 @@
                 listOf(
                     activeNotificationModel(
                         key = "notif",
-                        statusBarChipIcon = mock<StatusBarIconView>(),
+                        statusBarChipIcon = createStatusBarIconViewOrNull(),
                         promotedContent = promotedContentBuilder.build(),
                     )
                 )
@@ -411,7 +429,7 @@
                 listOf(
                     activeNotificationModel(
                         key = "notif",
-                        statusBarChipIcon = mock<StatusBarIconView>(),
+                        statusBarChipIcon = createStatusBarIconViewOrNull(),
                         promotedContent = promotedContentBuilder.build(),
                     )
                 )
@@ -439,7 +457,7 @@
                 listOf(
                     activeNotificationModel(
                         key = "notif",
-                        statusBarChipIcon = mock<StatusBarIconView>(),
+                        statusBarChipIcon = createStatusBarIconViewOrNull(),
                         promotedContent = promotedContentBuilder.build(),
                     )
                 )
@@ -467,7 +485,7 @@
                 listOf(
                     activeNotificationModel(
                         key = "notif",
-                        statusBarChipIcon = mock<StatusBarIconView>(),
+                        statusBarChipIcon = createStatusBarIconViewOrNull(),
                         promotedContent = promotedContentBuilder.build(),
                     )
                 )
@@ -499,7 +517,7 @@
                 listOf(
                     activeNotificationModel(
                         key = "notif",
-                        statusBarChipIcon = mock<StatusBarIconView>(),
+                        statusBarChipIcon = createStatusBarIconViewOrNull(),
                         promotedContent = promotedContentBuilder.build(),
                     )
                 )
@@ -531,7 +549,7 @@
                 listOf(
                     activeNotificationModel(
                         key = "clickTest",
-                        statusBarChipIcon = mock<StatusBarIconView>(),
+                        statusBarChipIcon = createStatusBarIconViewOrNull(),
                         promotedContent =
                             PromotedNotificationContentModel.Builder("clickTest").build(),
                     )
@@ -552,9 +570,21 @@
     }
 
     companion object {
-        fun assertIsNotifChip(latest: OngoingActivityChipModel?, expectedIcon: StatusBarIconView) {
-            assertThat((latest as OngoingActivityChipModel.Shown).icon)
-                .isEqualTo(OngoingActivityChipModel.ChipIcon.StatusBarView(expectedIcon))
+        fun assertIsNotifChip(
+            latest: OngoingActivityChipModel?,
+            expectedIcon: StatusBarIconView?,
+            notificationKey: String,
+        ) {
+            val shown = latest as OngoingActivityChipModel.Shown
+            if (StatusBarConnectedDisplays.isEnabled) {
+                assertThat(shown.icon)
+                    .isEqualTo(
+                        OngoingActivityChipModel.ChipIcon.StatusBarNotificationIcon(notificationKey)
+                    )
+            } else {
+                assertThat(latest.icon)
+                    .isEqualTo(OngoingActivityChipModel.ChipIcon.StatusBarView(expectedIcon!!))
+            }
         }
 
         fun assertIsNotifKey(latest: OngoingActivityChipModel?, expectedKey: String) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/compose/ChronometerStateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/compose/ChronometerStateTest.kt
new file mode 100644
index 0000000..e68045f
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/compose/ChronometerStateTest.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.systemui.statusbar.chips.ui.compose
+
+import android.text.format.DateUtils.formatElapsedTime
+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.cancelAndJoin
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.advanceTimeBy
+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 ChronometerStateTest : SysuiTestCase() {
+
+    private lateinit var mockTimeSource: MutableTimeSource
+
+    @Before
+    fun setup() {
+        mockTimeSource = MutableTimeSource()
+    }
+
+    @Test
+    fun initialText_isCorrect() = runTest {
+        val state = ChronometerState(mockTimeSource, 0L)
+        assertThat(state.currentTimeText).isEqualTo(formatElapsedTime(0))
+    }
+
+    @Test
+    fun textUpdates_withTime() = runTest {
+        val startTime = 1000L
+        val state = ChronometerState(mockTimeSource, startTime)
+        val job = launch { state.run() }
+
+        val elapsedTime = 5000L
+        mockTimeSource.time = startTime + elapsedTime
+        advanceTimeBy(elapsedTime)
+        assertThat(state.currentTimeText).isEqualTo(formatElapsedTime(elapsedTime / 1000))
+
+        job.cancelAndJoin()
+    }
+
+    @Test
+    fun textUpdates_toLargerValue() = runTest {
+        val startTime = 1000L
+        val state = ChronometerState(mockTimeSource, startTime)
+        val job = launch { state.run() }
+
+        val elapsedTime = 15000L
+        mockTimeSource.time = startTime + elapsedTime
+        advanceTimeBy(elapsedTime)
+        assertThat(state.currentTimeText).isEqualTo(formatElapsedTime(elapsedTime / 1000))
+
+        job.cancelAndJoin()
+    }
+
+    @Test
+    fun textUpdates_afterResettingBase() = runTest {
+        val initialElapsedTime = 30000L
+        val startTime = 50000L
+        val state = ChronometerState(mockTimeSource, startTime)
+        val job = launch { state.run() }
+
+        mockTimeSource.time = startTime + initialElapsedTime
+        advanceTimeBy(initialElapsedTime)
+        assertThat(state.currentTimeText).isEqualTo(formatElapsedTime(initialElapsedTime / 1000))
+
+        job.cancelAndJoin()
+
+        val newElapsedTime = 5000L
+        val newStartTime = 100000L
+        val newState = ChronometerState(mockTimeSource, newStartTime)
+        val newJob = launch { newState.run() }
+
+        mockTimeSource.time = newStartTime + newElapsedTime
+        advanceTimeBy(newElapsedTime)
+        assertThat(newState.currentTimeText).isEqualTo(formatElapsedTime(newElapsedTime / 1000))
+
+        newJob.cancelAndJoin()
+    }
+}
+
+/** A fake implementation of [TimeSource] that allows the caller to set the current time */
+class MutableTimeSource(var time: Long = 0L) : TimeSource {
+    override fun getCurrentTime(): Long {
+        return time
+    }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
index c5c2a94..42358cc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt
@@ -38,10 +38,10 @@
 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.notification.demo.ui.viewmodel.demoNotifChipViewModel
 import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
 import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
 import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer
+import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
 import com.android.systemui.statusbar.phone.SystemUIDialog
 import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory
 import com.android.systemui.statusbar.phone.ongoingcall.data.repository.ongoingCallRepository
@@ -97,7 +97,6 @@
     @Before
     fun setUp() {
         setUpPackageManagerForMediaProjection(kosmos)
-        kosmos.demoNotifChipViewModel.start()
         val icon =
             BitmapDrawable(
                 context.resources,
@@ -171,29 +170,35 @@
     @Test
     fun primaryChip_screenRecordAndShareToAppAndCastToOtherHideAndCallShown_callShown() =
         testScope.runTest {
+            val notificationKey = "call"
             screenRecordState.value = ScreenRecordModel.DoingNothing
             // MediaProjection covers both share-to-app and cast-to-other-device
             mediaProjectionState.value = MediaProjectionState.NotProjecting
 
-            callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
+            callRepo.setOngoingCallState(
+                inCallModel(startTimeMs = 34, notificationKey = notificationKey)
+            )
 
             val latest by collectLastValue(underTest.primaryChip)
 
-            assertIsCallChip(latest)
+            assertIsCallChip(latest, notificationKey)
         }
 
     @Test
     fun primaryChip_higherPriorityChipAdded_lowerPriorityChipReplaced() =
         testScope.runTest {
             // Start with just the lowest priority chip shown
-            callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
+            val callNotificationKey = "call"
+            callRepo.setOngoingCallState(
+                inCallModel(startTimeMs = 34, notificationKey = callNotificationKey)
+            )
             // And everything else hidden
             mediaProjectionState.value = MediaProjectionState.NotProjecting
             screenRecordState.value = ScreenRecordModel.DoingNothing
 
             val latest by collectLastValue(underTest.primaryChip)
 
-            assertIsCallChip(latest)
+            assertIsCallChip(latest, callNotificationKey)
 
             // WHEN the higher priority media projection chip is added
             mediaProjectionState.value =
@@ -220,7 +225,10 @@
             screenRecordState.value = ScreenRecordModel.Recording
             mediaProjectionState.value =
                 MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
-            callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
+            val callNotificationKey = "call"
+            callRepo.setOngoingCallState(
+                inCallModel(startTimeMs = 34, notificationKey = callNotificationKey)
+            )
 
             val latest by collectLastValue(underTest.primaryChip)
 
@@ -237,7 +245,7 @@
             mediaProjectionState.value = MediaProjectionState.NotProjecting
 
             // THEN the lower priority call is used
-            assertIsCallChip(latest)
+            assertIsCallChip(latest, callNotificationKey)
         }
 
     /** Regression test for b/347726238. */
@@ -366,13 +374,27 @@
             assertThat(icon.res).isEqualTo(R.drawable.ic_present_to_all)
         }
 
-        fun assertIsCallChip(latest: OngoingActivityChipModel?) {
-            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
+        fun assertIsCallChip(latest: OngoingActivityChipModel?, notificationKey: String) {
+            assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown.Timer::class.java)
+            if (StatusBarConnectedDisplays.isEnabled) {
+                assertNotificationIcon(latest, notificationKey)
+                return
+            }
             val icon =
                 (((latest as OngoingActivityChipModel.Shown).icon)
                         as OngoingActivityChipModel.ChipIcon.SingleColorIcon)
                     .impl as Icon.Resource
             assertThat(icon.res).isEqualTo(com.android.internal.R.drawable.ic_phone)
         }
+
+        private fun assertNotificationIcon(
+            latest: OngoingActivityChipModel?,
+            notificationKey: String,
+        ) {
+            val shown = latest as OngoingActivityChipModel.Shown
+            val notificationIcon =
+                shown.icon as OngoingActivityChipModel.ChipIcon.StatusBarNotificationIcon
+            assertThat(notificationIcon.notificationKey).isEqualTo(notificationKey)
+        }
     }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt
index b2e7feb..0f42f29 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt
@@ -34,11 +34,9 @@
 import com.android.systemui.res.R
 import com.android.systemui.screenrecord.data.model.ScreenRecordModel
 import com.android.systemui.screenrecord.data.repository.screenRecordRepository
-import com.android.systemui.statusbar.StatusBarIconView
+import com.android.systemui.statusbar.chips.call.ui.viewmodel.CallChipViewModelTest.Companion.createStatusBarIconViewOrNull
 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.notification.demo.ui.viewmodel.DemoNotifChipViewModelTest.Companion.addDemoNotifChip
-import com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel.demoNotifChipViewModel
 import com.android.systemui.statusbar.chips.notification.domain.interactor.statusBarNotificationChipsInteractor
 import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
 import com.android.systemui.statusbar.chips.notification.ui.viewmodel.NotifChipsViewModelTest.Companion.assertIsNotifChip
@@ -112,7 +110,6 @@
     @Before
     fun setUp() {
         setUpPackageManagerForMediaProjection(kosmos)
-        kosmos.demoNotifChipViewModel.start()
         kosmos.statusBarNotificationChipsInteractor.start()
         val icon =
             BitmapDrawable(
@@ -189,13 +186,16 @@
     @Test
     fun chips_screenRecordShowAndCallShow_primaryIsScreenRecordSecondaryIsCall() =
         testScope.runTest {
+            val callNotificationKey = "call"
             screenRecordState.value = ScreenRecordModel.Recording
-            callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
+            callRepo.setOngoingCallState(
+                inCallModel(startTimeMs = 34, notificationKey = callNotificationKey)
+            )
 
             val latest by collectLastValue(underTest.chips)
 
             assertIsScreenRecordChip(latest!!.primary)
-            assertIsCallChip(latest!!.secondary)
+            assertIsCallChip(latest!!.secondary, callNotificationKey)
         }
 
     @Test
@@ -243,29 +243,18 @@
     @Test
     fun chips_shareToAppShowAndCallShow_primaryIsShareToAppSecondaryIsCall() =
         testScope.runTest {
+            val callNotificationKey = "call"
             screenRecordState.value = ScreenRecordModel.DoingNothing
             mediaProjectionState.value =
                 MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
-            callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
+            callRepo.setOngoingCallState(
+                inCallModel(startTimeMs = 34, notificationKey = callNotificationKey)
+            )
 
             val latest by collectLastValue(underTest.chips)
 
             assertIsShareToAppChip(latest!!.primary)
-            assertIsCallChip(latest!!.secondary)
-        }
-
-    @Test
-    fun chips_threeActiveChips_topTwoShown() =
-        testScope.runTest {
-            screenRecordState.value = ScreenRecordModel.Recording
-            callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
-            addDemoNotifChip(commandRegistry, pw)
-
-            val latest by collectLastValue(underTest.chips)
-
-            assertIsScreenRecordChip(latest!!.primary)
-            assertIsCallChip(latest!!.secondary)
-            // Demo notif chip is dropped
+            assertIsCallChip(latest!!.secondary, callNotificationKey)
         }
 
     @Test
@@ -275,25 +264,31 @@
             // MediaProjection covers both share-to-app and cast-to-other-device
             mediaProjectionState.value = MediaProjectionState.NotProjecting
 
-            callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
+            val callNotificationKey = "call"
+            callRepo.setOngoingCallState(
+                inCallModel(startTimeMs = 34, notificationKey = callNotificationKey)
+            )
 
             val latest by collectLastValue(underTest.primaryChip)
 
-            assertIsCallChip(latest)
+            assertIsCallChip(latest, callNotificationKey)
         }
 
     @Test
     fun chips_onlyCallShown_primaryIsCallSecondaryIsHidden() =
         testScope.runTest {
+            val callNotificationKey = "call"
             screenRecordState.value = ScreenRecordModel.DoingNothing
             // MediaProjection covers both share-to-app and cast-to-other-device
             mediaProjectionState.value = MediaProjectionState.NotProjecting
 
-            callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
+            callRepo.setOngoingCallState(
+                inCallModel(startTimeMs = 34, notificationKey = callNotificationKey)
+            )
 
             val latest by collectLastValue(underTest.chips)
 
-            assertIsCallChip(latest!!.primary)
+            assertIsCallChip(latest!!.primary, callNotificationKey)
             assertThat(latest!!.secondary).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
         }
 
@@ -302,7 +297,7 @@
         testScope.runTest {
             val latest by collectLastValue(underTest.chips)
 
-            val icon = mock<StatusBarIconView>()
+            val icon = createStatusBarIconViewOrNull()
             setNotifs(
                 listOf(
                     activeNotificationModel(
@@ -313,7 +308,7 @@
                 )
             )
 
-            assertIsNotifChip(latest!!.primary, icon)
+            assertIsNotifChip(latest!!.primary, icon, "notif")
             assertThat(latest!!.secondary).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
         }
 
@@ -322,8 +317,8 @@
         testScope.runTest {
             val latest by collectLastValue(underTest.chips)
 
-            val firstIcon = mock<StatusBarIconView>()
-            val secondIcon = mock<StatusBarIconView>()
+            val firstIcon = createStatusBarIconViewOrNull()
+            val secondIcon = createStatusBarIconViewOrNull()
             setNotifs(
                 listOf(
                     activeNotificationModel(
@@ -341,8 +336,8 @@
                 )
             )
 
-            assertIsNotifChip(latest!!.primary, firstIcon)
-            assertIsNotifChip(latest!!.secondary, secondIcon)
+            assertIsNotifChip(latest!!.primary, firstIcon, "firstNotif")
+            assertIsNotifChip(latest!!.secondary, secondIcon, "secondNotif")
         }
 
     @Test
@@ -350,9 +345,9 @@
         testScope.runTest {
             val latest by collectLastValue(underTest.chips)
 
-            val firstIcon = mock<StatusBarIconView>()
-            val secondIcon = mock<StatusBarIconView>()
-            val thirdIcon = mock<StatusBarIconView>()
+            val firstIcon = createStatusBarIconViewOrNull()
+            val secondIcon = createStatusBarIconViewOrNull()
+            val thirdIcon = createStatusBarIconViewOrNull()
             setNotifs(
                 listOf(
                     activeNotificationModel(
@@ -376,8 +371,8 @@
                 )
             )
 
-            assertIsNotifChip(latest!!.primary, firstIcon)
-            assertIsNotifChip(latest!!.secondary, secondIcon)
+            assertIsNotifChip(latest!!.primary, firstIcon, "firstNotif")
+            assertIsNotifChip(latest!!.secondary, secondIcon, "secondNotif")
         }
 
     @Test
@@ -385,8 +380,12 @@
         testScope.runTest {
             val latest by collectLastValue(underTest.chips)
 
-            callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
-            val firstIcon = mock<StatusBarIconView>()
+            val callNotificationKey = "call"
+            callRepo.setOngoingCallState(
+                inCallModel(startTimeMs = 34, notificationKey = callNotificationKey)
+            )
+
+            val firstIcon = createStatusBarIconViewOrNull()
             setNotifs(
                 listOf(
                     activeNotificationModel(
@@ -397,43 +396,56 @@
                     ),
                     activeNotificationModel(
                         key = "secondNotif",
-                        statusBarChipIcon = mock<StatusBarIconView>(),
+                        statusBarChipIcon = createStatusBarIconViewOrNull(),
                         promotedContent =
                             PromotedNotificationContentModel.Builder("secondNotif").build(),
                     ),
                 )
             )
 
-            assertIsCallChip(latest!!.primary)
-            assertIsNotifChip(latest!!.secondary, firstIcon)
+            assertIsCallChip(latest!!.primary, callNotificationKey)
+            assertIsNotifChip(latest!!.secondary, firstIcon, "firstNotif")
         }
 
     @Test
     fun chips_screenRecordAndCallAndPromotedNotifs_notifsNotShown() =
         testScope.runTest {
+            val callNotificationKey = "call"
             val latest by collectLastValue(underTest.chips)
 
-            callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
+            callRepo.setOngoingCallState(
+                inCallModel(startTimeMs = 34, notificationKey = callNotificationKey)
+            )
             screenRecordState.value = ScreenRecordModel.Recording
             setNotifs(
                 listOf(
                     activeNotificationModel(
                         key = "notif",
-                        statusBarChipIcon = mock<StatusBarIconView>(),
+                        statusBarChipIcon = createStatusBarIconViewOrNull(),
                         promotedContent = PromotedNotificationContentModel.Builder("notif").build(),
                     )
                 )
             )
 
             assertIsScreenRecordChip(latest!!.primary)
-            assertIsCallChip(latest!!.secondary)
+            assertIsCallChip(latest!!.secondary, callNotificationKey)
         }
 
     @Test
     fun primaryChip_higherPriorityChipAdded_lowerPriorityChipReplaced() =
         testScope.runTest {
+            val callNotificationKey = "call"
             // Start with just the lowest priority chip shown
-            addDemoNotifChip(commandRegistry, pw)
+            val notifIcon = createStatusBarIconViewOrNull()
+            setNotifs(
+                listOf(
+                    activeNotificationModel(
+                        key = "notif",
+                        statusBarChipIcon = notifIcon,
+                        promotedContent = PromotedNotificationContentModel.Builder("notif").build(),
+                    )
+                )
+            )
             // And everything else hidden
             callRepo.setOngoingCallState(OngoingCallModel.NoCall)
             mediaProjectionState.value = MediaProjectionState.NotProjecting
@@ -441,13 +453,15 @@
 
             val latest by collectLastValue(underTest.primaryChip)
 
-            assertIsDemoNotifChip(latest)
+            assertIsNotifChip(latest, notifIcon, "notif")
 
             // WHEN the higher priority call chip is added
-            callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
+            callRepo.setOngoingCallState(
+                inCallModel(startTimeMs = 34, notificationKey = callNotificationKey)
+            )
 
             // THEN the higher priority call chip is used
-            assertIsCallChip(latest)
+            assertIsCallChip(latest, callNotificationKey)
 
             // WHEN the higher priority media projection chip is added
             mediaProjectionState.value =
@@ -470,12 +484,24 @@
     @Test
     fun primaryChip_highestPriorityChipRemoved_showsNextPriorityChip() =
         testScope.runTest {
+            val callNotificationKey = "call"
             // WHEN all chips are active
             screenRecordState.value = ScreenRecordModel.Recording
             mediaProjectionState.value =
                 MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE)
-            callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
-            addDemoNotifChip(commandRegistry, pw)
+            callRepo.setOngoingCallState(
+                inCallModel(startTimeMs = 34, notificationKey = callNotificationKey)
+            )
+            val notifIcon = createStatusBarIconViewOrNull()
+            setNotifs(
+                listOf(
+                    activeNotificationModel(
+                        key = "notif",
+                        statusBarChipIcon = notifIcon,
+                        promotedContent = PromotedNotificationContentModel.Builder("notif").build(),
+                    )
+                )
+            )
 
             val latest by collectLastValue(underTest.primaryChip)
 
@@ -492,20 +518,30 @@
             mediaProjectionState.value = MediaProjectionState.NotProjecting
 
             // THEN the lower priority call is used
-            assertIsCallChip(latest)
+            assertIsCallChip(latest, callNotificationKey)
 
             // WHEN the higher priority call is removed
             callRepo.setOngoingCallState(OngoingCallModel.NoCall)
 
-            // THEN the lower priority demo notif is used
-            assertIsDemoNotifChip(latest)
+            // THEN the lower priority notif is used
+            assertIsNotifChip(latest, notifIcon, "notif")
         }
 
     @Test
     fun chips_movesChipsAroundAccordingToPriority() =
         testScope.runTest {
+            val callNotificationKey = "call"
             // Start with just the lowest priority chip shown
-            addDemoNotifChip(commandRegistry, pw)
+            val notifIcon = createStatusBarIconViewOrNull()
+            setNotifs(
+                listOf(
+                    activeNotificationModel(
+                        key = "notif",
+                        statusBarChipIcon = notifIcon,
+                        promotedContent = PromotedNotificationContentModel.Builder("notif").build(),
+                    )
+                )
+            )
             // And everything else hidden
             callRepo.setOngoingCallState(OngoingCallModel.NoCall)
             mediaProjectionState.value = MediaProjectionState.NotProjecting
@@ -513,16 +549,18 @@
 
             val latest by collectLastValue(underTest.chips)
 
-            assertIsDemoNotifChip(latest!!.primary)
+            assertIsNotifChip(latest!!.primary, notifIcon, "notif")
             assertThat(latest!!.secondary).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
 
             // WHEN the higher priority call chip is added
-            callRepo.setOngoingCallState(inCallModel(startTimeMs = 34))
+            callRepo.setOngoingCallState(
+                inCallModel(startTimeMs = 34, notificationKey = callNotificationKey)
+            )
 
-            // THEN the higher priority call chip is used as primary and demo notif is demoted to
+            // THEN the higher priority call chip is used as primary and notif is demoted to
             // secondary
-            assertIsCallChip(latest!!.primary)
-            assertIsDemoNotifChip(latest!!.secondary)
+            assertIsCallChip(latest!!.primary, callNotificationKey)
+            assertIsNotifChip(latest!!.secondary, notifIcon, "notif")
 
             // WHEN the higher priority media projection chip is added
             mediaProjectionState.value =
@@ -533,9 +571,9 @@
                 )
 
             // THEN the higher priority media projection chip is used as primary and call is demoted
-            // to secondary (and demo notif is dropped altogether)
+            // to secondary (and notif is dropped altogether)
             assertIsShareToAppChip(latest!!.primary)
-            assertIsCallChip(latest!!.secondary)
+            assertIsCallChip(latest!!.secondary, callNotificationKey)
 
             // WHEN the higher priority screen record chip is added
             screenRecordState.value = ScreenRecordModel.Recording
@@ -547,15 +585,15 @@
             screenRecordState.value = ScreenRecordModel.DoingNothing
             callRepo.setOngoingCallState(OngoingCallModel.NoCall)
 
-            // THEN media projection and demo notif remain
+            // THEN media projection and notif remain
             assertIsShareToAppChip(latest!!.primary)
-            assertIsDemoNotifChip(latest!!.secondary)
+            assertIsNotifChip(latest!!.secondary, notifIcon, "notif")
 
             // WHEN media projection is dropped
             mediaProjectionState.value = MediaProjectionState.NotProjecting
 
-            // THEN demo notif is promoted to primary
-            assertIsDemoNotifChip(latest!!.primary)
+            // THEN notif is promoted to primary
+            assertIsNotifChip(latest!!.primary, notifIcon, "notif")
             assertThat(latest!!.secondary).isInstanceOf(OngoingActivityChipModel.Hidden::class.java)
         }
 
@@ -669,12 +707,6 @@
             assertThat(latest).isEqualTo(OngoingActivityChipModel.Hidden(shouldAnimate = false))
         }
 
-    private fun assertIsDemoNotifChip(latest: OngoingActivityChipModel?) {
-        assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java)
-        assertThat((latest as OngoingActivityChipModel.Shown).icon)
-            .isInstanceOf(OngoingActivityChipModel.ChipIcon.FullColorAppIcon::class.java)
-    }
-
     private fun setNotifs(notifs: List<ActiveNotificationModel>) {
         activeNotificationListRepository.activeNotifications.value =
             ActiveNotificationsStore.Builder()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
index f502cab..809df41 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/connectivity/NetworkControllerBaseTest.java
@@ -676,14 +676,6 @@
         }
     }
 
-    protected void verifyLastCallStrength(int icon) {
-        ArgumentCaptor<IconState> iconArg = ArgumentCaptor.forClass(IconState.class);
-        verify(mCallbackHandler, Mockito.atLeastOnce()).setCallIndicator(
-                iconArg.capture(),
-                anyInt());
-        assertEquals("Call strength, in status bar", icon, (int) iconArg.getValue().icon);
-    }
-
     protected void assertNetworkNameEquals(String expected) {
         assertEquals("Network name", expected, mMobileSignalController.getState().networkName);
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt
index 009b33b..3515c56 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarInitializerTest.kt
@@ -26,10 +26,12 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.fragments.FragmentHostManager
 import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.plugins.fakeDarkIconDispatcher
 import com.android.systemui.statusbar.data.repository.fakeStatusBarModePerDisplayRepository
 import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment
 import com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarComponent
 import com.android.systemui.statusbar.pipeline.shared.ui.composable.StatusBarRootFactory
+import com.android.systemui.statusbar.policy.statusBarConfigurationController
 import com.android.systemui.statusbar.window.StatusBarWindowController
 import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
 import com.android.systemui.testKosmos
@@ -77,6 +79,8 @@
             componentFactory = mock(HomeStatusBarComponent.Factory::class.java),
             creationListeners = setOf(),
             statusBarModePerDisplayRepository = statusBarModePerDisplayRepository,
+            darkIconDispatcher = kosmos.fakeDarkIconDispatcher,
+            statusBarConfigurationController = kosmos.statusBarConfigurationController,
         )
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarOrchestratorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarOrchestratorTest.kt
index 659e53f..04c5bd9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarOrchestratorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/core/StatusBarOrchestratorTest.kt
@@ -38,9 +38,9 @@
 import com.android.systemui.statusbar.data.model.StatusBarMode.OPAQUE
 import com.android.systemui.statusbar.data.model.StatusBarMode.TRANSPARENT
 import com.android.systemui.statusbar.data.repository.fakeStatusBarModePerDisplayRepository
-import com.android.systemui.statusbar.window.data.model.StatusBarWindowState
 import com.android.systemui.statusbar.window.data.repository.fakeStatusBarWindowStatePerDisplayRepository
 import com.android.systemui.statusbar.window.fakeStatusBarWindowController
+import com.android.systemui.statusbar.window.shared.model.StatusBarWindowState
 import com.android.systemui.testKosmos
 import com.android.wm.shell.bubbles.bubbles
 import com.google.common.truth.Truth.assertThat
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/LightBarControllerStoreImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/LightBarControllerStoreImplTest.kt
index 18eef33..884c35c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/LightBarControllerStoreImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/LightBarControllerStoreImplTest.kt
@@ -51,7 +51,7 @@
     @Test
     fun forDisplay_startsInstance() =
         testScope.runTest {
-            val instance = underTest.forDisplay(DEFAULT_DISPLAY)
+            val instance = underTest.forDisplay(DEFAULT_DISPLAY)!!
 
             verify(instance).start()
         }
@@ -59,7 +59,7 @@
     @Test
     fun beforeDisplayRemoved_doesNotStopInstances() =
         testScope.runTest {
-            val instance = underTest.forDisplay(DEFAULT_DISPLAY)
+            val instance = underTest.forDisplay(DEFAULT_DISPLAY)!!
 
             verify(instance, never()).stop()
         }
@@ -67,7 +67,7 @@
     @Test
     fun displayRemoved_stopsInstance() =
         testScope.runTest {
-            val instance = underTest.forDisplay(DEFAULT_DISPLAY)
+            val instance = underTest.forDisplay(DEFAULT_DISPLAY)!!
 
             fakeDisplayRepository.removeDisplay(DEFAULT_DISPLAY)
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/MultiDisplayDarkIconDispatcherStoreTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/MultiDisplayDarkIconDispatcherStoreTest.kt
index a2c3c66..f37648a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/MultiDisplayDarkIconDispatcherStoreTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/MultiDisplayDarkIconDispatcherStoreTest.kt
@@ -56,7 +56,7 @@
     @Test
     fun beforeDisplayRemoved_doesNotStopInstances() =
         testScope.runTest {
-            val instance = underTest.forDisplay(DEFAULT_DISPLAY)
+            val instance = underTest.forDisplay(DEFAULT_DISPLAY)!!
 
             verify(instance, never()).stop()
         }
@@ -64,7 +64,7 @@
     @Test
     fun displayRemoved_stopsInstance() =
         testScope.runTest {
-            val instance = underTest.forDisplay(DEFAULT_DISPLAY)
+            val instance = underTest.forDisplay(DEFAULT_DISPLAY)!!
 
             fakeDisplayRepository.removeDisplay(DEFAULT_DISPLAY)
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/MultiDisplayStatusBarContentInsetsProviderStoreTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/MultiDisplayStatusBarContentInsetsProviderStoreTest.kt
index 4a26fdf..e0a1f27 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/MultiDisplayStatusBarContentInsetsProviderStoreTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/MultiDisplayStatusBarContentInsetsProviderStoreTest.kt
@@ -51,7 +51,7 @@
     @Test
     fun forDisplay_startsInstances() =
         testScope.runTest {
-            val instance = underTest.forDisplay(DEFAULT_DISPLAY)
+            val instance = underTest.forDisplay(DEFAULT_DISPLAY)!!
 
             verify(instance).start()
         }
@@ -59,7 +59,7 @@
     @Test
     fun beforeDisplayRemoved_doesNotStopInstances() =
         testScope.runTest {
-            val instance = underTest.forDisplay(DEFAULT_DISPLAY)
+            val instance = underTest.forDisplay(DEFAULT_DISPLAY)!!
 
             verify(instance, never()).stop()
         }
@@ -67,7 +67,7 @@
     @Test
     fun displayRemoved_stopsInstance() =
         testScope.runTest {
-            val instance = underTest.forDisplay(DEFAULT_DISPLAY)
+            val instance = underTest.forDisplay(DEFAULT_DISPLAY)!!
 
             fakeDisplayRepository.removeDisplay(DEFAULT_DISPLAY)
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/MultiDisplayStatusBarModeRepositoryStoreTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/MultiDisplayStatusBarModeRepositoryStoreTest.kt
index a9920ec5..11fd902 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/MultiDisplayStatusBarModeRepositoryStoreTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/MultiDisplayStatusBarModeRepositoryStoreTest.kt
@@ -53,7 +53,7 @@
     @Test
     fun forDisplay_startsInstance() =
         testScope.runTest {
-            val instance = underTest.forDisplay(DEFAULT_DISPLAY)
+            val instance = underTest.forDisplay(DEFAULT_DISPLAY)!!
 
             verify(instance).start()
         }
@@ -61,7 +61,7 @@
     @Test
     fun displayRemoved_stopsInstance() =
         testScope.runTest {
-            val instance = underTest.forDisplay(DEFAULT_DISPLAY)
+            val instance = underTest.forDisplay(DEFAULT_DISPLAY)!!
 
             fakeDisplayRepository.removeDisplay(DEFAULT_DISPLAY)
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt
index feda0c6..ab475c5 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/StatusBarModeRepositoryImplTest.kt
@@ -32,10 +32,10 @@
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.statusbar.CommandQueue
 import com.android.systemui.statusbar.data.model.StatusBarMode
-import com.android.systemui.statusbar.phone.BoundsPair
-import com.android.systemui.statusbar.phone.LetterboxAppearance
-import com.android.systemui.statusbar.phone.LetterboxAppearanceCalculator
-import com.android.systemui.statusbar.phone.StatusBarBoundsProvider
+import com.android.systemui.statusbar.layout.BoundsPair
+import com.android.systemui.statusbar.layout.LetterboxAppearance
+import com.android.systemui.statusbar.layout.LetterboxAppearanceCalculator
+import com.android.systemui.statusbar.layout.StatusBarBoundsProvider
 import com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarComponent
 import com.android.systemui.statusbar.phone.ongoingcall.data.repository.ongoingCallRepository
 import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/SystemEventChipAnimationControllerStoreImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/SystemEventChipAnimationControllerStoreImplTest.kt
index e65c04c..3cc592c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/SystemEventChipAnimationControllerStoreImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/data/repository/SystemEventChipAnimationControllerStoreImplTest.kt
@@ -56,7 +56,7 @@
     @Test
     fun beforeDisplayRemoved_doesNotStopInstances() =
         testScope.runTest {
-            val instance = underTest.forDisplay(DEFAULT_DISPLAY)
+            val instance = underTest.forDisplay(DEFAULT_DISPLAY)!!
 
             verify(instance, never()).stop()
         }
@@ -64,7 +64,7 @@
     @Test
     fun displayRemoved_stopsInstance() =
         testScope.runTest {
-            val instance = underTest.forDisplay(DEFAULT_DISPLAY)
+            val instance = underTest.forDisplay(DEFAULT_DISPLAY)!!
 
             fakeDisplayRepository.removeDisplay(DEFAULT_DISPLAY)
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/events/PrivacyDotViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/events/PrivacyDotViewControllerTest.kt
index 4795a12..db51a58 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/events/PrivacyDotViewControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/events/PrivacyDotViewControllerTest.kt
@@ -33,7 +33,7 @@
 import com.android.systemui.statusbar.events.PrivacyDotCorner.BottomRight
 import com.android.systemui.statusbar.events.PrivacyDotCorner.TopLeft
 import com.android.systemui.statusbar.events.PrivacyDotCorner.TopRight
-import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
+import com.android.systemui.statusbar.layout.StatusBarContentInsetsProvider
 import com.android.systemui.statusbar.policy.FakeConfigurationController
 import com.android.systemui.util.concurrency.DelayableExecutor
 import com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt
index 5f3668a..0a96013 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/events/SystemEventChipAnimationControllerTest.kt
@@ -27,8 +27,8 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.AnimatorTestRule
-import com.android.systemui.statusbar.phone.StatusBarContentInsetsChangedListener
-import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
+import com.android.systemui.statusbar.layout.StatusBarContentInsetsChangedListener
+import com.android.systemui.statusbar.layout.StatusBarContentInsetsProvider
 import com.android.systemui.statusbar.window.StatusBarWindowController
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.argumentCaptor
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/featurepods/media/domain/interactor/MediaControlChipInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/featurepods/media/domain/interactor/MediaControlChipInteractorTest.kt
index dd81b75..1a5f57d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/featurepods/media/domain/interactor/MediaControlChipInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/featurepods/media/domain/interactor/MediaControlChipInteractorTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.featurepods.media.domain.interactor
 
+import android.graphics.drawable.Drawable
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
@@ -23,12 +24,15 @@
 import com.android.systemui.kosmos.runTest
 import com.android.systemui.kosmos.useUnconfinedTestDispatcher
 import com.android.systemui.media.controls.data.repository.mediaFilterRepository
+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.MediaDataLoadingModel
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
@@ -102,4 +106,70 @@
 
             assertThat(model?.songName).isEqualTo(newSongName)
         }
+
+    @Test
+    fun mediaControlModel_playPauseActionChanges_emitsUpdatedModel() =
+        kosmos.runTest {
+            val model by collectLastValue(underTest.mediaControlModel)
+
+            val mockDrawable = mock<Drawable>()
+
+            val initialAction =
+                MediaAction(
+                    icon = mockDrawable,
+                    action = {},
+                    contentDescription = "Initial Action",
+                    background = mockDrawable,
+                )
+            val mediaButton = MediaButton(playOrPause = initialAction)
+            val userMedia = MediaData(active = true, semanticActions = mediaButton)
+            val instanceId = userMedia.instanceId
+            mediaFilterRepository.addSelectedUserMediaEntry(userMedia)
+            mediaFilterRepository.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(instanceId))
+
+            assertThat(model).isNotNull()
+            assertThat(model?.playOrPause).isEqualTo(initialAction)
+
+            val newAction =
+                MediaAction(
+                    icon = mockDrawable,
+                    action = {},
+                    contentDescription = "New Action",
+                    background = mockDrawable,
+                )
+            val updatedMediaButton = MediaButton(playOrPause = newAction)
+            val updatedUserMedia = userMedia.copy(semanticActions = updatedMediaButton)
+            mediaFilterRepository.addSelectedUserMediaEntry(updatedUserMedia)
+
+            assertThat(model?.playOrPause).isEqualTo(newAction)
+        }
+
+    @Test
+    fun mediaControlModel_playPauseActionRemoved_playPauseNull() =
+        kosmos.runTest {
+            val model by collectLastValue(underTest.mediaControlModel)
+
+            val mockDrawable = mock<Drawable>()
+
+            val initialAction =
+                MediaAction(
+                    icon = mockDrawable,
+                    action = {},
+                    contentDescription = "Initial Action",
+                    background = mockDrawable,
+                )
+            val mediaButton = MediaButton(playOrPause = initialAction)
+            val userMedia = MediaData(active = true, semanticActions = mediaButton)
+            val instanceId = userMedia.instanceId
+            mediaFilterRepository.addSelectedUserMediaEntry(userMedia)
+            mediaFilterRepository.addMediaDataLoadingState(MediaDataLoadingModel.Loaded(instanceId))
+
+            assertThat(model).isNotNull()
+            assertThat(model?.playOrPause).isEqualTo(initialAction)
+
+            val updatedUserMedia = userMedia.copy(semanticActions = MediaButton())
+            mediaFilterRepository.addSelectedUserMediaEntry(updatedUserMedia)
+
+            assertThat(model?.playOrPause).isNull()
+        }
 }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LetterboxAppearanceCalculatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/layout/LetterboxAppearanceCalculatorTest.kt
similarity index 82%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LetterboxAppearanceCalculatorTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/layout/LetterboxAppearanceCalculatorTest.kt
index 518b327..f1affbc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LetterboxAppearanceCalculatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/layout/LetterboxAppearanceCalculatorTest.kt
@@ -14,11 +14,11 @@
  * limitations under the License.
  */
 
-package com.android.systemui.statusbar.phone
+package com.android.systemui.statusbar.layout
 
 import android.graphics.Color
 import android.graphics.Rect
-import android.view.WindowInsetsController
+import android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS
 import android.view.WindowInsetsController.APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
@@ -42,7 +42,7 @@
 
     companion object {
         private const val DEFAULT_APPEARANCE = 0
-        private const val TEST_APPEARANCE = WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS
+        private const val TEST_APPEARANCE = APPEARANCE_LIGHT_STATUS_BARS
         private val TEST_APPEARANCE_REGION_BOUNDS = Rect(0, 0, 20, 100)
         private val TEST_APPEARANCE_REGION =
             AppearanceRegion(TEST_APPEARANCE, TEST_APPEARANCE_REGION_BOUNDS)
@@ -74,7 +74,11 @@
 
         val letterboxAppearance =
             calculator.getLetterboxAppearance(
-                TEST_APPEARANCE, TEST_APPEARANCE_REGIONS, listOf(letterbox), BoundsPair(start, end))
+                TEST_APPEARANCE,
+                TEST_APPEARANCE_REGIONS,
+                listOf(letterbox),
+                BoundsPair(start, end),
+            )
 
         expect
             .that(letterboxAppearance.appearance)
@@ -90,7 +94,11 @@
 
         val letterboxAppearance =
             calculator.getLetterboxAppearance(
-                TEST_APPEARANCE, TEST_APPEARANCE_REGIONS, listOf(letterbox), BoundsPair(start, end))
+                TEST_APPEARANCE,
+                TEST_APPEARANCE_REGIONS,
+                listOf(letterbox),
+                BoundsPair(start, end),
+            )
 
         expect
             .that(letterboxAppearance.appearance)
@@ -109,10 +117,10 @@
         val letterBoxInnerBoundsCopy = Rect(letterBoxInnerBounds)
 
         calculator.getLetterboxAppearance(
-                TEST_APPEARANCE,
-                TEST_APPEARANCE_REGIONS,
+            TEST_APPEARANCE,
+            TEST_APPEARANCE_REGIONS,
             listOf(letterboxWithInnerBounds(letterBoxInnerBounds)),
-            BoundsPair(statusBarStartSideBounds, statusBarEndSideBounds)
+            BoundsPair(statusBarStartSideBounds, statusBarEndSideBounds),
         )
 
         expect.that(statusBarStartSideBounds).isEqualTo(statusBarStartSideBoundsCopy)
@@ -129,11 +137,15 @@
 
         val letterboxAppearance =
             calculator.getLetterboxAppearance(
-                TEST_APPEARANCE, TEST_APPEARANCE_REGIONS, listOf(letterbox), BoundsPair(start, end))
+                TEST_APPEARANCE,
+                TEST_APPEARANCE_REGIONS,
+                listOf(letterbox),
+                BoundsPair(start, end),
+            )
 
         expect
-                .that(letterboxAppearance.appearance)
-                .isEqualTo(TEST_APPEARANCE or APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS)
+            .that(letterboxAppearance.appearance)
+            .isEqualTo(TEST_APPEARANCE or APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS)
         expect.that(letterboxAppearance.appearanceRegions).isEqualTo(TEST_APPEARANCE_REGIONS)
     }
 
@@ -145,7 +157,11 @@
 
         val letterboxAppearance =
             calculator.getLetterboxAppearance(
-                TEST_APPEARANCE, TEST_APPEARANCE_REGIONS, listOf(letterbox), BoundsPair(start, end))
+                TEST_APPEARANCE,
+                TEST_APPEARANCE_REGIONS,
+                listOf(letterbox),
+                BoundsPair(start, end),
+            )
 
         assertThat(letterboxAppearance.appearance).isEqualTo(TEST_APPEARANCE)
     }
@@ -158,7 +174,11 @@
 
         val letterboxAppearance =
             calculator.getLetterboxAppearance(
-                TEST_APPEARANCE, TEST_APPEARANCE_REGIONS, listOf(letterbox), BoundsPair(start, end))
+                TEST_APPEARANCE,
+                TEST_APPEARANCE_REGIONS,
+                listOf(letterbox),
+                BoundsPair(start, end),
+            )
 
         assertThat(letterboxAppearance.appearance).isEqualTo(TEST_APPEARANCE)
     }
@@ -171,7 +191,11 @@
 
         val letterboxAppearance =
             calculator.getLetterboxAppearance(
-                TEST_APPEARANCE, TEST_APPEARANCE_REGIONS, listOf(letterbox), BoundsPair(start, end))
+                TEST_APPEARANCE,
+                TEST_APPEARANCE_REGIONS,
+                listOf(letterbox),
+                BoundsPair(start, end),
+            )
 
         assertThat(letterboxAppearance.appearance).isEqualTo(TEST_APPEARANCE)
     }
@@ -184,7 +208,11 @@
 
         val letterboxAppearance =
             calculator.getLetterboxAppearance(
-                TEST_APPEARANCE, TEST_APPEARANCE_REGIONS, listOf(letterbox), BoundsPair(start, end))
+                TEST_APPEARANCE,
+                TEST_APPEARANCE_REGIONS,
+                listOf(letterbox),
+                BoundsPair(start, end),
+            )
 
         assertThat(letterboxAppearance.appearance).isEqualTo(TEST_APPEARANCE)
     }
@@ -198,7 +226,11 @@
 
         val letterboxAppearance =
             calculator.getLetterboxAppearance(
-                TEST_APPEARANCE, listOf(letterboxRegion), listOf(letterbox), BoundsPair(start, end))
+                TEST_APPEARANCE,
+                listOf(letterboxRegion),
+                listOf(letterbox),
+                BoundsPair(start, end),
+            )
 
         val letterboxAdaptedRegion = letterboxRegion.copy(bounds = letterbox.letterboxInnerBounds)
         assertThat(letterboxAppearance.appearanceRegions.toList()).contains(letterboxAdaptedRegion)
@@ -212,12 +244,17 @@
         val letterbox =
             letterboxWithBounds(
                 innerBounds = Rect(left = 25, top = 0, right = 75, bottom = 100),
-                fullBounds = Rect(left = 0, top = 0, right = 100, bottom = 100))
+                fullBounds = Rect(left = 0, top = 0, right = 100, bottom = 100),
+            )
         val letterboxRegion = TEST_APPEARANCE_REGION.copy(bounds = letterbox.letterboxFullBounds)
 
         val letterboxAppearance =
             calculator.getLetterboxAppearance(
-                TEST_APPEARANCE, listOf(letterboxRegion), listOf(letterbox), BoundsPair(start, end))
+                TEST_APPEARANCE,
+                listOf(letterboxRegion),
+                listOf(letterbox),
+                BoundsPair(start, end),
+            )
 
         val outerRegions =
             listOf(
@@ -230,8 +267,7 @@
                     Rect(left = 75, top = 0, right = 100, bottom = 100),
                 ),
             )
-        assertThat(letterboxAppearance.appearanceRegions)
-            .containsAtLeastElementsIn(outerRegions)
+        assertThat(letterboxAppearance.appearanceRegions).containsAtLeastElementsIn(outerRegions)
     }
 
     private fun letterboxWithBounds(innerBounds: Rect, fullBounds: Rect) =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarBoundsProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/layout/StatusBarBoundsProviderTest.kt
similarity index 95%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarBoundsProviderTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/layout/StatusBarBoundsProviderTest.kt
index b9cfe21..04319f05 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarBoundsProviderTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/layout/StatusBarBoundsProviderTest.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.statusbar.phone
+package com.android.systemui.statusbar.layout
 
 import android.graphics.Rect
 import android.testing.TestableLooper.RunWithLooper
@@ -23,7 +23,6 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.statusbar.phone.StatusBarBoundsProvider.BoundsChangeListener
 import com.android.systemui.util.mockito.any
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
@@ -47,7 +46,7 @@
         private val END_SIDE_BOUNDS = Rect(250, 300, 350, 400)
     }
 
-    @Mock private lateinit var boundsChangeListener: BoundsChangeListener
+    @Mock private lateinit var boundsChangeListener: StatusBarBoundsProvider.BoundsChangeListener
 
     private lateinit var boundsProvider: StatusBarBoundsProvider
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/layout/StatusBarContentInsetsProviderTest.kt
similarity index 99%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/layout/StatusBarContentInsetsProviderTest.kt
index 7a51b2d..c9c9617 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/layout/StatusBarContentInsetsProviderTest.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.statusbar.phone
+package com.android.systemui.statusbar.layout
 
 import android.content.Context
 import android.content.res.Configuration
@@ -32,6 +32,7 @@
 import com.android.systemui.dump.DumpManager
 import com.android.systemui.statusbar.commandline.CommandRegistry
 import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
+import com.android.systemui.statusbar.phone.ConfigurationControllerImpl
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.util.leak.RotationUtils
 import com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinatorTest.kt
index d38fb50..5f154ac 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinatorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/OriginalUnseenKeyguardCoordinatorTest.kt
@@ -48,7 +48,7 @@
 import com.android.systemui.statusbar.notification.domain.interactor.lockScreenShowOnlyUnseenNotificationsSetting
 import com.android.systemui.statusbar.notification.domain.interactor.seenNotificationsInteractor
 import com.android.systemui.statusbar.notification.headsup.OnHeadsUpChangedListener
-import com.android.systemui.statusbar.notification.headsup.headsUpManager
+import com.android.systemui.statusbar.notification.headsup.mockHeadsUpManager
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
 import com.android.systemui.testKosmos
 import com.android.systemui.util.settings.FakeSettings
@@ -625,7 +625,7 @@
         val keyguardCoordinator =
             OriginalUnseenKeyguardCoordinator(
                 dumpManager = kosmos.dumpManager,
-                headsUpManager = kosmos.headsUpManager,
+                headsUpManager = kosmos.mockHeadsUpManager,
                 keyguardRepository = kosmos.keyguardRepository,
                 keyguardTransitionInteractor = kosmos.keyguardTransitionInteractor,
                 logger = KeyguardCoordinatorLogger(logcatLogBuffer()),
@@ -663,7 +663,8 @@
 
         val onHeadsUpChangedListener: OnHeadsUpChangedListener
             get() =
-                argumentCaptor { verify(kosmos.headsUpManager).addListener(capture()) }.lastValue
+                argumentCaptor { verify(kosmos.mockHeadsUpManager).addListener(capture()) }
+                    .lastValue
     }
 
     companion object {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManagerTest.kt
index a70d24e..e6fbc72 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManagerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManagerTest.kt
@@ -28,11 +28,11 @@
 import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderEntryListener
 import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderGroupListener
 import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderListListener
+import com.android.systemui.util.mockito.withArgCaptor
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 import org.mockito.kotlin.any
-import org.mockito.kotlin.argumentCaptor
 import org.mockito.kotlin.inOrder
 import org.mockito.kotlin.mock
 import org.mockito.kotlin.never
@@ -59,10 +59,9 @@
     fun setUp() {
         renderStageManager = RenderStageManager()
         renderStageManager.attach(shadeListBuilder)
-
-        val captor = argumentCaptor<ShadeListBuilder.OnRenderListListener>()
-        verify(shadeListBuilder).setOnRenderListListener(captor.capture())
-        onRenderListListener = captor.lastValue
+        onRenderListListener = withArgCaptor {
+            verify(shadeListBuilder).setOnRenderListListener(capture())
+        }
     }
 
     private fun setUpRenderer() {
@@ -102,7 +101,6 @@
         // VERIFY that the renderer is not queried for group or row controllers
         inOrder(spyViewRenderer).apply {
             verify(spyViewRenderer, times(1)).onRenderList(any())
-            verify(spyViewRenderer, times(1)).getStackController()
             verify(spyViewRenderer, never()).getGroupController(any())
             verify(spyViewRenderer, never()).getRowController(any())
             verify(spyViewRenderer, times(1)).onDispatchComplete()
@@ -122,7 +120,6 @@
         // VERIFY that the renderer is queried once per group/entry
         inOrder(spyViewRenderer).apply {
             verify(spyViewRenderer, times(1)).onRenderList(any())
-            verify(spyViewRenderer, times(1)).getStackController()
             verify(spyViewRenderer, times(2)).getGroupController(any())
             verify(spyViewRenderer, times(8)).getRowController(any())
             verify(spyViewRenderer, times(1)).onDispatchComplete()
@@ -145,7 +142,6 @@
         // VERIFY that the renderer is queried once per group/entry
         inOrder(spyViewRenderer).apply {
             verify(spyViewRenderer, times(1)).onRenderList(any())
-            verify(spyViewRenderer, times(1)).getStackController()
             verify(spyViewRenderer, times(2)).getGroupController(any())
             verify(spyViewRenderer, times(8)).getRowController(any())
             verify(spyViewRenderer, times(1)).onDispatchComplete()
@@ -163,7 +159,7 @@
         onRenderListListener.onRenderList(listWith2Groups8Entries())
 
         // VERIFY that the listeners are invoked once per group and once per entry
-        verify(onAfterRenderListListener, times(1)).onAfterRenderList(any(), any())
+        verify(onAfterRenderListListener, times(1)).onAfterRenderList(any())
         verify(onAfterRenderGroupListener, times(2)).onAfterRenderGroup(any(), any())
         verify(onAfterRenderEntryListener, times(8)).onAfterRenderEntry(any(), any())
         verifyNoMoreInteractions(
@@ -183,7 +179,7 @@
         onRenderListListener.onRenderList(listOf())
 
         // VERIFY that the stack listener is invoked once but other listeners are not
-        verify(onAfterRenderListListener, times(1)).onAfterRenderList(any(), any())
+        verify(onAfterRenderListListener, times(1)).onAfterRenderList(any())
         verify(onAfterRenderGroupListener, never()).onAfterRenderGroup(any(), any())
         verify(onAfterRenderEntryListener, never()).onAfterRenderEntry(any(), any())
         verifyNoMoreInteractions(
@@ -204,8 +200,6 @@
     private class FakeNotifViewRenderer : NotifViewRenderer {
         override fun onRenderList(notifList: List<ListEntry>) {}
 
-        override fun getStackController(): NotifStackController = mock()
-
         override fun getGroupController(group: GroupEntry): NotifGroupController = mock()
 
         override fun getRowController(entry: NotificationEntry): NotifRowController = mock()
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorTest.kt
index 54ce88b..83c6150 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorTest.kt
@@ -26,7 +26,7 @@
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
-import com.android.systemui.statusbar.notification.collection.render.NotifStats
+import com.android.systemui.statusbar.notification.data.model.NotifStats
 import com.android.systemui.statusbar.notification.data.model.activeNotificationModel
 import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore
 import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
@@ -275,7 +275,6 @@
 
             activeNotificationListRepository.notifStats.value =
                 NotifStats(
-                    numActiveNotifs = 2,
                     hasNonClearableAlertingNotifs = false,
                     hasClearableAlertingNotifs = true,
                     hasNonClearableSilentNotifs = false,
@@ -293,7 +292,6 @@
 
             activeNotificationListRepository.notifStats.value =
                 NotifStats(
-                    numActiveNotifs = 2,
                     hasNonClearableAlertingNotifs = false,
                     hasClearableAlertingNotifs = false,
                     hasNonClearableSilentNotifs = false,
@@ -311,7 +309,6 @@
 
             activeNotificationListRepository.notifStats.value =
                 NotifStats(
-                    numActiveNotifs = 0,
                     hasNonClearableAlertingNotifs = false,
                     hasClearableAlertingNotifs = false,
                     hasNonClearableSilentNotifs = false,
@@ -329,7 +326,6 @@
 
             activeNotificationListRepository.notifStats.value =
                 NotifStats(
-                    numActiveNotifs = 2,
                     hasNonClearableAlertingNotifs = false,
                     hasClearableAlertingNotifs = false,
                     hasNonClearableSilentNotifs = false,
@@ -347,7 +343,6 @@
 
             activeNotificationListRepository.notifStats.value =
                 NotifStats(
-                    numActiveNotifs = 2,
                     hasNonClearableAlertingNotifs = true,
                     hasClearableAlertingNotifs = false,
                     hasNonClearableSilentNotifs = true,
@@ -365,7 +360,6 @@
 
             activeNotificationListRepository.notifStats.value =
                 NotifStats(
-                    numActiveNotifs = 2,
                     hasNonClearableAlertingNotifs = false,
                     hasClearableAlertingNotifs = true,
                     hasNonClearableSilentNotifs = false,
@@ -383,7 +377,6 @@
 
             activeNotificationListRepository.notifStats.value =
                 NotifStats(
-                    numActiveNotifs = 2,
                     hasNonClearableAlertingNotifs = false,
                     hasClearableAlertingNotifs = false,
                     hasNonClearableSilentNotifs = true,
@@ -401,7 +394,6 @@
 
             activeNotificationListRepository.notifStats.value =
                 NotifStats(
-                    numActiveNotifs = 2,
                     hasNonClearableAlertingNotifs = false,
                     hasClearableAlertingNotifs = false,
                     hasNonClearableSilentNotifs = false,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModelTest.kt
index 34f4608..3d5d1ed 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModelTest.kt
@@ -33,7 +33,6 @@
 import com.android.systemui.shared.settings.data.repository.fakeSecureSettingsRepository
 import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
 import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyShadeFix
-import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
 import com.android.systemui.statusbar.policy.data.repository.zenModeRepository
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
@@ -48,7 +47,6 @@
 @OptIn(ExperimentalCoroutinesApi::class)
 @RunWith(ParameterizedAndroidJunit4::class)
 @SmallTest
-@EnableFlags(FooterViewRefactor.FLAG_NAME)
 class EmptyShadeViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
     private val kosmos = testKosmos()
     private val testScope = kosmos.testScope
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java
index 615f4b01..daa1db2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterViewTest.java
@@ -16,8 +16,6 @@
 
 package com.android.systemui.statusbar.notification.footer.ui.view;
 
-import static com.android.systemui.log.LogAssertKt.assertLogsWtf;
-
 import static com.google.common.truth.Truth.assertThat;
 
 import static junit.framework.Assert.assertFalse;
@@ -34,7 +32,6 @@
 
 import android.content.Context;
 import android.platform.test.annotations.DisableFlags;
-import android.platform.test.annotations.EnableFlags;
 import android.platform.test.flag.junit.FlagsParameterization;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -44,7 +41,6 @@
 
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.res.R;
-import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor;
 import com.android.systemui.statusbar.notification.footer.shared.NotifRedesignFooter;
 
 import org.junit.Before;
@@ -62,8 +58,7 @@
 
     @Parameters(name = "{0}")
     public static List<FlagsParameterization> getFlags() {
-        return FlagsParameterization.progressionOf(FooterViewRefactor.FLAG_NAME,
-                NotifRedesignFooter.FLAG_NAME);
+        return FlagsParameterization.allCombinationsOf(NotifRedesignFooter.FLAG_NAME);
     }
 
     public FooterViewTest(FlagsParameterization flags) {
@@ -106,24 +101,6 @@
     }
 
     @Test
-    @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
-    public void setHistoryShown() {
-        mView.showHistory(true);
-        assertTrue(mView.isHistoryShown());
-        assertTrue(((TextView) mView.findViewById(R.id.manage_text))
-                .getText().toString().contains("History"));
-    }
-
-    @Test
-    @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
-    public void setHistoryNotShown() {
-        mView.showHistory(false);
-        assertFalse(mView.isHistoryShown());
-        assertTrue(((TextView) mView.findViewById(R.id.manage_text))
-                .getText().toString().contains("Manage"));
-    }
-
-    @Test
     public void testPerformVisibilityAnimation() {
         mView.setVisible(false /* visible */, false /* animate */);
         assertFalse(mView.isVisible());
@@ -140,7 +117,6 @@
     }
 
     @Test
-    @EnableFlags(FooterViewRefactor.FLAG_NAME)
     @DisableFlags(NotifRedesignFooter.FLAG_NAME)
     public void testSetManageOrHistoryButtonText_resourceOnlyFetchedOnce() {
         int resId = R.string.manage_notifications_history_text;
@@ -160,16 +136,6 @@
     }
 
     @Test
-    @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
-    public void testSetManageOrHistoryButtonText_expectsFlagEnabled() {
-        clearInvocations(mSpyContext);
-        int resId = R.string.manage_notifications_history_text;
-        assertLogsWtf(() -> mView.setManageOrHistoryButtonText(resId));
-        verify(mSpyContext, never()).getString(anyInt());
-    }
-
-    @Test
-    @EnableFlags(FooterViewRefactor.FLAG_NAME)
     @DisableFlags(NotifRedesignFooter.FLAG_NAME)
     public void testSetManageOrHistoryButtonDescription_resourceOnlyFetchedOnce() {
         int resId = R.string.manage_notifications_history_text;
@@ -189,16 +155,6 @@
     }
 
     @Test
-    @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
-    public void testSetManageOrHistoryButtonDescription_expectsFlagEnabled() {
-        clearInvocations(mSpyContext);
-        int resId = R.string.accessibility_clear_all;
-        assertLogsWtf(() -> mView.setManageOrHistoryButtonDescription(resId));
-        verify(mSpyContext, never()).getString(anyInt());
-    }
-
-    @Test
-    @EnableFlags(FooterViewRefactor.FLAG_NAME)
     public void testSetClearAllButtonText_resourceOnlyFetchedOnce() {
         int resId = R.string.clear_all_notifications_text;
         mView.setClearAllButtonText(resId);
@@ -217,16 +173,6 @@
     }
 
     @Test
-    @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
-    public void testSetClearAllButtonText_expectsFlagEnabled() {
-        clearInvocations(mSpyContext);
-        int resId = R.string.clear_all_notifications_text;
-        assertLogsWtf(() -> mView.setClearAllButtonText(resId));
-        verify(mSpyContext, never()).getString(anyInt());
-    }
-
-    @Test
-    @EnableFlags(FooterViewRefactor.FLAG_NAME)
     public void testSetClearAllButtonDescription_resourceOnlyFetchedOnce() {
         int resId = R.string.accessibility_clear_all;
         mView.setClearAllButtonDescription(resId);
@@ -245,16 +191,6 @@
     }
 
     @Test
-    @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
-    public void testSetClearAllButtonDescription_expectsFlagEnabled() {
-        clearInvocations(mSpyContext);
-        int resId = R.string.accessibility_clear_all;
-        assertLogsWtf(() -> mView.setClearAllButtonDescription(resId));
-        verify(mSpyContext, never()).getString(anyInt());
-    }
-
-    @Test
-    @EnableFlags(FooterViewRefactor.FLAG_NAME)
     public void testSetMessageString_resourceOnlyFetchedOnce() {
         int resId = R.string.unlock_to_see_notif_text;
         mView.setMessageString(resId);
@@ -273,16 +209,6 @@
     }
 
     @Test
-    @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
-    public void testSetMessageString_expectsFlagEnabled() {
-        clearInvocations(mSpyContext);
-        int resId = R.string.unlock_to_see_notif_text;
-        assertLogsWtf(() -> mView.setMessageString(resId));
-        verify(mSpyContext, never()).getString(anyInt());
-    }
-
-    @Test
-    @EnableFlags(FooterViewRefactor.FLAG_NAME)
     public void testSetMessageIcon_resourceOnlyFetchedOnce() {
         int resId = R.drawable.ic_friction_lock_closed;
         mView.setMessageIcon(resId);
@@ -298,15 +224,6 @@
     }
 
     @Test
-    @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
-    public void testSetMessageIcon_expectsFlagEnabled() {
-        clearInvocations(mSpyContext);
-        int resId = R.drawable.ic_friction_lock_closed;
-        assertLogsWtf(() -> mView.setMessageIcon(resId));
-        verify(mSpyContext, never()).getDrawable(anyInt());
-    }
-
-    @Test
     public void testSetFooterLabelVisible() {
         mView.setFooterLabelVisible(true);
         assertThat(mView.findViewById(R.id.unlock_prompt_footer).getVisibility())
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt
index 1adfc2b..b3a60b0 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt
@@ -37,10 +37,9 @@
 import com.android.systemui.res.R
 import com.android.systemui.shade.shadeTestUtil
 import com.android.systemui.shared.settings.data.repository.fakeSecureSettingsRepository
-import com.android.systemui.statusbar.notification.collection.render.NotifStats
+import com.android.systemui.statusbar.notification.data.model.NotifStats
 import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository
 import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyShadeFix
-import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
 import com.android.systemui.statusbar.notification.footer.shared.NotifRedesignFooter
 import com.android.systemui.testKosmos
 import com.android.systemui.util.ui.isAnimating
@@ -57,7 +56,6 @@
 
 @RunWith(ParameterizedAndroidJunit4::class)
 @SmallTest
-@EnableFlags(FooterViewRefactor.FLAG_NAME)
 class FooterViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
     private val kosmos =
         testKosmos().apply {
@@ -117,7 +115,6 @@
 
             activeNotificationListRepository.notifStats.value =
                 NotifStats(
-                    numActiveNotifs = 2,
                     hasNonClearableAlertingNotifs = false,
                     hasClearableAlertingNotifs = true,
                     hasNonClearableSilentNotifs = false,
@@ -135,7 +132,6 @@
 
             activeNotificationListRepository.notifStats.value =
                 NotifStats(
-                    numActiveNotifs = 2,
                     hasNonClearableAlertingNotifs = false,
                     hasClearableAlertingNotifs = false,
                     hasNonClearableSilentNotifs = false,
@@ -153,7 +149,6 @@
 
             activeNotificationListRepository.notifStats.value =
                 NotifStats(
-                    numActiveNotifs = 2,
                     hasNonClearableAlertingNotifs = false,
                     hasClearableAlertingNotifs = true,
                     hasNonClearableSilentNotifs = false,
@@ -185,7 +180,6 @@
             // AND there are clearable notifications
             activeNotificationListRepository.notifStats.value =
                 NotifStats(
-                    numActiveNotifs = 2,
                     hasNonClearableAlertingNotifs = false,
                     hasClearableAlertingNotifs = true,
                     hasNonClearableSilentNotifs = false,
@@ -219,7 +213,6 @@
             // AND there are clearable notifications
             activeNotificationListRepository.notifStats.value =
                 NotifStats(
-                    numActiveNotifs = 2,
                     hasNonClearableAlertingNotifs = false,
                     hasClearableAlertingNotifs = true,
                     hasNonClearableSilentNotifs = false,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt
index be20bc1..d86c6ef 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModelTest.kt
@@ -225,7 +225,7 @@
             val displayId = 123
             darkIconRepository.darkState(displayId).value =
                 SysuiDarkIconDispatcher.DarkChange(emptyList(), 0f, 0xAABBCC)
-            val iconColors by collectLastValue(underTest.iconColors(displayId))
+            val iconColors by collectLastValue(underTest.iconColors(displayId)!!)
             assertThat(iconColors).isNotNull()
 
             assertThat(iconColors!!.tint).isEqualTo(0xAABBCC)
@@ -241,7 +241,7 @@
             val displayId = 321
             darkIconRepository.darkState(displayId).value =
                 SysuiDarkIconDispatcher.DarkChange(listOf(Rect(0, 0, 5, 5)), 0f, 0xAABBCC)
-            val iconColors by collectLastValue(underTest.iconColors(displayId))
+            val iconColors by collectLastValue(underTest.iconColors(displayId)!!)
             val staticDrawableColor = iconColors?.staticDrawableColor(Rect(6, 6, 7, 7))
             assertThat(staticDrawableColor).isEqualTo(DarkIconDispatcher.DEFAULT_ICON_TINT)
         }
@@ -252,7 +252,7 @@
             val displayId = 987
             darkIconRepository.darkState(displayId).value =
                 SysuiDarkIconDispatcher.DarkChange(listOf(Rect(0, 0, 5, 5)), 0f, 0xAABBCC)
-            val iconColors by collectLastValue(underTest.iconColors(displayId))
+            val iconColors by collectLastValue(underTest.iconColors(displayId)!!)
             assertThat(iconColors!!.staticDrawableColor(Rect(6, 6, 7, 7)))
                 .isEqualTo(DarkIconDispatcher.DEFAULT_ICON_TINT)
         }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
index 14bbd38..72a91bc 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
@@ -279,7 +279,7 @@
                 notification);
         RemoteViews headerRemoteViews;
         if (lowPriority) {
-            headerRemoteViews = builder.makeLowPriorityContentView(true);
+            headerRemoteViews = builder.makeLowPriorityContentView(true, false);
         } else {
             headerRemoteViews = builder.makeNotificationGroupHeader();
         }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
index 87abd0a..256da253 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
@@ -22,6 +22,7 @@
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
 import com.android.systemui.statusbar.notification.row.ExpandableView
 import com.android.systemui.statusbar.notification.shared.NotificationMinimalism
+import com.android.systemui.statusbar.notification.shelf.NotificationShelfIconContainer
 import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.StackScrollAlgorithmState
 import com.android.systemui.util.mockito.mock
 import junit.framework.Assert.assertEquals
@@ -134,89 +135,208 @@
     @EnableFlags(NotificationMinimalism.FLAG_NAME)
     fun testAlignment_splitShade_LTR() {
         // Given: LTR mode, split shade
+        val width = 100
+        val actualWidth = 40
+        val iconContainerPadding = 16f
         val shelfSpy =
-            prepareShelfSpy(shelf, rtl = false, splitShade = true, width = 100, actualWidth = 40)
+            prepareShelfSpy(
+                shelf,
+                rtl = false,
+                splitShade = true,
+                width = width,
+                actualWidth = actualWidth,
+                iconContainerPadding = iconContainerPadding,
+            )
 
         // Then: shelf should align to end
         assertTrue(shelfSpy.isAlignedToEnd)
         assertTrue(shelfSpy.isAlignedToRight)
         assertTrue(shelfSpy.mBackgroundNormal.alignToEnd)
-        assertTrue(shelfSpy.mShelfIcons.alignToEnd)
+
+        // Then: icon container should align to end, right
+        val iconContainer = shelfSpy.shelfIcons as NotificationShelfIconContainer
+        assertTrue(iconContainer.alignToEnd)
+        assertTrue(iconContainer.isAlignedToRight)
+
+        // Then: icon container bounds are updated based on the widths and paddings
+        val actualPaddingStart = iconContainerPadding
+        val actualPaddingEnd = iconContainerPadding
+        val expectedLeftBound = width - actualWidth + actualPaddingStart
+        val expectedRightBound = width - actualPaddingEnd
+        assertEquals(expectedLeftBound, iconContainer.leftBound)
+        assertEquals(expectedRightBound, iconContainer.rightBound)
     }
 
     @Test
     @EnableFlags(NotificationMinimalism.FLAG_NAME)
     fun testAlignment_nonSplitShade_LTR() {
         // Given: LTR mode, non split shade
+        val width = 100
+        val actualWidth = 40
+        val iconContainerPadding = 16f
         val shelfSpy =
-            prepareShelfSpy(shelf, rtl = false, splitShade = false, width = 100, actualWidth = 40)
+            prepareShelfSpy(
+                shelf,
+                rtl = false,
+                splitShade = false,
+                width = width,
+                actualWidth = actualWidth,
+                iconContainerPadding = iconContainerPadding,
+            )
 
         // Then: shelf should not align to end
+        // left bound of icon container should be 16f (actualPaddingStart)
+        // right bound of icon container should be 24f (actualWidth - actualPaddingEnd)
         assertFalse(shelfSpy.isAlignedToEnd)
         assertFalse(shelfSpy.isAlignedToRight)
         assertFalse(shelfSpy.mBackgroundNormal.alignToEnd)
-        assertFalse(shelfSpy.mShelfIcons.alignToEnd)
+
+        // Then: icon container should align to start, left
+
+        val iconContainer = shelfSpy.shelfIcons as NotificationShelfIconContainer
+        assertFalse(iconContainer.alignToEnd)
+        assertFalse(iconContainer.isAlignedToRight)
+
+        // Then: icon container bounds are updated based on the widths and paddings
+        val actualPaddingStart = iconContainerPadding
+        val actualPaddingEnd = iconContainerPadding
+        val expectedLeftBound = actualPaddingStart
+        val expectedRightBound = actualWidth - actualPaddingEnd
+        assertEquals(expectedLeftBound, iconContainer.leftBound)
+        assertEquals(expectedRightBound, iconContainer.rightBound)
     }
 
     @Test
     @EnableFlags(NotificationMinimalism.FLAG_NAME)
     fun testAlignment_splitShade_RTL() {
         // Given: RTL mode, split shade
+        val width = 100
+        val actualWidth = 40
+        val iconContainerPadding = 16f
         val shelfSpy =
-            prepareShelfSpy(shelf, rtl = true, splitShade = true, width = 100, actualWidth = 40)
+            prepareShelfSpy(
+                shelf,
+                rtl = true,
+                splitShade = true,
+                width = width,
+                actualWidth = actualWidth,
+                iconContainerPadding = iconContainerPadding,
+            )
 
         // Then: shelf should align to end, but to left due to RTL
+        // left bound of icon container should be 16f (actualPaddingStart)
+        // right bound of icon container should be 24f (actualWidth - actualPaddingEnd)
         assertTrue(shelfSpy.isAlignedToEnd)
         assertFalse(shelfSpy.isAlignedToRight)
         assertTrue(shelfSpy.mBackgroundNormal.alignToEnd)
-        assertTrue(shelfSpy.mShelfIcons.alignToEnd)
+
+        // Then: icon container should align to end, left
+        val iconContainer = shelfSpy.shelfIcons as NotificationShelfIconContainer
+        assertTrue(iconContainer.alignToEnd)
+        assertFalse(iconContainer.isAlignedToRight)
+
+        // Then: icon container bounds are updated based on the widths and paddings
+        val actualPaddingStart = iconContainerPadding
+        val actualPaddingEnd = iconContainerPadding
+        val expectedLeftBound = actualPaddingStart
+        val expectedRightBound = actualWidth - actualPaddingEnd
+        assertEquals(expectedLeftBound, iconContainer.leftBound)
+        assertEquals(expectedRightBound, iconContainer.rightBound)
     }
 
     @Test
     @EnableFlags(NotificationMinimalism.FLAG_NAME)
     fun testAlignment_nonSplitShade_RTL() {
         // Given: RTL mode, non split shade
+        val width = 100
+        val actualWidth = 40
+        val iconContainerPadding = 16f
         val shelfSpy =
-            prepareShelfSpy(shelf, rtl = true, splitShade = false, width = 100, actualWidth = 40)
+            prepareShelfSpy(
+                shelf,
+                rtl = true,
+                splitShade = false,
+                width = width,
+                actualWidth = actualWidth,
+                iconContainerPadding = iconContainerPadding,
+            )
 
         // Then: shelf should not align to end, but to right due to RTL
         assertFalse(shelfSpy.isAlignedToEnd)
         assertTrue(shelfSpy.isAlignedToRight)
         assertFalse(shelfSpy.mBackgroundNormal.alignToEnd)
-        assertFalse(shelfSpy.mShelfIcons.alignToEnd)
+
+        // Then: icon container should align to start, right
+        val iconContainer = shelfSpy.shelfIcons as NotificationShelfIconContainer
+        assertFalse(iconContainer.alignToEnd)
+        assertTrue(iconContainer.isAlignedToRight)
+
+        // Then: icon container bounds are updated based on the widths and paddings
+        val actualPaddingStart = iconContainerPadding
+        val actualPaddingEnd = iconContainerPadding
+        val expectedLeftBound = width - actualWidth + actualPaddingStart
+        val expectedRightBound = width - actualPaddingEnd
+        assertEquals(expectedLeftBound, iconContainer.leftBound)
+        assertEquals(expectedRightBound, iconContainer.rightBound)
     }
 
     @Test
     @EnableFlags(NotificationMinimalism.FLAG_NAME)
     fun testGetShelfLeftBound_splitShade_LTR() {
         // Given: LTR mode, split shade
+        val width = 100
+        val actualWidth = 40
         val shelfSpy =
-            prepareShelfSpy(shelf, rtl = false, splitShade = true, width = 100, actualWidth = 40)
+            prepareShelfSpy(
+                shelf,
+                rtl = false,
+                splitShade = true,
+                width = width,
+                actualWidth = actualWidth,
+            )
 
         // When: get the left bound of the shelf
         val shelfLeftBound = shelfSpy.shelfLeftBound
 
         // Then: should be equal to shelf's width - actual width
-        assertEquals(60f, shelfLeftBound)
+        val expectedLeftBound = (width - actualWidth).toFloat()
+        assertEquals(expectedLeftBound, shelfLeftBound)
     }
 
     @Test
     @EnableFlags(NotificationMinimalism.FLAG_NAME)
     fun testGetShelfRightBound_splitShade_LTR() {
         // Given: LTR mode, split shade, width 100, actual width 40
+        val width = 100
+        val actualWidth = 40
         val shelfSpy =
-            prepareShelfSpy(shelf, rtl = false, splitShade = true, width = 100, actualWidth = 40)
+            prepareShelfSpy(
+                shelf,
+                rtl = false,
+                splitShade = true,
+                width = width,
+                actualWidth = actualWidth,
+            )
 
         // Then: the right bound of the shelf should be equal to shelf's width
-        assertEquals(100f, shelfSpy.shelfRightBound)
+        val expectedRightBound = width.toFloat()
+        assertEquals(expectedRightBound, shelfSpy.shelfRightBound)
     }
 
     @Test
     @EnableFlags(NotificationMinimalism.FLAG_NAME)
     fun testGetShelfLeftBound_nonSplitShade_LTR() {
         // Given: LTR mode, non split shade
+        val width = 100
+        val actualWidth = 40
         val shelfSpy =
-            prepareShelfSpy(shelf, rtl = false, splitShade = false, width = 100, actualWidth = 40)
+            prepareShelfSpy(
+                shelf,
+                rtl = false,
+                splitShade = false,
+                width = width,
+                actualWidth = actualWidth,
+            )
 
         // When: get the left bound of the shelf
         val shelfLeftBound = shelfSpy.shelfLeftBound
@@ -229,19 +349,35 @@
     @EnableFlags(NotificationMinimalism.FLAG_NAME)
     fun testGetShelfRightBound_nonSplitShade_LTR() {
         // Given: LTR mode, non split shade, width 100, actual width 40
+        val width = 100
+        val actualWidth = 40
         val shelfSpy =
-            prepareShelfSpy(shelf, rtl = false, splitShade = false, width = 100, actualWidth = 40)
+            prepareShelfSpy(
+                shelf,
+                rtl = false,
+                splitShade = false,
+                width = width,
+                actualWidth = actualWidth,
+            )
 
         // Then: the right bound of the shelf should be equal to shelf's actual width
-        assertEquals(40f, shelfSpy.shelfRightBound)
+        assertEquals(actualWidth.toFloat(), shelfSpy.shelfRightBound)
     }
 
     @Test
     @EnableFlags(NotificationMinimalism.FLAG_NAME)
     fun testGetShelfLeftBound_splitShade_RTL() {
         // Given: RTL mode, split shade
+        val width = 100
+        val actualWidth = 40
         val shelfSpy =
-            prepareShelfSpy(shelf, rtl = true, splitShade = true, width = 100, actualWidth = 40)
+            prepareShelfSpy(
+                shelf,
+                rtl = true,
+                splitShade = true,
+                width = width,
+                actualWidth = actualWidth,
+            )
 
         // When: get the left bound of the shelf
         val shelfLeftBound = shelfSpy.shelfLeftBound
@@ -254,36 +390,61 @@
     @EnableFlags(NotificationMinimalism.FLAG_NAME)
     fun testGetShelfRightBound_splitShade_RTL() {
         // Given: RTL mode, split shade, width 100, actual width 40
+        val width = 100
+        val actualWidth = 40
         val shelfSpy =
-            prepareShelfSpy(shelf, rtl = true, splitShade = true, width = 100, actualWidth = 40)
+            prepareShelfSpy(
+                shelf,
+                rtl = true,
+                splitShade = true,
+                width = width,
+                actualWidth = actualWidth,
+            )
 
         // Then: the right bound of the shelf should be equal to shelf's actual width
-        assertEquals(40f, shelfSpy.shelfRightBound)
+        assertEquals(actualWidth.toFloat(), shelfSpy.shelfRightBound)
     }
 
     @Test
     @EnableFlags(NotificationMinimalism.FLAG_NAME)
     fun testGetShelfLeftBound_nonSplitShade_RTL() {
         // Given: RTL mode, non split shade
+        val width = 100
+        val actualWidth = 40
         val shelfSpy =
-            prepareShelfSpy(shelf, rtl = true, splitShade = false, width = 100, actualWidth = 40)
+            prepareShelfSpy(
+                shelf,
+                rtl = true,
+                splitShade = false,
+                width = width,
+                actualWidth = actualWidth,
+            )
 
         // When: get the left bound of the shelf
         val shelfLeftBound = shelfSpy.shelfLeftBound
 
         // Then: should be equal to shelf's width - actual width
-        assertEquals(60f, shelfLeftBound)
+        val expectedLeftBound = (width - actualWidth).toFloat()
+        assertEquals(expectedLeftBound, shelfLeftBound)
     }
 
     @Test
     @EnableFlags(NotificationMinimalism.FLAG_NAME)
     fun testGetShelfRightBound_nonSplitShade_RTL() {
         // Given: LTR mode, non split shade, width 100, actual width 40
+        val width = 100
+        val actualWidth = 40
         val shelfSpy =
-            prepareShelfSpy(shelf, rtl = true, splitShade = false, width = 100, actualWidth = 40)
+            prepareShelfSpy(
+                shelf,
+                rtl = true,
+                splitShade = false,
+                width = width,
+                actualWidth = actualWidth,
+            )
 
         // Then: the right bound of the shelf should be equal to shelf's width
-        assertEquals(100f, shelfSpy.shelfRightBound)
+        assertEquals(width.toFloat(), shelfSpy.shelfRightBound)
     }
 
     private fun prepareShelfSpy(
@@ -292,12 +453,23 @@
         splitShade: Boolean,
         width: Int,
         actualWidth: Int,
+        iconContainerPadding: Float? = null,
     ): NotificationShelf {
         val shelfSpy = spy(shelf)
         whenever(shelfSpy.isLayoutRtl).thenReturn(rtl)
         whenever(ambientState.useSplitShade).thenReturn(splitShade)
-        whenever(shelfSpy.width).thenReturn(width)
+        shelfSpy.layout(0, 0, width, 5)
+        shelfSpy.mShelfIcons.layout(0, 0, width, 5)
+        iconContainerPadding?.let {
+            shelfSpy.mShelfIcons.actualPaddingStart = it
+            shelfSpy.mShelfIcons.setActualPaddingEnd(it)
+        }
         shelfSpy.setActualWidth(actualWidth.toFloat())
+
+        val iconContainerSpy = spy(shelf.mShelfIcons)
+        whenever(iconContainerSpy.isLayoutRtl).thenReturn(rtl)
+        whenever(shelfSpy.shelfIcons).thenReturn(iconContainerSpy)
+
         return shelfSpy
     }
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
index c6cffa9..20cd6c7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
@@ -25,14 +25,10 @@
 
 import static kotlinx.coroutines.flow.FlowKt.emptyFlow;
 
-import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.argThat;
-import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
-import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
@@ -45,7 +41,6 @@
 import android.platform.test.annotations.EnableFlags;
 import android.testing.TestableLooper;
 import android.view.MotionEvent;
-import android.view.View;
 import android.view.ViewTreeObserver;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -57,15 +52,12 @@
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.systemui.ExpandHelper;
 import com.android.systemui.SysuiTestCase;
-import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
 import com.android.systemui.classifier.FalsingCollectorFake;
 import com.android.systemui.classifier.FalsingManagerFake;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.flags.DisableSceneContainer;
 import com.android.systemui.flags.EnableSceneContainer;
 import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository;
-import com.android.systemui.keyguard.shared.model.KeyguardState;
-import com.android.systemui.keyguard.shared.model.TransitionStep;
 import com.android.systemui.kosmos.KosmosJavaAdapter;
 import com.android.systemui.media.controls.ui.controller.KeyguardMediaController;
 import com.android.systemui.plugins.ActivityStarter;
@@ -78,23 +70,18 @@
 import com.android.systemui.statusbar.LockscreenShadeTransitionController;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener;
-import com.android.systemui.statusbar.NotificationRemoteInputManager;
-import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.notification.ColorUpdateLogger;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
-import com.android.systemui.statusbar.notification.headsup.HeadsUpTouchHelper;
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
 import com.android.systemui.statusbar.notification.collection.NotifCollection;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
 import com.android.systemui.statusbar.notification.collection.provider.NotificationDismissibilityProvider;
 import com.android.systemui.statusbar.notification.collection.provider.VisibilityLocationProviderDelegator;
 import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
-import com.android.systemui.statusbar.notification.collection.render.NotifStats;
 import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
-import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController;
-import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor;
-import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor;
+import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
+import com.android.systemui.statusbar.notification.headsup.HeadsUpTouchHelper;
 import com.android.systemui.statusbar.notification.init.NotificationsController;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
@@ -106,11 +93,8 @@
 import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.DeviceProvisionedController;
-import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
 import com.android.systemui.statusbar.policy.ResourcesSplitShadeStateController;
 import com.android.systemui.statusbar.policy.SensitiveNotificationProtectionController;
-import com.android.systemui.statusbar.policy.ZenModeController;
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.util.settings.SecureSettings;
 import com.android.systemui.wallpapers.domain.interactor.WallpaperInteractor;
@@ -145,16 +129,13 @@
     @Mock private Provider<IStatusBarService> mStatusBarService;
     @Mock private NotificationRoundnessManager mNotificationRoundnessManager;
     @Mock private TunerService mTunerService;
-    @Mock private DeviceProvisionedController mDeviceProvisionedController;
     @Mock private DynamicPrivacyController mDynamicPrivacyController;
     @Mock private ConfigurationController mConfigurationController;
     @Mock private NotificationStackScrollLayout mNotificationStackScrollLayout;
-    @Mock private ZenModeController mZenModeController;
     @Mock private KeyguardMediaController mKeyguardMediaController;
     @Mock private SysuiStatusBarStateController mSysuiStatusBarStateController;
     @Mock private KeyguardBypassController mKeyguardBypassController;
     @Mock private PowerInteractor mPowerInteractor;
-    @Mock private PrimaryBouncerInteractor mPrimaryBouncerInteractor;
     @Mock private WallpaperInteractor mWallpaperInteractor;
     @Mock private NotificationLockscreenUserManager mNotificationLockscreenUserManager;
     @Mock private MetricsLogger mMetricsLogger;
@@ -164,12 +145,10 @@
     private NotificationSwipeHelper.Builder mNotificationSwipeHelperBuilder;
     @Mock private NotificationSwipeHelper mNotificationSwipeHelper;
     @Mock private GroupExpansionManager mGroupExpansionManager;
-    @Mock private SectionHeaderController mSilentHeaderController;
     @Mock private NotifPipeline mNotifPipeline;
     @Mock private NotifCollection mNotifCollection;
     @Mock private UiEventLogger mUiEventLogger;
     @Mock private LockscreenShadeTransitionController mLockscreenShadeTransitionController;
-    @Mock private NotificationRemoteInputManager mRemoteInputManager;
     @Mock private VisibilityLocationProviderDelegator mVisibilityLocationProviderDelegator;
     @Mock private ShadeController mShadeController;
     @Mock private Provider<WindowRootView> mWindowRootView;
@@ -193,9 +172,6 @@
     @Captor
     private ArgumentCaptor<StatusBarStateController.StateListener> mStateListenerArgumentCaptor;
 
-    private final SeenNotificationsInteractor mSeenNotificationsInteractor =
-            mKosmos.getSeenNotificationsInteractor();
-
     private NotificationStackScrollLayoutController mController;
 
     private NotificationTestHelper mNotificationTestHelper;
@@ -279,114 +255,6 @@
     }
 
     @Test
-    @DisableFlags(FooterViewRefactor.FLAG_NAME)
-    public void testUpdateEmptyShadeView_notificationsVisible_zenHiding() {
-        when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(true);
-        initController(/* viewIsAttached= */ true);
-
-        setupShowEmptyShadeViewState(true);
-        reset(mNotificationStackScrollLayout);
-        mController.updateShowEmptyShadeView();
-        verify(mNotificationStackScrollLayout).updateEmptyShadeView(
-                /* visible= */ true,
-                /* notifVisibleInShade= */ true);
-
-        setupShowEmptyShadeViewState(false);
-        reset(mNotificationStackScrollLayout);
-        mController.updateShowEmptyShadeView();
-        verify(mNotificationStackScrollLayout).updateEmptyShadeView(
-                /* visible= */ false,
-                /* notifVisibleInShade= */ true);
-    }
-
-    @Test
-    @DisableFlags(FooterViewRefactor.FLAG_NAME)
-    public void testUpdateEmptyShadeView_notificationsHidden_zenNotHiding() {
-        when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false);
-        initController(/* viewIsAttached= */ true);
-
-        setupShowEmptyShadeViewState(true);
-        reset(mNotificationStackScrollLayout);
-        mController.updateShowEmptyShadeView();
-        verify(mNotificationStackScrollLayout).updateEmptyShadeView(
-                /* visible= */ true,
-                /* notifVisibleInShade= */ false);
-
-        setupShowEmptyShadeViewState(false);
-        reset(mNotificationStackScrollLayout);
-        mController.updateShowEmptyShadeView();
-        verify(mNotificationStackScrollLayout).updateEmptyShadeView(
-                /* visible= */ false,
-                /* notifVisibleInShade= */ false);
-    }
-
-    @Test
-    @DisableFlags(FooterViewRefactor.FLAG_NAME)
-    public void testUpdateEmptyShadeView_splitShadeMode_alwaysShowEmptyView() {
-        when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false);
-        initController(/* viewIsAttached= */ true);
-
-        verify(mSysuiStatusBarStateController).addCallback(
-                mStateListenerArgumentCaptor.capture(), anyInt());
-        StatusBarStateController.StateListener stateListener =
-                mStateListenerArgumentCaptor.getValue();
-        stateListener.onStateChanged(SHADE);
-        mController.getView().removeAllViews();
-
-        mController.setQsFullScreen(false);
-        reset(mNotificationStackScrollLayout);
-        mController.updateShowEmptyShadeView();
-        verify(mNotificationStackScrollLayout).updateEmptyShadeView(
-                /* visible= */ true,
-                /* notifVisibleInShade= */ false);
-
-        mController.setQsFullScreen(true);
-        reset(mNotificationStackScrollLayout);
-        mController.updateShowEmptyShadeView();
-        verify(mNotificationStackScrollLayout).updateEmptyShadeView(
-                /* visible= */ true,
-                /* notifVisibleInShade= */ false);
-    }
-
-    @Test
-    @DisableFlags(FooterViewRefactor.FLAG_NAME)
-    public void testUpdateEmptyShadeView_bouncerShowing_hideEmptyView() {
-        when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false);
-        initController(/* viewIsAttached= */ true);
-
-        when(mPrimaryBouncerInteractor.isBouncerShowing()).thenReturn(true);
-
-        setupShowEmptyShadeViewState(true);
-        reset(mNotificationStackScrollLayout);
-        mController.updateShowEmptyShadeView();
-
-        // THEN the PrimaryBouncerInteractor value is used. Since the bouncer is showing, we
-        // hide the empty view.
-        verify(mNotificationStackScrollLayout).updateEmptyShadeView(
-                /* visible= */ false,
-                /* areNotificationsHiddenInShade= */ false);
-    }
-
-    @Test
-    @DisableFlags(FooterViewRefactor.FLAG_NAME)
-    public void testUpdateEmptyShadeView_bouncerNotShowing_showEmptyView() {
-        when(mZenModeController.areNotificationsHiddenInShade()).thenReturn(false);
-        initController(/* viewIsAttached= */ true);
-
-        when(mPrimaryBouncerInteractor.isBouncerShowing()).thenReturn(false);
-
-        setupShowEmptyShadeViewState(true);
-        reset(mNotificationStackScrollLayout);
-        mController.updateShowEmptyShadeView();
-
-        // THEN the PrimaryBouncerInteractor value is used. Since the bouncer isn't showing, we
-        // can show the empty view.
-        verify(mNotificationStackScrollLayout).updateEmptyShadeView(
-                /* visible= */ true,
-                /* areNotificationsHiddenInShade= */ false);
-    }
-
-    @Test
     public void testOnUserChange_verifyNotSensitive() {
         when(mNotificationLockscreenUserManager.isAnyProfilePublicMode()).thenReturn(false);
         initController(/* viewIsAttached= */ true);
@@ -788,31 +656,6 @@
     }
 
     @Test
-    @DisableFlags(FooterViewRefactor.FLAG_NAME)
-    public void testUpdateFooter_remoteInput() {
-        ArgumentCaptor<RemoteInputController.Callback> callbackCaptor =
-                ArgumentCaptor.forClass(RemoteInputController.Callback.class);
-        doNothing().when(mRemoteInputManager).addControllerCallback(callbackCaptor.capture());
-        when(mRemoteInputManager.isRemoteInputActive()).thenReturn(false);
-        initController(/* viewIsAttached= */ true);
-        verify(mNotificationStackScrollLayout).setIsRemoteInputActive(false);
-        RemoteInputController.Callback callback = callbackCaptor.getValue();
-        callback.onRemoteInputActive(true);
-        verify(mNotificationStackScrollLayout).setIsRemoteInputActive(true);
-    }
-
-    @Test
-    @DisableFlags(FooterViewRefactor.FLAG_NAME)
-    public void testSetNotifStats_updatesHasFilteredOutSeenNotifications() {
-        initController(/* viewIsAttached= */ true);
-        mSeenNotificationsInteractor.setHasFilteredOutSeenNotifications(true);
-        mController.getNotifStackController().setNotifStats(NotifStats.getEmpty());
-        verify(mNotificationStackScrollLayout).setHasFilteredOutSeenNotifications(true);
-        verify(mNotificationStackScrollLayout).updateFooter();
-        verify(mNotificationStackScrollLayout).updateEmptyShadeView(anyBoolean(), anyBoolean());
-    }
-
-    @Test
     public void testAttach_updatesViewStatusBarState() {
         // GIVEN: Controller is attached
         initController(/* viewIsAttached= */ true);
@@ -844,98 +687,6 @@
     }
 
     @Test
-    @DisableFlags(FooterViewRefactor.FLAG_NAME)
-    public void updateImportantForAccessibility_noChild_onKeyGuard_notImportantForA11y() {
-        // GIVEN: Controller is attached, active notifications is empty,
-        // and mNotificationStackScrollLayout.onKeyguard() is true
-        initController(/* viewIsAttached= */ true);
-        when(mNotificationStackScrollLayout.onKeyguard()).thenReturn(true);
-        mController.getNotifStackController().setNotifStats(NotifStats.getEmpty());
-
-        // THEN: mNotificationStackScrollLayout should not be important for A11y
-        verify(mNotificationStackScrollLayout)
-                .setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
-    }
-
-    @Test
-    @DisableFlags(FooterViewRefactor.FLAG_NAME)
-    public void updateImportantForAccessibility_hasChild_onKeyGuard_importantForA11y() {
-        // GIVEN: Controller is attached, active notifications is not empty,
-        // and mNotificationStackScrollLayout.onKeyguard() is true
-        initController(/* viewIsAttached= */ true);
-        when(mNotificationStackScrollLayout.onKeyguard()).thenReturn(true);
-        mController.getNotifStackController().setNotifStats(
-                new NotifStats(
-                        /* numActiveNotifs = */ 1,
-                        /* hasNonClearableAlertingNotifs = */ false,
-                        /* hasClearableAlertingNotifs = */ false,
-                        /* hasNonClearableSilentNotifs = */ false,
-                        /* hasClearableSilentNotifs = */ false)
-        );
-
-        // THEN: mNotificationStackScrollLayout should be important for A11y
-        verify(mNotificationStackScrollLayout)
-                .setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
-    }
-
-    @Test
-    @DisableFlags(FooterViewRefactor.FLAG_NAME)
-    public void updateImportantForAccessibility_hasChild_notOnKeyGuard_importantForA11y() {
-        // GIVEN: Controller is attached, active notifications is not empty,
-        // and mNotificationStackScrollLayout.onKeyguard() is false
-        initController(/* viewIsAttached= */ true);
-        when(mNotificationStackScrollLayout.onKeyguard()).thenReturn(false);
-        mController.getNotifStackController().setNotifStats(
-                new NotifStats(
-                        /* numActiveNotifs = */ 1,
-                        /* hasNonClearableAlertingNotifs = */ false,
-                        /* hasClearableAlertingNotifs = */ false,
-                        /* hasNonClearableSilentNotifs = */ false,
-                        /* hasClearableSilentNotifs = */ false)
-        );
-
-        // THEN: mNotificationStackScrollLayout should be important for A11y
-        verify(mNotificationStackScrollLayout)
-                .setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
-    }
-
-    @Test
-    @DisableFlags(FooterViewRefactor.FLAG_NAME)
-    public void updateImportantForAccessibility_noChild_notOnKeyGuard_importantForA11y() {
-        // GIVEN: Controller is attached, active notifications is empty,
-        // and mNotificationStackScrollLayout.onKeyguard() is false
-        initController(/* viewIsAttached= */ true);
-        when(mNotificationStackScrollLayout.onKeyguard()).thenReturn(false);
-        mController.getNotifStackController().setNotifStats(NotifStats.getEmpty());
-
-        // THEN: mNotificationStackScrollLayout should be important for A11y
-        verify(mNotificationStackScrollLayout)
-                .setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
-    }
-
-    @Test
-    @DisableFlags(FooterViewRefactor.FLAG_NAME)
-    public void updateEmptyShadeView_onKeyguardTransitionToAod_hidesView() {
-        initController(/* viewIsAttached= */ true);
-        mController.onKeyguardTransitionChanged(
-                new TransitionStep(
-                        /* from= */ KeyguardState.GONE,
-                        /* to= */ KeyguardState.AOD));
-        verify(mNotificationStackScrollLayout).updateEmptyShadeView(eq(false), anyBoolean());
-    }
-
-    @Test
-    @DisableFlags(FooterViewRefactor.FLAG_NAME)
-    public void updateEmptyShadeView_onKeyguardOccludedTransitionToAod_hidesView() {
-        initController(/* viewIsAttached= */ true);
-        mController.onKeyguardTransitionChanged(
-                new TransitionStep(
-                        /* from= */ KeyguardState.OCCLUDED,
-                        /* to= */ KeyguardState.AOD));
-        verify(mNotificationStackScrollLayout).updateEmptyShadeView(eq(false), anyBoolean());
-    }
-
-    @Test
     @DisableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING)
     public void sensitiveNotificationProtectionControllerListenerNotRegistered() {
         initController(/* viewIsAttached= */ true);
@@ -996,24 +747,6 @@
         return argThat(new LogMatcher(category, type));
     }
 
-    private void setupShowEmptyShadeViewState(boolean toShow) {
-        if (toShow) {
-            mController.onKeyguardTransitionChanged(
-                    new TransitionStep(
-                            /* from= */ KeyguardState.LOCKSCREEN,
-                            /* to= */ KeyguardState.GONE));
-            mController.setQsFullScreen(false);
-            mController.getView().removeAllViews();
-        } else {
-            mController.onKeyguardTransitionChanged(
-                    new TransitionStep(
-                            /* from= */ KeyguardState.GONE,
-                            /* to= */ KeyguardState.AOD));
-            mController.setQsFullScreen(true);
-            mController.getView().addContainerView(mock(ExpandableNotificationRow.class));
-        }
-    }
-
     private void initController(boolean viewIsAttached) {
         when(mNotificationStackScrollLayout.isAttachedToWindow()).thenReturn(viewIsAttached);
         ViewTreeObserver viewTreeObserver = mock(ViewTreeObserver.class);
@@ -1033,16 +766,12 @@
                 mStatusBarService,
                 mNotificationRoundnessManager,
                 mTunerService,
-                mDeviceProvisionedController,
                 mDynamicPrivacyController,
                 mConfigurationController,
                 mSysuiStatusBarStateController,
                 mKeyguardMediaController,
                 mKeyguardBypassController,
                 mPowerInteractor,
-                mPrimaryBouncerInteractor,
-                mKeyguardTransitionRepo,
-                mZenModeController,
                 mNotificationLockscreenUserManager,
                 mMetricsLogger,
                 mColorUpdateLogger,
@@ -1051,14 +780,11 @@
                 new FalsingManagerFake(),
                 mNotificationSwipeHelperBuilder,
                 mGroupExpansionManager,
-                mSilentHeaderController,
                 mNotifPipeline,
                 mNotifCollection,
                 mLockscreenShadeTransitionController,
                 mUiEventLogger,
-                mRemoteInputManager,
                 mVisibilityLocationProviderDelegator,
-                mSeenNotificationsInteractor,
                 mViewBinder,
                 mShadeController,
                 mWindowRootView,
@@ -1076,7 +802,7 @@
     }
 
     static class LogMatcher implements ArgumentMatcher<LogMaker> {
-        private int mCategory, mType;
+        private final int mCategory, mType;
 
         LogMatcher(int category, int type) {
             mCategory = category;
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
index dcac294..39cff63 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
@@ -2,12 +2,10 @@
 
 import android.annotation.DimenRes
 import android.content.pm.PackageManager
-import android.platform.test.annotations.DisableFlags
 import android.platform.test.flag.junit.FlagsParameterization
 import android.widget.FrameLayout
 import androidx.test.filters.SmallTest
 import com.android.keyguard.BouncerPanelExpansionCalculator.aboutToShowBouncerProgress
-import com.android.systemui.Flags
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.animation.ShadeInterpolation.getContentAlpha
 import com.android.systemui.dump.DumpManager
@@ -740,20 +738,6 @@
         assertThat((footerView.viewState as FooterViewState).hideContent).isTrue()
     }
 
-    @DisableFlags(Flags.FLAG_NOTIFICATIONS_FOOTER_VIEW_REFACTOR)
-    @Test
-    fun resetViewStates_clearAllInProgress_allRowsRemoved_emptyShade_footerHidden() {
-        ambientState.isClearAllInProgress = true
-        ambientState.isShadeExpanded = true
-        ambientState.stackEndHeight = maxPanelHeight // plenty space for the footer in the stack
-        hostView.removeAllViews() // remove all rows
-        hostView.addView(footerView)
-
-        stackScrollAlgorithm.resetViewStates(ambientState, 0)
-
-        assertThat((footerView.viewState as FooterViewState).hideContent).isTrue()
-    }
-
     @Test
     fun getGapForLocation_onLockscreen_returnsSmallGap() {
         val gap =
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 e592e4b..1b4f9a7 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
@@ -18,7 +18,6 @@
 
 package com.android.systemui.statusbar.notification.stack.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
@@ -41,7 +40,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.emptyshade.shared.ModesEmptyShadeFix
-import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
 import com.android.systemui.statusbar.notification.headsup.PinnedStatus
 import com.android.systemui.statusbar.notification.stack.data.repository.headsUpNotificationRepository
 import com.android.systemui.statusbar.policy.data.repository.fakeUserSetupRepository
@@ -63,7 +61,6 @@
 
 @SmallTest
 @RunWith(ParameterizedAndroidJunit4::class)
-@EnableFlags(FooterViewRefactor.FLAG_NAME)
 class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCase() {
     private val kosmos =
         testKosmos().apply {
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 45977886..a045b37 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
@@ -70,6 +70,7 @@
 import com.android.systemui.statusbar.notification.stack.domain.interactor.sharedNotificationContainerInteractor
 import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel.HorizontalPosition
 import com.android.systemui.testKosmos
+import com.android.systemui.window.ui.viewmodel.fakeBouncerTransitions
 import com.google.common.collect.Range
 import com.google.common.truth.Truth.assertThat
 import kotlin.test.assertIs
@@ -1395,6 +1396,19 @@
             assertThat(stackAbsoluteBottom).isEqualTo(100F)
         }
 
+    @Test
+    fun blurRadius_emitsValues_fromPrimaryBouncerTransitions() =
+        testScope.runTest {
+            val blurRadius by collectLastValue(underTest.blurRadius)
+            assertThat(blurRadius).isEqualTo(0.0f)
+
+            kosmos.fakeBouncerTransitions.first().notificationBlurRadius.value = 30.0f
+            assertThat(blurRadius).isEqualTo(30.0f)
+
+            kosmos.fakeBouncerTransitions.last().notificationBlurRadius.value = 40.0f
+            assertThat(blurRadius).isEqualTo(40.0f)
+        }
+
     private suspend fun TestScope.showLockscreen() {
         shadeTestUtil.setQsExpansion(0f)
         shadeTestUtil.setLockscreenShadeExpansion(0f)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
index 7f139bd..f318c74 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
@@ -65,6 +65,7 @@
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
+import com.android.systemui.wallet.controller.QuickAccessWalletController;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -111,6 +112,7 @@
     @Mock private EmergencyGestureIntentFactory mEmergencyGestureIntentFactory;
     @Mock private KeyguardInteractor mKeyguardInteractor;
     @Mock private QSPanelController mQSPanelController;
+    @Mock private QuickAccessWalletController mQuickAccessWalletController;
 
     CentralSurfacesCommandQueueCallbacks mSbcqCallbacks;
 
@@ -149,7 +151,8 @@
                 mQSHost,
                 mActivityStarter,
                 mKeyguardInteractor,
-                mEmergencyGestureIntentFactory);
+                mEmergencyGestureIntentFactory,
+                mQuickAccessWalletController);
 
         when(mUserTracker.getUserHandle()).thenReturn(
                 UserHandle.of(ActivityManager.getCurrentUser()));
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.kt
index f4c2545..216f51d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/HeadsUpAppearanceControllerTest.kt
@@ -38,7 +38,7 @@
 import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationIconInteractor
 import com.android.systemui.statusbar.notification.domain.interactor.headsUpNotificationIconInteractor
 import com.android.systemui.statusbar.notification.headsup.PinnedStatus
-import com.android.systemui.statusbar.notification.headsup.headsUpManager
+import com.android.systemui.statusbar.notification.headsup.mockHeadsUpManager
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
 import com.android.systemui.statusbar.notification.row.NotificationTestHelper
 import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation
@@ -75,7 +75,7 @@
     private val keyguardStateController = kosmos.keyguardStateController
     private val commandQueue = kosmos.commandQueue
     private val notificationRoundnessManager = mock<NotificationRoundnessManager>()
-    private var headsUpManager = kosmos.headsUpManager
+    private var headsUpManager = kosmos.mockHeadsUpManager
 
     private lateinit var testHelper: NotificationTestHelper
     private lateinit var row: ExpandableNotificationRow
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.kt
index b815c6c..ffd349d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.kt
@@ -53,6 +53,7 @@
 import com.android.systemui.statusbar.StatusBarState
 import com.android.systemui.statusbar.data.repository.StatusBarContentInsetsProviderStore
 import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler
+import com.android.systemui.statusbar.layout.statusBarContentInsetsProvider
 import com.android.systemui.statusbar.phone.ui.StatusBarIconController
 import com.android.systemui.statusbar.phone.ui.TintedIconManager
 import com.android.systemui.statusbar.policy.BatteryController
@@ -560,7 +561,6 @@
         updateStateToKeyguard()
 
         controller.setDozing(true)
-        controller.updateViewState()
 
         Truth.assertThat(keyguardStatusBarView.visibility).isEqualTo(View.INVISIBLE)
     }
@@ -573,7 +573,6 @@
         updateStateToKeyguard()
 
         controller.setDozing(false)
-        controller.updateViewState()
 
         Truth.assertThat(keyguardStatusBarView.visibility).isEqualTo(View.VISIBLE)
     }
@@ -633,7 +632,6 @@
         Truth.assertThat(keyguardStatusBarView.visibility).isEqualTo(View.VISIBLE)
 
         controller.setDozing(true)
-        controller.updateViewState()
 
         // setDozing(true) should typically cause the view to hide. But since the flag is on, we
         // should ignore these set dozing calls and stay the same visibility.
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LetterboxBackgroundProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LetterboxBackgroundProviderTest.kt
index 788c2cb2..7786689 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LetterboxBackgroundProviderTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LetterboxBackgroundProviderTest.kt
@@ -25,6 +25,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.layout.LetterboxBackgroundProvider
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.mockito.eq
 import com.android.systemui.util.time.FakeSystemClock
@@ -134,6 +135,7 @@
     fun isLetterboxBackgroundMultiColored_defaultValue_returnsFalse() {
         assertThat(provider.isLetterboxBackgroundMultiColored).isEqualTo(false)
     }
+
     @Test
     fun isLetterboxBackgroundMultiColored_afterOnStart_executorNotDone_returnsDefaultValue() {
         whenever(windowManager.isLetterboxBackgroundMultiColored).thenReturn(true)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java
index 9099334..a65ccad 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/LightBarControllerTest.java
@@ -51,6 +51,7 @@
 import com.android.systemui.statusbar.data.model.StatusBarAppearance;
 import com.android.systemui.statusbar.data.model.StatusBarMode;
 import com.android.systemui.statusbar.data.repository.FakeStatusBarModePerDisplayRepository;
+import com.android.systemui.statusbar.layout.BoundsPair;
 import com.android.systemui.statusbar.policy.BatteryController;
 
 import kotlinx.coroutines.test.TestScope;
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/MultiDisplayAutoHideControllerStoreTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/MultiDisplayAutoHideControllerStoreTest.kt
index 90506a1..d163726 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/MultiDisplayAutoHideControllerStoreTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/MultiDisplayAutoHideControllerStoreTest.kt
@@ -56,7 +56,7 @@
     @Test
     fun beforeDisplayRemoved_doesNotStopInstances() =
         testScope.runTest {
-            val instance = underTest.forDisplay(DEFAULT_DISPLAY)
+            val instance = underTest.forDisplay(DEFAULT_DISPLAY)!!
 
             verify(instance, never()).stop()
         }
@@ -64,7 +64,7 @@
     @Test
     fun displayRemoved_stopsInstance() =
         testScope.runTest {
-            val instance = underTest.forDisplay(DEFAULT_DISPLAY)
+            val instance = underTest.forDisplay(DEFAULT_DISPLAY)!!
 
             fakeDisplayRepository.removeDisplay(DEFAULT_DISPLAY)
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index d174484..2e12336 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -40,7 +40,6 @@
 
 import android.platform.test.annotations.DisableFlags;
 import android.platform.test.annotations.EnableFlags;
-import android.platform.test.annotations.RequiresFlagsEnabled;
 import android.platform.test.flag.junit.CheckFlagsRule;
 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.testing.TestableLooper;
@@ -610,7 +609,6 @@
     }
 
     @Test
-    @RequiresFlagsEnabled(com.android.systemui.Flags.FLAG_PREDICTIVE_BACK_ANIMATE_BOUNCER)
     public void testPredictiveBackCallback_registration() {
         /* verify that a predictive back callback is registered when the bouncer becomes visible */
         mBouncerExpansionCallback.onVisibilityChanged(true);
@@ -625,7 +623,6 @@
     }
 
     @Test
-    @RequiresFlagsEnabled(com.android.systemui.Flags.FLAG_PREDICTIVE_BACK_ANIMATE_BOUNCER)
     public void testPredictiveBackCallback_invocationHidesBouncer() {
         mBouncerExpansionCallback.onVisibilityChanged(true);
         /* capture the predictive back callback during registration */
@@ -643,7 +640,6 @@
     }
 
     @Test
-    @RequiresFlagsEnabled(com.android.systemui.Flags.FLAG_PREDICTIVE_BACK_ANIMATE_BOUNCER)
     public void testPredictiveBackCallback_noBackAnimationForFullScreenBouncer() {
         when(mKeyguardSecurityModel.getSecurityMode(anyInt()))
                 .thenReturn(KeyguardSecurityModel.SecurityMode.SimPin);
@@ -663,7 +659,6 @@
     }
 
     @Test
-    @RequiresFlagsEnabled(com.android.systemui.Flags.FLAG_PREDICTIVE_BACK_ANIMATE_BOUNCER)
     public void testPredictiveBackCallback_forwardsBackDispatches() {
         mBouncerExpansionCallback.onVisibilityChanged(true);
         /* capture the predictive back callback during registration */
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java
index 0652a83..650fa7c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java
@@ -31,7 +31,6 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.res.Configuration;
-import android.platform.test.annotations.RequiresFlagsEnabled;
 import android.platform.test.flag.junit.CheckFlagsRule;
 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
 import android.platform.test.ravenwood.RavenwoodRule;
@@ -41,7 +40,6 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.Dependency;
-import com.android.systemui.Flags;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.animation.DialogTransitionAnimator;
 import com.android.systemui.animation.back.BackAnimationSpec;
@@ -137,7 +135,6 @@
     }
 
     @Test
-    @RequiresFlagsEnabled(Flags.FLAG_PREDICTIVE_BACK_ANIMATE_DIALOGS)
     public void usePredictiveBackAnimFlag() {
         final SystemUIDialog dialog = new SystemUIDialog(mContext);
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/domain/interactor/LightsOutInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/domain/interactor/LightsOutInteractorTest.kt
index 2d9880a..659d91a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/domain/interactor/LightsOutInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/domain/interactor/LightsOutInteractorTest.kt
@@ -39,7 +39,7 @@
     fun isLowProfile_lightsOutStatusBarMode_false() = runTest {
         statusBarModeRepository.defaultDisplay.statusBarMode.value = StatusBarMode.LIGHTS_OUT
 
-        val actual by collectLastValue(interactor.isLowProfile(DISPLAY_ID))
+        val actual by collectLastValue(interactor.isLowProfile(DISPLAY_ID)!!)
 
         assertThat(actual).isTrue()
     }
@@ -49,7 +49,7 @@
         statusBarModeRepository.defaultDisplay.statusBarMode.value =
             StatusBarMode.LIGHTS_OUT_TRANSPARENT
 
-        val actual by collectLastValue(interactor.isLowProfile(DISPLAY_ID))
+        val actual by collectLastValue(interactor.isLowProfile(DISPLAY_ID)!!)
 
         assertThat(actual).isTrue()
     }
@@ -58,7 +58,7 @@
     fun isLowProfile_transparentStatusBarMode_false() = runTest {
         statusBarModeRepository.defaultDisplay.statusBarMode.value = StatusBarMode.TRANSPARENT
 
-        val actual by collectLastValue(interactor.isLowProfile(DISPLAY_ID))
+        val actual by collectLastValue(interactor.isLowProfile(DISPLAY_ID)!!)
 
         assertThat(actual).isFalse()
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/ethernet/domain/EthernetInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/ethernet/domain/EthernetInteractorTest.kt
index f9ae5ff..e7bfa0d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/ethernet/domain/EthernetInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/ethernet/domain/EthernetInteractorTest.kt
@@ -19,39 +19,40 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
 import com.android.settingslib.AccessibilityContentDescriptions
-import com.android.systemui.res.R
 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.coroutines.collectLastValue
-import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.pipeline.shared.data.repository.connectivityRepository
+import com.android.systemui.statusbar.pipeline.shared.data.repository.fake
+import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.runTest
 import org.junit.Test
 import org.junit.runner.RunWith
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class EthernetInteractorTest : SysuiTestCase() {
-    private val connectivityRepository = FakeConnectivityRepository()
-    private val underTest = EthernetInteractor(connectivityRepository)
-
-    private val testScope = TestScope()
+    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
+    private val Kosmos.underTest by Kosmos.Fixture { ethernetInteractor }
 
     @Test
     fun icon_default_validated() =
-        testScope.runTest {
+        kosmos.runTest {
             val latest by collectLastValue(underTest.icon)
 
-            connectivityRepository.setEthernetConnected(default = true, validated = true)
+            connectivityRepository.fake.setEthernetConnected(default = true, validated = true)
 
             val expected =
                 Icon.Resource(
                     R.drawable.stat_sys_ethernet_fully,
                     ContentDescription.Resource(
                         AccessibilityContentDescriptions.ETHERNET_CONNECTION_VALUES[1]
-                    )
+                    ),
                 )
 
             assertThat(latest).isEqualTo(expected)
@@ -59,17 +60,17 @@
 
     @Test
     fun icon_default_notValidated() =
-        testScope.runTest {
+        kosmos.runTest {
             val latest by collectLastValue(underTest.icon)
 
-            connectivityRepository.setEthernetConnected(default = true, validated = false)
+            connectivityRepository.fake.setEthernetConnected(default = true, validated = false)
 
             val expected =
                 Icon.Resource(
                     R.drawable.stat_sys_ethernet,
                     ContentDescription.Resource(
                         AccessibilityContentDescriptions.ETHERNET_CONNECTION_VALUES[0]
-                    )
+                    ),
                 )
 
             assertThat(latest).isEqualTo(expected)
@@ -77,20 +78,20 @@
 
     @Test
     fun icon_notDefault_validated() =
-        testScope.runTest {
+        kosmos.runTest {
             val latest by collectLastValue(underTest.icon)
 
-            connectivityRepository.setEthernetConnected(default = false, validated = true)
+            connectivityRepository.fake.setEthernetConnected(default = false, validated = true)
 
             assertThat(latest).isNull()
         }
 
     @Test
     fun icon_notDefault_notValidated() =
-        testScope.runTest {
+        kosmos.runTest {
             val latest by collectLastValue(underTest.icon)
 
-            connectivityRepository.setEthernetConnected(default = false, validated = false)
+            connectivityRepository.fake.setEthernetConnected(default = false, validated = false)
 
             assertThat(latest).isNull()
         }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt
index a9db0b7..faf736a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/FakeHomeStatusBarViewModel.kt
@@ -31,7 +31,7 @@
 class FakeHomeStatusBarViewModel(
     override val operatorNameViewModel: StatusBarOperatorNameViewModel
 ) : HomeStatusBarViewModel {
-    private val areNotificationLightsOut = MutableStateFlow(false)
+    override val areNotificationsLightsOut = MutableStateFlow(false)
 
     override val isTransitioningFromLockscreenToOccluded = MutableStateFlow(false)
 
@@ -77,14 +77,12 @@
 
     override val iconBlockList: MutableStateFlow<List<String>> = MutableStateFlow(listOf())
 
-    override fun areNotificationsLightsOut(displayId: Int): Flow<Boolean> = areNotificationLightsOut
-
     val darkRegions = mutableListOf<Rect>()
 
     var darkIconTint = Color.BLACK
     var lightIconTint = Color.WHITE
 
-    override fun areaTint(displayId: Int): Flow<StatusBarTintColor> =
+    override val areaTint: Flow<StatusBarTintColor> =
         MutableStateFlow(
             StatusBarTintColor { viewBounds ->
                 if (DarkIconDispatcher.isInAreas(darkRegions, viewBounds)) {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt
index e91875c..a70b777 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelImplTest.kt
@@ -22,6 +22,7 @@
 import android.app.StatusBarManager.DISABLE_NONE
 import android.app.StatusBarManager.DISABLE_NOTIFICATION_ICONS
 import android.app.StatusBarManager.DISABLE_SYSTEM_INFO
+import android.content.testableContext
 import android.graphics.Rect
 import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
@@ -59,7 +60,6 @@
 import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModelTest.Companion.assertIsScreenRecordChip
 import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModelTest.Companion.assertIsShareToAppChip
 import com.android.systemui.statusbar.data.model.StatusBarMode
-import com.android.systemui.statusbar.data.repository.FakeStatusBarModeRepository.Companion.DISPLAY_ID
 import com.android.systemui.statusbar.data.repository.fakeStatusBarModeRepository
 import com.android.systemui.statusbar.disableflags.data.repository.fakeDisableFlagsRepository
 import com.android.systemui.statusbar.disableflags.shared.model.DisableFlagsModel
@@ -363,7 +363,7 @@
             activeNotificationListRepository.activeNotifications.value =
                 activeNotificationsStore(testNotifications)
 
-            val actual by collectLastValue(underTest.areNotificationsLightsOut(DISPLAY_ID))
+            val actual by collectLastValue(underTest.areNotificationsLightsOut)
 
             assertThat(actual).isTrue()
         }
@@ -377,7 +377,7 @@
             activeNotificationListRepository.activeNotifications.value =
                 activeNotificationsStore(emptyList())
 
-            val actual by collectLastValue(underTest.areNotificationsLightsOut(DISPLAY_ID))
+            val actual by collectLastValue(underTest.areNotificationsLightsOut)
 
             assertThat(actual).isFalse()
         }
@@ -391,7 +391,7 @@
             activeNotificationListRepository.activeNotifications.value =
                 activeNotificationsStore(emptyList())
 
-            val actual by collectLastValue(underTest.areNotificationsLightsOut(DISPLAY_ID))
+            val actual by collectLastValue(underTest.areNotificationsLightsOut)
 
             assertThat(actual).isFalse()
         }
@@ -405,7 +405,7 @@
             activeNotificationListRepository.activeNotifications.value =
                 activeNotificationsStore(testNotifications)
 
-            val actual by collectLastValue(underTest.areNotificationsLightsOut(DISPLAY_ID))
+            val actual by collectLastValue(underTest.areNotificationsLightsOut)
 
             assertThat(actual).isFalse()
         }
@@ -415,7 +415,7 @@
     fun areNotificationsLightsOut_requiresFlagEnabled() =
         kosmos.runTest {
             assertLogsWtf {
-                val flow = underTest.areNotificationsLightsOut(DISPLAY_ID)
+                val flow = underTest.areNotificationsLightsOut
                 assertThat(flow).isEqualTo(emptyFlow<Boolean>())
             }
         }
@@ -1005,11 +1005,11 @@
     @Test
     fun areaTint_viewIsInDarkBounds_getsDarkTint() =
         kosmos.runTest {
-            val displayId = 321
+            val displayId = testableContext.displayId
             fakeDarkIconRepository.darkState(displayId).value =
                 SysuiDarkIconDispatcher.DarkChange(listOf(Rect(0, 0, 5, 5)), 0f, 0xAABBCC)
 
-            val areaTint by collectLastValue(underTest.areaTint(displayId))
+            val areaTint by collectLastValue(underTest.areaTint)
 
             val tint = areaTint?.tint(Rect(1, 1, 3, 3))
 
@@ -1019,11 +1019,11 @@
     @Test
     fun areaTint_viewIsNotInDarkBounds_getsDefaultTint() =
         kosmos.runTest {
-            val displayId = 321
+            val displayId = testableContext.displayId
             fakeDarkIconRepository.darkState(displayId).value =
                 SysuiDarkIconDispatcher.DarkChange(listOf(Rect(0, 0, 5, 5)), 0f, 0xAABBCC)
 
-            val areaTint by collectLastValue(underTest.areaTint(displayId))
+            val areaTint by collectLastValue(underTest.areaTint)
 
             val tint = areaTint?.tint(Rect(6, 6, 7, 7))
 
@@ -1033,11 +1033,11 @@
     @Test
     fun areaTint_viewIsInDarkBounds_darkBoundsChange_viewUpdates() =
         kosmos.runTest {
-            val displayId = 321
+            val displayId = testableContext.displayId
             fakeDarkIconRepository.darkState(displayId).value =
                 SysuiDarkIconDispatcher.DarkChange(listOf(Rect(0, 0, 5, 5)), 0f, 0xAABBCC)
 
-            val areaTint by collectLastValue(underTest.areaTint(displayId))
+            val areaTint by collectLastValue(underTest.areaTint)
 
             var tint = areaTint?.tint(Rect(1, 1, 3, 3))
 
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 3d6882c..c6bae19 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractorTest.kt
@@ -18,9 +18,8 @@
 
 import android.app.AutomaticZenRule
 import android.app.Flags
-import android.app.NotificationManager.INTERRUPTION_FILTER_NONE
-import android.app.NotificationManager.INTERRUPTION_FILTER_PRIORITY
 import android.app.NotificationManager.Policy
+import android.media.AudioManager
 import android.platform.test.annotations.EnableFlags
 import android.provider.Settings
 import android.provider.Settings.Secure.ZEN_DURATION
@@ -34,6 +33,7 @@
 import com.android.internal.R
 import com.android.settingslib.notification.data.repository.updateNotificationPolicy
 import com.android.settingslib.notification.modes.TestModeBuilder
+import com.android.settingslib.volume.shared.model.AudioStream
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.kosmos.testScope
@@ -402,47 +402,12 @@
 
     @Test
     @EnableFlags(Flags.FLAG_MODES_UI)
-    fun activeModesBlockingEverything_hasModesWithFilterNone() =
-        testScope.runTest {
-            val blockingEverything by collectLastValue(underTest.activeModesBlockingEverything)
-
-            zenModeRepository.addModes(
-                listOf(
-                    TestModeBuilder()
-                        .setName("Filter=None, Not active")
-                        .setInterruptionFilter(INTERRUPTION_FILTER_NONE)
-                        .setActive(false)
-                        .build(),
-                    TestModeBuilder()
-                        .setName("Filter=Priority, Active")
-                        .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
-                        .setActive(true)
-                        .build(),
-                    TestModeBuilder()
-                        .setName("Filter=None, Active")
-                        .setInterruptionFilter(INTERRUPTION_FILTER_NONE)
-                        .setActive(true)
-                        .build(),
-                    TestModeBuilder()
-                        .setName("Filter=None, Active Too")
-                        .setInterruptionFilter(INTERRUPTION_FILTER_NONE)
-                        .setActive(true)
-                        .build(),
-                )
-            )
-            runCurrent()
-
-            assertThat(blockingEverything!!.mainMode!!.name).isEqualTo("Filter=None, Active")
-            assertThat(blockingEverything!!.modeNames)
-                .containsExactly("Filter=None, Active", "Filter=None, Active Too")
-                .inOrder()
-        }
-
-    @Test
-    @EnableFlags(Flags.FLAG_MODES_UI)
     fun activeModesBlockingMedia_hasModesWithPolicyBlockingMedia() =
         testScope.runTest {
-            val blockingMedia by collectLastValue(underTest.activeModesBlockingMedia)
+            val blockingMedia by
+                collectLastValue(
+                    underTest.activeModesBlockingStream(AudioStream(AudioManager.STREAM_MUSIC))
+                )
 
             zenModeRepository.addModes(
                 listOf(
@@ -480,7 +445,10 @@
     @EnableFlags(Flags.FLAG_MODES_UI)
     fun activeModesBlockingAlarms_hasModesWithPolicyBlockingAlarms() =
         testScope.runTest {
-            val blockingAlarms by collectLastValue(underTest.activeModesBlockingAlarms)
+            val blockingAlarms by
+                collectLastValue(
+                    underTest.activeModesBlockingStream(AudioStream(AudioManager.STREAM_ALARM))
+                )
 
             zenModeRepository.addModes(
                 listOf(
@@ -515,6 +483,47 @@
         }
 
     @Test
+    @EnableFlags(Flags.FLAG_MODES_UI)
+    fun activeModesBlockingAlarms_hasModesWithPolicyBlockingSystem() =
+        testScope.runTest {
+            val blockingSystem by
+                collectLastValue(
+                    underTest.activeModesBlockingStream(AudioStream(AudioManager.STREAM_SYSTEM))
+                )
+
+            zenModeRepository.addModes(
+                listOf(
+                    TestModeBuilder()
+                        .setName("Blocks system, Not active")
+                        .setZenPolicy(ZenPolicy.Builder().allowSystem(false).build())
+                        .setActive(false)
+                        .build(),
+                    TestModeBuilder()
+                        .setName("Allows system, Active")
+                        .setZenPolicy(ZenPolicy.Builder().allowSystem(true).build())
+                        .setActive(true)
+                        .build(),
+                    TestModeBuilder()
+                        .setName("Blocks system, Active")
+                        .setZenPolicy(ZenPolicy.Builder().allowSystem(false).build())
+                        .setActive(true)
+                        .build(),
+                    TestModeBuilder()
+                        .setName("Blocks system, Active Too")
+                        .setZenPolicy(ZenPolicy.Builder().allowSystem(false).build())
+                        .setActive(true)
+                        .build(),
+                )
+            )
+            runCurrent()
+
+            assertThat(blockingSystem!!.mainMode!!.name).isEqualTo("Blocks system, Active")
+            assertThat(blockingSystem!!.modeNames)
+                .containsExactly("Blocks system, Active", "Blocks system, Active Too")
+                .inOrder()
+        }
+
+    @Test
     @EnableFlags(ModesEmptyShadeFix.FLAG_NAME, Flags.FLAG_MODES_UI, Flags.FLAG_MODES_API)
     fun modesHidingNotifications_onlyIncludesModesWithNotifListSuppression() =
         testScope.runTest {
diff --git a/packages/SystemUI/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 e686ede..07d088b 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
@@ -80,7 +80,7 @@
                 kosmos.mockModesDialogEventLogger,
             )
 
-        timeScheduleInfo = ZenModeConfig.ScheduleInfo()
+        timeScheduleInfo = ScheduleInfo()
         timeScheduleInfo.days = intArrayOf(Calendar.MONDAY, Calendar.TUESDAY, Calendar.WEDNESDAY)
         timeScheduleInfo.startHour = 11
         timeScheduleInfo.endHour = 15
@@ -126,7 +126,7 @@
             )
             runCurrent()
 
-            assertThat(tiles?.size).isEqualTo(3)
+            assertThat(tiles).hasSize(3)
             with(tiles?.elementAt(0)!!) {
                 assertThat(this.text).isEqualTo("Disabled by other")
                 assertThat(this.subtext).isEqualTo("Not set")
@@ -176,7 +176,7 @@
             )
             runCurrent()
 
-            assertThat(tiles?.size).isEqualTo(3)
+            assertThat(tiles).hasSize(3)
             with(tiles?.elementAt(0)!!) {
                 assertThat(this.text).isEqualTo("Active without manual")
                 assertThat(this.subtext).isEqualTo("On")
@@ -226,7 +226,7 @@
             )
             runCurrent()
 
-            assertThat(tiles?.size).isEqualTo(3)
+            assertThat(tiles).hasSize(3)
 
             // Check that tile is initially present
             with(tiles?.elementAt(0)!!) {
@@ -239,7 +239,7 @@
                 runCurrent()
             }
             // Check that tile is still present at the same location, but turned off
-            assertThat(tiles?.size).isEqualTo(3)
+            assertThat(tiles).hasSize(3)
             with(tiles?.elementAt(0)!!) {
                 assertThat(this.text).isEqualTo("Active without manual")
                 assertThat(this.subtext).isEqualTo("Manage in settings")
@@ -252,7 +252,7 @@
             runCurrent()
 
             // Check that tile is now gone
-            assertThat(tiles2?.size).isEqualTo(2)
+            assertThat(tiles2).hasSize(2)
             assertThat(tiles2?.elementAt(0)!!.text).isEqualTo("Active with manual")
             assertThat(tiles2?.elementAt(1)!!.text).isEqualTo("Inactive with manual")
         }
@@ -287,22 +287,22 @@
             )
             runCurrent()
 
-            assertThat(tiles?.size).isEqualTo(3)
+            assertThat(tiles).hasSize(3)
 
             repository.removeMode("A")
             runCurrent()
 
-            assertThat(tiles?.size).isEqualTo(2)
+            assertThat(tiles).hasSize(2)
 
             repository.removeMode("B")
             runCurrent()
 
-            assertThat(tiles?.size).isEqualTo(1)
+            assertThat(tiles).hasSize(1)
 
             repository.removeMode("C")
             runCurrent()
 
-            assertThat(tiles?.size).isEqualTo(0)
+            assertThat(tiles).hasSize(0)
         }
 
     @Test
@@ -439,8 +439,11 @@
             with(tiles?.elementAt(6)!!) {
                 assertThat(this.stateDescription).isEqualTo("Off")
                 assertThat(this.subtextDescription)
-                    .isEqualTo(SystemZenRules.getDaysOfWeekFull(context, timeScheduleInfo)
-                    + ", " + SystemZenRules.getTimeSummary(context, timeScheduleInfo))
+                    .isEqualTo(
+                        SystemZenRules.getDaysOfWeekFull(context, timeScheduleInfo) +
+                            ", " +
+                            SystemZenRules.getTimeSummary(context, timeScheduleInfo)
+                    )
             }
 
             // All tiles have the same long click info
@@ -464,7 +467,7 @@
             )
             runCurrent()
 
-            assertThat(tiles?.size).isEqualTo(1)
+            assertThat(tiles).hasSize(1)
             assertThat(tiles?.elementAt(0)?.enabled).isFalse()
 
             // Trigger onClick
@@ -497,13 +500,13 @@
             )
             runCurrent()
 
-            assertThat(tiles?.size).isEqualTo(1)
+            assertThat(tiles).hasSize(1)
 
             // Click tile to toggle it off
             tiles?.elementAt(0)!!.onClick()
             runCurrent()
 
-            assertThat(tiles?.size).isEqualTo(1)
+            assertThat(tiles).hasSize(1)
             with(tiles?.elementAt(0)!!) {
                 assertThat(this.text).isEqualTo("Active without manual")
                 assertThat(this.subtext).isEqualTo("Manage in settings")
@@ -538,7 +541,7 @@
             )
             runCurrent()
 
-            assertThat(tiles?.size).isEqualTo(1)
+            assertThat(tiles).hasSize(1)
             with(tiles?.elementAt(0)!!) {
                 assertThat(this.text).isEqualTo("Disabled by other")
                 assertThat(this.subtext).isEqualTo("Not set")
@@ -590,7 +593,7 @@
             )
             runCurrent()
 
-            assertThat(tiles?.size).isEqualTo(2)
+            assertThat(tiles).hasSize(2)
 
             // Trigger onLongClick for A
             tiles?.first()?.onLongClick?.let { it() }
@@ -641,7 +644,7 @@
             )
             runCurrent()
 
-            assertThat(tiles?.size).isEqualTo(3)
+            assertThat(tiles).hasSize(3)
 
             // Trigger onClick for each tile in sequence
             tiles?.forEach { it.onClick.invoke() }
@@ -682,7 +685,7 @@
             )
             runCurrent()
 
-            assertThat(tiles?.size).isEqualTo(2)
+            assertThat(tiles).hasSize(2)
             val modeCaptor = argumentCaptor<ZenMode>()
 
             // long click manual DND and then automatic mode
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/window/MultiDisplayStatusBarWindowControllerStoreTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/window/MultiDisplayStatusBarWindowControllerStoreTest.kt
index 7a9d017..769f012 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/window/MultiDisplayStatusBarWindowControllerStoreTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/window/MultiDisplayStatusBarWindowControllerStoreTest.kt
@@ -53,7 +53,7 @@
     @Test
     fun beforeDisplayRemoved_doesNotStopInstances() =
         testScope.runTest {
-            val instance = underTest.forDisplay(DEFAULT_DISPLAY)
+            val instance = underTest.forDisplay(DEFAULT_DISPLAY)!!
 
             verify(instance, never()).stop()
         }
@@ -61,7 +61,7 @@
     @Test
     fun displayRemoved_stopsInstance() =
         testScope.runTest {
-            val instance = underTest.forDisplay(DEFAULT_DISPLAY)
+            val instance = underTest.forDisplay(DEFAULT_DISPLAY)!!
 
             fakeDisplayRepository.removeDisplay(DEFAULT_DISPLAY)
 
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStatePerDisplayRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStatePerDisplayRepositoryTest.kt
index c7c7fdc..42ebaf7 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStatePerDisplayRepositoryTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStatePerDisplayRepositoryTest.kt
@@ -28,7 +28,7 @@
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.statusbar.CommandQueue
 import com.android.systemui.statusbar.commandQueue
-import com.android.systemui.statusbar.window.data.model.StatusBarWindowState
+import com.android.systemui.statusbar.window.shared.model.StatusBarWindowState
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStateRepositoryStoreTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStateRepositoryStoreTest.kt
index e23e88c..e36178c 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStateRepositoryStoreTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStateRepositoryStoreTest.kt
@@ -27,7 +27,7 @@
 import com.android.systemui.settings.displayTracker
 import com.android.systemui.statusbar.CommandQueue
 import com.android.systemui.statusbar.commandQueue
-import com.android.systemui.statusbar.window.data.model.StatusBarWindowState
+import com.android.systemui.statusbar.window.shared.model.StatusBarWindowState
 import com.android.systemui.testKosmos
 import com.google.common.truth.Truth.assertThat
 import kotlin.test.Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/StateTransitionsTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/StateTransitionsTest.kt
deleted file mode 100644
index 2ad1124..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/StateTransitionsTest.kt
+++ /dev/null
@@ -1,111 +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
-
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState
-import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.Error
-import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.Finished
-import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.InProgress
-import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.InProgressAfterError
-import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.NotStarted
-import com.android.systemui.touchpad.tutorial.ui.composable.toGestureUiState
-import com.android.systemui.touchpad.tutorial.ui.composable.toTutorialActionState
-import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState
-import com.google.common.truth.Truth.assertThat
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@SmallTest
-@RunWith(AndroidJUnit4::class)
-class StateTransitionsTest : SysuiTestCase() {
-
-    companion object {
-        private const val START_MARKER = "startMarker"
-        private const val END_MARKER = "endMarker"
-        private const val SUCCESS_ANIMATION = 0
-    }
-
-    // needed to simulate caching last state as it's used to create new state
-    private var lastState: TutorialActionState = NotStarted
-
-    private fun GestureState.toTutorialActionState(): TutorialActionState {
-        val newState =
-            this.toGestureUiState(
-                    progressStartMarker = START_MARKER,
-                    progressEndMarker = END_MARKER,
-                    successAnimation = SUCCESS_ANIMATION,
-                )
-                .toTutorialActionState(lastState)
-        lastState = newState
-        return lastState
-    }
-
-    @Test
-    fun gestureStateProducesEquivalentTutorialActionStateInHappyPath() {
-        val happyPath =
-            listOf(
-                GestureState.NotStarted,
-                GestureState.InProgress(0f),
-                GestureState.InProgress(0.5f),
-                GestureState.InProgress(1f),
-                GestureState.Finished,
-            )
-
-        val resultingStates = mutableListOf<TutorialActionState>()
-        happyPath.forEach { resultingStates.add(it.toTutorialActionState()) }
-
-        assertThat(resultingStates)
-            .containsExactly(
-                NotStarted,
-                InProgress(0f, START_MARKER, END_MARKER),
-                InProgress(0.5f, START_MARKER, END_MARKER),
-                InProgress(1f, START_MARKER, END_MARKER),
-                Finished(SUCCESS_ANIMATION),
-            )
-            .inOrder()
-    }
-
-    @Test
-    fun gestureStateProducesEquivalentTutorialActionStateInErrorPath() {
-        val errorPath =
-            listOf(
-                GestureState.NotStarted,
-                GestureState.InProgress(0f),
-                GestureState.Error,
-                GestureState.InProgress(0.5f),
-                GestureState.InProgress(1f),
-                GestureState.Finished,
-            )
-
-        val resultingStates = mutableListOf<TutorialActionState>()
-        errorPath.forEach { resultingStates.add(it.toTutorialActionState()) }
-
-        assertThat(resultingStates)
-            .containsExactly(
-                NotStarted,
-                InProgress(0f, START_MARKER, END_MARKER),
-                Error,
-                InProgressAfterError(InProgress(0.5f, START_MARKER, END_MARKER)),
-                InProgressAfterError(InProgress(1f, START_MARKER, END_MARKER)),
-                Finished(SUCCESS_ANIMATION),
-            )
-            .inOrder()
-    }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/ThreeFingerGestureRecognizerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/ThreeFingerGestureRecognizerTest.kt
index 8972f3e..8b526bb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/ThreeFingerGestureRecognizerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/gesture/ThreeFingerGestureRecognizerTest.kt
@@ -30,12 +30,14 @@
 import com.android.systemui.touchpad.ui.gesture.FakeVelocityTracker
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
+import org.junit.Ignore
 import org.junit.Test
 import org.junit.runner.RunWith
 import platform.test.runner.parameterized.ParameterizedAndroidJunit4
 import platform.test.runner.parameterized.Parameters
 
 @SmallTest
+@Ignore("b/386412866")
 @RunWith(ParameterizedAndroidJunit4::class)
 class ThreeFingerGestureRecognizerTest(
     private val recognizer: GestureRecognizer,
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/BackGestureScreenViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/BackGestureScreenViewModelTest.kt
index 79c1f9f..d752046 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/BackGestureScreenViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/BackGestureScreenViewModelTest.kt
@@ -16,36 +16,46 @@
 
 package com.android.systemui.touchpad.tutorial.ui.viewmodel
 
+import android.content.res.mockResources
 import android.view.MotionEvent
 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.common.ui.domain.interactor.configurationInteractor
+import com.android.systemui.inputdevice.tutorial.inputDeviceTutorialLogger
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.Error
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.Finished
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.InProgress
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.collectLastValue
 import com.android.systemui.kosmos.runTest
 import com.android.systemui.kosmos.useUnconfinedTestDispatcher
 import com.android.systemui.res.R
 import com.android.systemui.testKosmos
-import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState
-import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState.Error
-import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState.Finished
-import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState.InProgress
 import com.android.systemui.touchpad.tutorial.ui.gesture.MultiFingerGesture.Companion.SWIPE_DISTANCE
 import com.android.systemui.touchpad.tutorial.ui.gesture.ThreeFingerGesture
+import com.android.systemui.touchpad.ui.gesture.touchpadGestureResources
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.kotlin.whenever
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class BackGestureScreenViewModelTest : SysuiTestCase() {
 
     private val kosmos = testKosmos()
+    private val resources = kosmos.mockResources
     private val fakeConfigRepository = kosmos.fakeConfigurationRepository
-    private val viewModel = BackGestureScreenViewModel(kosmos.configurationInteractor)
+    private val viewModel =
+        BackGestureScreenViewModel(
+            GestureRecognizerAdapter(
+                BackGestureRecognizerProvider(kosmos.touchpadGestureResources),
+                kosmos.inputDeviceTutorialLogger,
+            )
+        )
 
     @Before
     fun before() {
@@ -61,8 +71,8 @@
                 expected =
                     InProgress(
                         progress = 1f,
-                        progressStartMarker = "gesture to L",
-                        progressEndMarker = "end progress L",
+                        startMarker = "gesture to L",
+                        endMarker = "end progress L",
                     ),
             )
         }
@@ -75,8 +85,8 @@
                 expected =
                     InProgress(
                         progress = 1f,
-                        progressStartMarker = "gesture to R",
-                        progressEndMarker = "end progress R",
+                        startMarker = "gesture to R",
+                        endMarker = "end progress R",
                     ),
             )
         }
@@ -104,7 +114,7 @@
         kosmos.runTest {
             fun performBackGesture() =
                 ThreeFingerGesture.swipeLeft().forEach { viewModel.handleEvent(it) }
-            val state by collectLastValue(viewModel.gestureUiState)
+            val state by collectLastValue(viewModel.tutorialState)
             performBackGesture()
             assertThat(state).isInstanceOf(Finished::class.java)
 
@@ -115,22 +125,30 @@
         }
 
     private fun setThresholdResource(threshold: Float) {
-        fakeConfigRepository.setDimensionPixelSize(
-            R.dimen.touchpad_tutorial_gestures_distance_threshold,
-            (threshold).toInt(),
-        )
+        whenever(
+                resources.getDimensionPixelSize(
+                    R.dimen.touchpad_tutorial_gestures_distance_threshold
+                )
+            )
+            .thenReturn(threshold.toInt())
         fakeConfigRepository.onAnyConfigurationChange()
     }
 
-    private fun Kosmos.assertProgressWhileMovingFingers(deltaX: Float, expected: GestureUiState) {
+    private fun Kosmos.assertProgressWhileMovingFingers(
+        deltaX: Float,
+        expected: TutorialActionState,
+    ) {
         assertStateAfterEvents(
             events = ThreeFingerGesture.eventsForGestureInProgress { move(deltaX = deltaX) },
             expected = expected,
         )
     }
 
-    private fun Kosmos.assertStateAfterEvents(events: List<MotionEvent>, expected: GestureUiState) {
-        val state by collectLastValue(viewModel.gestureUiState)
+    private fun Kosmos.assertStateAfterEvents(
+        events: List<MotionEvent>,
+        expected: TutorialActionState,
+    ) {
+        val state by collectLastValue(viewModel.tutorialState)
         events.forEach { viewModel.handleEvent(it) }
         assertThat(state).isEqualTo(expected)
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/EasterEggGestureViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/EasterEggGestureViewModelTest.kt
index 4af3742..8bd796b 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/EasterEggGestureViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/EasterEggGestureViewModelTest.kt
@@ -20,6 +20,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.kosmos.Kosmos
 import com.android.systemui.kosmos.collectLastValue
 import com.android.systemui.kosmos.runTest
@@ -36,7 +37,13 @@
 class EasterEggGestureViewModelTest : SysuiTestCase() {
 
     private val kosmos = testKosmos()
-    private val viewModel = EasterEggGestureViewModel()
+    private val viewModel =
+        EasterEggGestureViewModel(
+            GestureRecognizerAdapter(
+                EasterEggRecognizerProvider(),
+                kosmos.inputDeviceTutorialLogger,
+            )
+        )
 
     @Before
     fun before() {
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/HomeGestureScreenViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/HomeGestureScreenViewModelTest.kt
index 4dfd01a..7862fd3 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/HomeGestureScreenViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/HomeGestureScreenViewModelTest.kt
@@ -22,21 +22,22 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
-import com.android.systemui.common.ui.domain.interactor.configurationInteractor
+import com.android.systemui.inputdevice.tutorial.inputDeviceTutorialLogger
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.Error
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.Finished
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.InProgress
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.collectLastValue
 import com.android.systemui.kosmos.runTest
 import com.android.systemui.kosmos.useUnconfinedTestDispatcher
 import com.android.systemui.res.R
 import com.android.systemui.testKosmos
-import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState
-import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState.Error
-import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState.Finished
-import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState.InProgress
 import com.android.systemui.touchpad.tutorial.ui.gesture.MultiFingerGesture.Companion.SWIPE_DISTANCE
 import com.android.systemui.touchpad.tutorial.ui.gesture.ThreeFingerGesture
 import com.android.systemui.touchpad.tutorial.ui.gesture.Velocity
 import com.android.systemui.touchpad.ui.gesture.fakeVelocityTracker
+import com.android.systemui.touchpad.ui.gesture.touchpadGestureResources
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
 import org.junit.Test
@@ -59,7 +60,12 @@
     private val resources = kosmos.mockResources
 
     private val viewModel =
-        HomeGestureScreenViewModel(kosmos.configurationInteractor, resources, fakeVelocityTracker)
+        HomeGestureScreenViewModel(
+            GestureRecognizerAdapter(
+                HomeGestureRecognizerProvider(kosmos.touchpadGestureResources, fakeVelocityTracker),
+                kosmos.inputDeviceTutorialLogger,
+            )
+        )
 
     @Before
     fun before() {
@@ -80,8 +86,8 @@
                 expected =
                     InProgress(
                         progress = 1f,
-                        progressStartMarker = "drag with gesture",
-                        progressEndMarker = "release playback realtime",
+                        startMarker = "drag with gesture",
+                        endMarker = "release playback realtime",
                     ),
             )
         }
@@ -102,7 +108,7 @@
     @Test
     fun gestureRecognitionTakesLatestDistanceThresholdIntoAccount() =
         kosmos.runTest {
-            val state by collectLastValue(viewModel.gestureUiState)
+            val state by collectLastValue(viewModel.tutorialState)
             performHomeGesture()
             assertThat(state).isInstanceOf(Finished::class.java)
 
@@ -115,7 +121,7 @@
     @Test
     fun gestureRecognitionTakesLatestVelocityThresholdIntoAccount() =
         kosmos.runTest {
-            val state by collectLastValue(viewModel.gestureUiState)
+            val state by collectLastValue(viewModel.tutorialState)
             performHomeGesture()
             assertThat(state).isInstanceOf(Finished::class.java)
 
@@ -126,10 +132,12 @@
         }
 
     private fun setDistanceThreshold(threshold: Float) {
-        fakeConfigRepository.setDimensionPixelSize(
-            R.dimen.touchpad_tutorial_gestures_distance_threshold,
-            (threshold).toInt(),
-        )
+        whenever(
+                resources.getDimensionPixelSize(
+                    R.dimen.touchpad_tutorial_gestures_distance_threshold
+                )
+            )
+            .thenReturn(threshold.toInt())
         fakeConfigRepository.onAnyConfigurationChange()
     }
 
@@ -139,8 +147,11 @@
         fakeConfigRepository.onAnyConfigurationChange()
     }
 
-    private fun Kosmos.assertStateAfterEvents(events: List<MotionEvent>, expected: GestureUiState) {
-        val state by collectLastValue(viewModel.gestureUiState)
+    private fun Kosmos.assertStateAfterEvents(
+        events: List<MotionEvent>,
+        expected: TutorialActionState,
+    ) {
+        val state by collectLastValue(viewModel.tutorialState)
         events.forEach { viewModel.handleEvent(it) }
         assertThat(state).isEqualTo(expected)
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/RecentAppsGestureScreenViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/RecentAppsGestureScreenViewModelTest.kt
index 66bf778..6180fa9 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/RecentAppsGestureScreenViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/RecentAppsGestureScreenViewModelTest.kt
@@ -22,21 +22,22 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
-import com.android.systemui.common.ui.domain.interactor.configurationInteractor
+import com.android.systemui.inputdevice.tutorial.inputDeviceTutorialLogger
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.Error
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.Finished
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.InProgress
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.collectLastValue
 import com.android.systemui.kosmos.runTest
 import com.android.systemui.kosmos.useUnconfinedTestDispatcher
 import com.android.systemui.res.R
 import com.android.systemui.testKosmos
-import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState
-import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState.Error
-import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState.Finished
-import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState.InProgress
 import com.android.systemui.touchpad.tutorial.ui.gesture.MultiFingerGesture.Companion.SWIPE_DISTANCE
 import com.android.systemui.touchpad.tutorial.ui.gesture.ThreeFingerGesture
 import com.android.systemui.touchpad.tutorial.ui.gesture.Velocity
 import com.android.systemui.touchpad.ui.gesture.fakeVelocityTracker
+import com.android.systemui.touchpad.ui.gesture.touchpadGestureResources
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
 import org.junit.Test
@@ -60,9 +61,13 @@
 
     private val viewModel =
         RecentAppsGestureScreenViewModel(
-            kosmos.configurationInteractor,
-            resources,
-            fakeVelocityTracker,
+            GestureRecognizerAdapter(
+                RecentAppsGestureRecognizerProvider(
+                    kosmos.touchpadGestureResources,
+                    fakeVelocityTracker,
+                ),
+                kosmos.inputDeviceTutorialLogger,
+            )
         )
 
     @Before
@@ -84,8 +89,8 @@
                 expected =
                     InProgress(
                         progress = 1f,
-                        progressStartMarker = "drag with gesture",
-                        progressEndMarker = "onPause",
+                        startMarker = "drag with gesture",
+                        endMarker = "onPause",
                     ),
             )
         }
@@ -106,7 +111,7 @@
     @Test
     fun gestureRecognitionTakesLatestDistanceThresholdIntoAccount() =
         kosmos.runTest {
-            val state by collectLastValue(viewModel.gestureUiState)
+            val state by collectLastValue(viewModel.tutorialState)
             performRecentAppsGesture()
             assertThat(state).isInstanceOf(Finished::class.java)
 
@@ -119,7 +124,7 @@
     @Test
     fun gestureRecognitionTakesLatestVelocityThresholdIntoAccount() =
         kosmos.runTest {
-            val state by collectLastValue(viewModel.gestureUiState)
+            val state by collectLastValue(viewModel.tutorialState)
             performRecentAppsGesture()
             assertThat(state).isInstanceOf(Finished::class.java)
 
@@ -145,8 +150,11 @@
         fakeConfigRepository.onAnyConfigurationChange()
     }
 
-    private fun Kosmos.assertStateAfterEvents(events: List<MotionEvent>, expected: GestureUiState) {
-        val state by collectLastValue(viewModel.gestureUiState)
+    private fun Kosmos.assertStateAfterEvents(
+        events: List<MotionEvent>,
+        expected: TutorialActionState,
+    ) {
+        val state by collectLastValue(viewModel.tutorialState)
         events.forEach { viewModel.handleEvent(it) }
         assertThat(state).isEqualTo(expected)
     }
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadTutorialScreenViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadTutorialScreenViewModelTest.kt
new file mode 100644
index 0000000..c113dd9
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadTutorialScreenViewModelTest.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.touchpad.tutorial.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.Error
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.Finished
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.InProgress
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.InProgressAfterError
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState.NotStarted
+import com.android.systemui.kosmos.collectValues
+import com.android.systemui.kosmos.runTest
+import com.android.systemui.kosmos.useUnconfinedTestDispatcher
+import com.android.systemui.testKosmos
+import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.asFlow
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class TouchpadTutorialScreenViewModelTest : SysuiTestCase() {
+
+    companion object {
+        private const val START_MARKER = "startMarker"
+        private const val END_MARKER = "endMarker"
+        private const val SUCCESS_ANIMATION = 0
+    }
+
+    private val kosmos = testKosmos()
+    private val animationProperties =
+        TutorialAnimationProperties(
+            progressStartMarker = START_MARKER,
+            progressEndMarker = END_MARKER,
+            successAnimation = SUCCESS_ANIMATION,
+        )
+
+    @Before
+    fun before() {
+        kosmos.useUnconfinedTestDispatcher()
+    }
+
+    @Test
+    fun gestureStateProducesEquivalentTutorialActionStateInHappyPath() =
+        kosmos.runTest {
+            val happyPath: Flow<Pair<GestureState, TutorialAnimationProperties>> =
+                listOf(
+                        GestureState.NotStarted,
+                        GestureState.InProgress(0f),
+                        GestureState.InProgress(0.5f),
+                        GestureState.InProgress(1f),
+                        GestureState.Finished,
+                    )
+                    .map { it to animationProperties }
+                    .asFlow()
+
+            val resultingStates by collectValues(happyPath.mapToTutorialState())
+
+            assertThat(resultingStates)
+                .containsExactly(
+                    NotStarted,
+                    InProgress(0f, START_MARKER, END_MARKER),
+                    InProgress(0.5f, START_MARKER, END_MARKER),
+                    InProgress(1f, START_MARKER, END_MARKER),
+                    Finished(SUCCESS_ANIMATION),
+                )
+                .inOrder()
+        }
+
+    @Test
+    fun gestureStateProducesEquivalentTutorialActionStateInErrorPath() =
+        kosmos.runTest {
+            val errorPath: Flow<Pair<GestureState, TutorialAnimationProperties>> =
+                listOf(
+                        GestureState.NotStarted,
+                        GestureState.InProgress(0f),
+                        GestureState.Error,
+                        GestureState.InProgress(0.5f),
+                        GestureState.InProgress(1f),
+                        GestureState.Finished,
+                    )
+                    .map { it to animationProperties }
+                    .asFlow()
+
+            val resultingStates by collectValues(errorPath.mapToTutorialState())
+
+            assertThat(resultingStates)
+                .containsExactly(
+                    NotStarted,
+                    InProgress(0f, START_MARKER, END_MARKER),
+                    Error,
+                    InProgressAfterError(InProgress(0.5f, START_MARKER, END_MARKER)),
+                    InProgressAfterError(InProgress(1f, START_MARKER, END_MARKER)),
+                    Finished(SUCCESS_ANIMATION),
+                )
+                .inOrder()
+        }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelTest.kt
index d3071f8..51cac69 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModelTest.kt
@@ -23,66 +23,40 @@
 import android.service.notification.ZenPolicy
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import androidx.test.filters.SmallTest
-import com.android.internal.logging.uiEventLogger
 import com.android.settingslib.notification.modes.TestModeBuilder
 import com.android.settingslib.volume.shared.model.AudioStream
 import com.android.systemui.SysuiTestCase
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.haptics.slider.sliderHapticsViewModelFactory
-import com.android.systemui.kosmos.testScope
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.kosmos.collectLastValue
+import com.android.systemui.kosmos.runCurrent
+import com.android.systemui.kosmos.runTest
 import com.android.systemui.statusbar.policy.data.repository.fakeZenModeRepository
-import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor
 import com.android.systemui.testKosmos
-import com.android.systemui.volume.domain.interactor.audioVolumeInteractor
-import com.android.systemui.volume.shared.volumePanelLogger
 import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
-import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
 
-@OptIn(ExperimentalCoroutinesApi::class)
 @SmallTest
 @RunWith(AndroidJUnit4::class)
 class AudioStreamSliderViewModelTest : SysuiTestCase() {
 
     private val kosmos = testKosmos()
-    private val testScope = kosmos.testScope
     private val zenModeRepository = kosmos.fakeZenModeRepository
 
-    private lateinit var mediaStream: AudioStreamSliderViewModel
-    private lateinit var alarmsStream: AudioStreamSliderViewModel
-    private lateinit var notificationStream: AudioStreamSliderViewModel
-    private lateinit var otherStream: AudioStreamSliderViewModel
-
-    @Before
-    fun setUp() {
-        mediaStream = audioStreamSliderViewModel(AudioManager.STREAM_MUSIC)
-        alarmsStream = audioStreamSliderViewModel(AudioManager.STREAM_ALARM)
-        notificationStream = audioStreamSliderViewModel(AudioManager.STREAM_NOTIFICATION)
-        otherStream = audioStreamSliderViewModel(AudioManager.STREAM_VOICE_CALL)
-    }
-
-    private fun audioStreamSliderViewModel(stream: Int): AudioStreamSliderViewModel {
-        return AudioStreamSliderViewModel(
+    private fun Kosmos.audioStreamSliderViewModel(stream: Int): AudioStreamSliderViewModel {
+        return audioStreamSliderViewModelFactory.create(
             AudioStreamSliderViewModel.FactoryAudioStreamWrapper(AudioStream(stream)),
-            testScope.backgroundScope,
-            context,
-            kosmos.audioVolumeInteractor,
-            kosmos.zenModeInteractor,
-            kosmos.uiEventLogger,
-            kosmos.volumePanelLogger,
-            kosmos.sliderHapticsViewModelFactory,
+            applicationCoroutineScope,
         )
     }
 
     @Test
     @EnableFlags(Flags.FLAG_MODES_UI, Flags.FLAG_MODES_UI_ICONS)
     fun slider_media_hasDisabledByModesText() =
-        testScope.runTest {
-            val mediaSlider by collectLastValue(mediaStream.slider)
+        kosmos.runTest {
+            val mediaSlider by
+                collectLastValue(audioStreamSliderViewModel(AudioManager.STREAM_MUSIC).slider)
 
             zenModeRepository.addMode(
                 TestModeBuilder()
@@ -112,8 +86,9 @@
     @Test
     @EnableFlags(Flags.FLAG_MODES_UI, Flags.FLAG_MODES_UI_ICONS)
     fun slider_alarms_hasDisabledByModesText() =
-        testScope.runTest {
-            val alarmsSlider by collectLastValue(alarmsStream.slider)
+        kosmos.runTest {
+            val alarmsSlider by
+                collectLastValue(audioStreamSliderViewModel(AudioManager.STREAM_ALARM).slider)
 
             zenModeRepository.addMode(
                 TestModeBuilder()
@@ -141,9 +116,10 @@
 
     @Test
     @EnableFlags(Flags.FLAG_MODES_UI, Flags.FLAG_MODES_UI_ICONS)
-    fun slider_other_hasDisabledByModesText() =
-        testScope.runTest {
-            val otherSlider by collectLastValue(otherStream.slider)
+    fun slider_other_hasDisabledText() =
+        kosmos.runTest {
+            val otherSlider by
+                collectLastValue(audioStreamSliderViewModel(AudioManager.STREAM_VOICE_CALL).slider)
 
             zenModeRepository.addMode(
                 TestModeBuilder()
@@ -154,20 +130,17 @@
             )
             runCurrent()
 
-            assertThat(otherSlider!!.disabledMessage)
-                .isEqualTo("Unavailable because Everything blocked is on")
-
-            zenModeRepository.clearModes()
-            runCurrent()
-
             assertThat(otherSlider!!.disabledMessage).isEqualTo("Unavailable")
         }
 
     @Test
     @EnableFlags(Flags.FLAG_MODES_UI, Flags.FLAG_MODES_UI_ICONS)
     fun slider_notification_hasSpecialDisabledText() =
-        testScope.runTest {
-            val notificationSlider by collectLastValue(notificationStream.slider)
+        kosmos.runTest {
+            val notificationSlider by
+                collectLastValue(
+                    audioStreamSliderViewModel(AudioManager.STREAM_NOTIFICATION).slider
+                )
             runCurrent()
 
             assertThat(notificationSlider!!.disabledMessage)
diff --git a/packages/SystemUI/proguard_common.flags b/packages/SystemUI/proguard_common.flags
index 162d8ae..02b2bcf 100644
--- a/packages/SystemUI/proguard_common.flags
+++ b/packages/SystemUI/proguard_common.flags
@@ -1,5 +1,11 @@
 -include proguard_kotlin.flags
--keep class com.android.systemui.VendorServices
+
+# VendorServices implements CoreStartable and may be instantiated reflectively in
+# SystemUIApplication#startAdditionalStartable.
+# TODO(b/373579455): Rewrite this to a @UsesReflection keep annotation.
+-keep class com.android.systemui.VendorServices {
+  public void <init>();
+}
 
 # Needed to ensure callback field references are kept in their respective
 # owning classes when the downstream callback registrars only store weak refs.
diff --git a/packages/SystemUI/res/color/active_track_color.xml b/packages/SystemUI/res/color/active_track_color.xml
new file mode 100644
index 0000000..2325555
--- /dev/null
+++ b/packages/SystemUI/res/color/active_track_color.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2025 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT 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" xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+    <item android:color="@androidprv:color/materialColorPrimary" android:state_enabled="true" />
+    <item android:alpha="0.38" android:color="@androidprv:color/materialColorOnSurface" />
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/color/inactive_track_color.xml b/packages/SystemUI/res/color/inactive_track_color.xml
new file mode 100644
index 0000000..2ba5ebd
--- /dev/null
+++ b/packages/SystemUI/res/color/inactive_track_color.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2025 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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" xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+    <item android:color="@androidprv:color/materialColorSurfaceContainerHighest" android:state_enabled="true" />
+    <item android:alpha="0.12" android:color="@androidprv:color/materialColorOnSurface" />
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/color/on_active_track_color.xml b/packages/SystemUI/res/color/on_active_track_color.xml
new file mode 100644
index 0000000..7ca79a9
--- /dev/null
+++ b/packages/SystemUI/res/color/on_active_track_color.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2025 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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" xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+    <item android:color="@androidprv:color/materialColorOnPrimary" android:state_enabled="true" />
+    <item android:color="@androidprv:color/materialColorOnSurfaceVariant" />
+</selector>
diff --git a/packages/SystemUI/res/color/on_inactive_track_color.xml b/packages/SystemUI/res/color/on_inactive_track_color.xml
new file mode 100644
index 0000000..0eb4bfa
--- /dev/null
+++ b/packages/SystemUI/res/color/on_inactive_track_color.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2025 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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" xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+    <item android:color="@androidprv:color/materialColorPrimary" android:state_enabled="true" />
+    <item android:color="@androidprv:color/materialColorOnSurfaceVariant" />
+</selector>
diff --git a/packages/SystemUI/res/color/thumb_color.xml b/packages/SystemUI/res/color/thumb_color.xml
new file mode 100644
index 0000000..2b0e3a9
--- /dev/null
+++ b/packages/SystemUI/res/color/thumb_color.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2025 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT 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" xmlns:androidprv="http://schemas.android.com/apk/prv/res/android">
+    <item android:color="@androidprv:color/materialColorPrimary" android:state_enabled="true" />
+    <item android:alpha="0.38" android:color="@androidprv:color/materialColorOnSurface" />
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_media_connecting_status_container.xml b/packages/SystemUI/res/drawable/ic_media_connecting_status_container.xml
new file mode 100644
index 0000000..f8c0fa0
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_media_connecting_status_container.xml
@@ -0,0 +1,199 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2025 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:aapt="http://schemas.android.com/aapt">
+    <target android:name="_R_G_L_1_G">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="83"
+                    android:propertyName="scaleX"
+                    android:startOffset="1000"
+                    android:valueFrom="0.45561"
+                    android:valueTo="0.69699"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.8,0.15 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="83"
+                    android:propertyName="scaleY"
+                    android:startOffset="1000"
+                    android:valueFrom="0.6288400000000001"
+                    android:valueTo="0.81618"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.8,0.15 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="417"
+                    android:propertyName="scaleX"
+                    android:startOffset="1083"
+                    android:valueFrom="0.69699"
+                    android:valueTo="1.05905"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.05,0.7 0.1,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="417"
+                    android:propertyName="scaleY"
+                    android:startOffset="1083"
+                    android:valueFrom="0.81618"
+                    android:valueTo="1.0972"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.05,0.7 0.1,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="500"
+                    android:propertyName="rotation"
+                    android:startOffset="0"
+                    android:valueFrom="90"
+                    android:valueTo="135"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="500"
+                    android:propertyName="rotation"
+                    android:startOffset="500"
+                    android:valueFrom="135"
+                    android:valueTo="180"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.167,0.167 0.833,0.833 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="83"
+                    android:propertyName="scaleX"
+                    android:startOffset="1000"
+                    android:valueFrom="0.0434"
+                    android:valueTo="0.05063"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.8,0.15 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="83"
+                    android:propertyName="scaleY"
+                    android:startOffset="1000"
+                    android:valueFrom="0.0434"
+                    android:valueTo="0.042350000000000006"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.8,0.15 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="417"
+                    android:propertyName="scaleX"
+                    android:startOffset="1083"
+                    android:valueFrom="0.05063"
+                    android:valueTo="0.06146"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.05,0.7 0.1,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="417"
+                    android:propertyName="scaleY"
+                    android:startOffset="1083"
+                    android:valueFrom="0.042350000000000006"
+                    android:valueTo="0.040780000000000004"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.05,0.7 0.1,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="time_group">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="1017"
+                    android:propertyName="translateX"
+                    android:startOffset="0"
+                    android:valueFrom="0"
+                    android:valueTo="1"
+                    android:valueType="floatType" />
+            </set>
+        </aapt:attr>
+    </target>
+    <aapt:attr name="android:drawable">
+        <vector
+            android:width="88dp"
+            android:height="56dp"
+            android:viewportHeight="56"
+            android:viewportWidth="88">
+            <group android:name="_R_G">
+                <group
+                    android:name="_R_G_L_1_G"
+                    android:pivotX="0.493"
+                    android:pivotY="0.124"
+                    android:scaleX="1.05905"
+                    android:scaleY="1.0972"
+                    android:translateX="43.528999999999996"
+                    android:translateY="27.898">
+                    <path
+                        android:name="_R_G_L_1_G_D_0_P_0"
+                        android:fillAlpha="1"
+                        android:fillColor="#3d90ff"
+                        android:fillType="nonZero"
+                        android:pathData=" M34.47 0.63 C34.47,0.63 34.42,0.64 34.42,0.64 C33.93,12.88 24.69,21.84 13.06,21.97 C13.06,21.97 -12.54,21.97 -12.54,21.97 C-23.11,21.84 -33.38,13.11 -33.52,-0.27 C-33.52,-0.27 -33.52,-0.05 -33.52,-0.05 C-33.5,-13.21 -21.73,-21.76 -12.9,-21.76 C-12.9,-21.76 14.59,-21.76 14.59,-21.76 C24.81,-21.88 34.49,-10.58 34.47,0.63c " />
+                </group>
+                <group
+                    android:name="_R_G_L_0_G"
+                    android:rotation="0"
+                    android:scaleX="0.06146"
+                    android:scaleY="0.040780000000000004"
+                    android:translateX="44"
+                    android:translateY="28">
+                    <path
+                        android:name="_R_G_L_0_G_D_0_P_0"
+                        android:fillAlpha="1"
+                        android:fillColor="#3d90ff"
+                        android:fillType="nonZero"
+                        android:pathData=" M-0.65 -437.37 C-0.65,-437.37 8.33,-437.66 8.33,-437.66 C8.33,-437.66 17.31,-437.95 17.31,-437.95 C17.31,-437.95 26.25,-438.78 26.25,-438.78 C26.25,-438.78 35.16,-439.95 35.16,-439.95 C35.16,-439.95 44.07,-441.11 44.07,-441.11 C44.07,-441.11 52.85,-443 52.85,-443 C52.85,-443 61.6,-445.03 61.6,-445.03 C61.6,-445.03 70.35,-447.09 70.35,-447.09 C70.35,-447.09 78.91,-449.83 78.91,-449.83 C78.91,-449.83 87.43,-452.67 87.43,-452.67 C87.43,-452.67 95.79,-455.97 95.79,-455.97 C95.79,-455.97 104.11,-459.35 104.11,-459.35 C104.11,-459.35 112.36,-462.93 112.36,-462.93 C112.36,-462.93 120.6,-466.51 120.6,-466.51 C120.6,-466.51 128.84,-470.09 128.84,-470.09 C128.84,-470.09 137.09,-473.67 137.09,-473.67 C137.09,-473.67 145.49,-476.84 145.49,-476.84 C145.49,-476.84 153.9,-480.01 153.9,-480.01 C153.9,-480.01 162.31,-483.18 162.31,-483.18 C162.31,-483.18 170.98,-485.54 170.98,-485.54 C170.98,-485.54 179.66,-487.85 179.66,-487.85 C179.66,-487.85 188.35,-490.15 188.35,-490.15 C188.35,-490.15 197.22,-491.58 197.22,-491.58 C197.22,-491.58 206.09,-493.01 206.09,-493.01 C206.09,-493.01 214.98,-494.28 214.98,-494.28 C214.98,-494.28 223.95,-494.81 223.95,-494.81 C223.95,-494.81 232.93,-495.33 232.93,-495.33 C232.93,-495.33 241.9,-495.5 241.9,-495.5 C241.9,-495.5 250.88,-495.13 250.88,-495.13 C250.88,-495.13 259.86,-494.75 259.86,-494.75 C259.86,-494.75 268.78,-493.78 268.78,-493.78 C268.78,-493.78 277.68,-492.52 277.68,-492.52 C277.68,-492.52 286.57,-491.26 286.57,-491.26 C286.57,-491.26 295.31,-489.16 295.31,-489.16 C295.31,-489.16 304.04,-487.04 304.04,-487.04 C304.04,-487.04 312.7,-484.65 312.7,-484.65 C312.7,-484.65 321.19,-481.72 321.19,-481.72 C321.19,-481.72 329.68,-478.78 329.68,-478.78 C329.68,-478.78 337.96,-475.31 337.96,-475.31 C337.96,-475.31 346.14,-471.59 346.14,-471.59 C346.14,-471.59 354.3,-467.82 354.3,-467.82 C354.3,-467.82 362.11,-463.38 362.11,-463.38 C362.11,-463.38 369.92,-458.93 369.92,-458.93 C369.92,-458.93 377.53,-454.17 377.53,-454.17 C377.53,-454.17 384.91,-449.04 384.91,-449.04 C384.91,-449.04 392.29,-443.91 392.29,-443.91 C392.29,-443.91 399.26,-438.24 399.26,-438.24 C399.26,-438.24 406.15,-432.48 406.15,-432.48 C406.15,-432.48 412.92,-426.57 412.92,-426.57 C412.92,-426.57 419.27,-420.22 419.27,-420.22 C419.27,-420.22 425.62,-413.87 425.62,-413.87 C425.62,-413.87 431.61,-407.18 431.61,-407.18 C431.61,-407.18 437.38,-400.29 437.38,-400.29 C437.38,-400.29 443.14,-393.39 443.14,-393.39 C443.14,-393.39 448.27,-386.01 448.27,-386.01 C448.27,-386.01 453.4,-378.64 453.4,-378.64 C453.4,-378.64 458.26,-371.09 458.26,-371.09 C458.26,-371.09 462.71,-363.28 462.71,-363.28 C462.71,-363.28 467.16,-355.47 467.16,-355.47 C467.16,-355.47 471.03,-347.37 471.03,-347.37 C471.03,-347.37 474.75,-339.19 474.75,-339.19 C474.75,-339.19 478.34,-330.95 478.34,-330.95 C478.34,-330.95 481.28,-322.46 481.28,-322.46 C481.28,-322.46 484.21,-313.97 484.21,-313.97 C484.21,-313.97 486.72,-305.35 486.72,-305.35 C486.72,-305.35 488.84,-296.62 488.84,-296.62 C488.84,-296.62 490.96,-287.88 490.96,-287.88 C490.96,-287.88 492.33,-279.01 492.33,-279.01 C492.33,-279.01 493.59,-270.11 493.59,-270.11 C493.59,-270.11 494.69,-261.2 494.69,-261.2 C494.69,-261.2 495.07,-252.22 495.07,-252.22 C495.07,-252.22 495.44,-243.24 495.44,-243.24 C495.44,-243.24 495.41,-234.27 495.41,-234.27 C495.41,-234.27 494.88,-225.29 494.88,-225.29 C494.88,-225.29 494.35,-216.32 494.35,-216.32 C494.35,-216.32 493.22,-207.42 493.22,-207.42 C493.22,-207.42 491.79,-198.55 491.79,-198.55 C491.79,-198.55 490.36,-189.68 490.36,-189.68 C490.36,-189.68 488.19,-180.96 488.19,-180.96 C488.19,-180.96 485.88,-172.28 485.88,-172.28 C485.88,-172.28 483.56,-163.6 483.56,-163.6 C483.56,-163.6 480.48,-155.16 480.48,-155.16 C480.48,-155.16 477.31,-146.75 477.31,-146.75 C477.31,-146.75 474.14,-138.34 474.14,-138.34 C474.14,-138.34 470.62,-130.07 470.62,-130.07 C470.62,-130.07 467.04,-121.83 467.04,-121.83 C467.04,-121.83 463.46,-113.59 463.46,-113.59 C463.46,-113.59 459.88,-105.35 459.88,-105.35 C459.88,-105.35 456.54,-97.01 456.54,-97.01 C456.54,-97.01 453.37,-88.6 453.37,-88.6 C453.37,-88.6 450.21,-80.19 450.21,-80.19 C450.21,-80.19 447.68,-71.57 447.68,-71.57 C447.68,-71.57 445.36,-62.89 445.36,-62.89 C445.36,-62.89 443.04,-54.21 443.04,-54.21 C443.04,-54.21 441.54,-45.35 441.54,-45.35 C441.54,-45.35 440.09,-36.48 440.09,-36.48 C440.09,-36.48 438.78,-27.6 438.78,-27.6 C438.78,-27.6 438.19,-18.63 438.19,-18.63 C438.19,-18.63 437.61,-9.66 437.61,-9.66 C437.61,-9.66 437.36,-0.69 437.36,-0.69 C437.36,-0.69 437.65,8.29 437.65,8.29 C437.65,8.29 437.95,17.27 437.95,17.27 C437.95,17.27 438.77,26.21 438.77,26.21 C438.77,26.21 439.94,35.12 439.94,35.12 C439.94,35.12 441.11,44.03 441.11,44.03 C441.11,44.03 442.99,52.81 442.99,52.81 C442.99,52.81 445.02,61.57 445.02,61.57 C445.02,61.57 447.07,70.31 447.07,70.31 C447.07,70.31 449.82,78.87 449.82,78.87 C449.82,78.87 452.65,87.4 452.65,87.4 C452.65,87.4 455.96,95.75 455.96,95.75 C455.96,95.75 459.33,104.08 459.33,104.08 C459.33,104.08 462.91,112.32 462.91,112.32 C462.91,112.32 466.49,120.57 466.49,120.57 C466.49,120.57 470.07,128.81 470.07,128.81 C470.07,128.81 473.65,137.05 473.65,137.05 C473.65,137.05 476.82,145.46 476.82,145.46 C476.82,145.46 479.99,153.87 479.99,153.87 C479.99,153.87 483.17,162.28 483.17,162.28 C483.17,162.28 485.52,170.94 485.52,170.94 C485.52,170.94 487.84,179.63 487.84,179.63 C487.84,179.63 490.14,188.31 490.14,188.31 C490.14,188.31 491.57,197.18 491.57,197.18 C491.57,197.18 493,206.06 493,206.06 C493,206.06 494.27,214.95 494.27,214.95 C494.27,214.95 494.8,223.92 494.8,223.92 C494.8,223.92 495.33,232.89 495.33,232.89 C495.33,232.89 495.5,241.86 495.5,241.86 C495.5,241.86 495.12,250.84 495.12,250.84 C495.12,250.84 494.75,259.82 494.75,259.82 C494.75,259.82 493.78,268.74 493.78,268.74 C493.78,268.74 492.52,277.64 492.52,277.64 C492.52,277.64 491.27,286.54 491.27,286.54 C491.27,286.54 489.16,295.27 489.16,295.27 C489.16,295.27 487.05,304.01 487.05,304.01 C487.05,304.01 484.66,312.66 484.66,312.66 C484.66,312.66 481.73,321.16 481.73,321.16 C481.73,321.16 478.79,329.65 478.79,329.65 C478.79,329.65 475.32,337.93 475.32,337.93 C475.32,337.93 471.6,346.11 471.6,346.11 C471.6,346.11 467.84,354.27 467.84,354.27 C467.84,354.27 463.39,362.08 463.39,362.08 C463.39,362.08 458.94,369.89 458.94,369.89 C458.94,369.89 454.19,377.5 454.19,377.5 C454.19,377.5 449.06,384.88 449.06,384.88 C449.06,384.88 443.93,392.26 443.93,392.26 C443.93,392.26 438.26,399.23 438.26,399.23 C438.26,399.23 432.5,406.12 432.5,406.12 C432.5,406.12 426.6,412.89 426.6,412.89 C426.6,412.89 420.24,419.24 420.24,419.24 C420.24,419.24 413.89,425.6 413.89,425.6 C413.89,425.6 407.2,431.59 407.2,431.59 C407.2,431.59 400.31,437.36 400.31,437.36 C400.31,437.36 393.42,443.12 393.42,443.12 C393.42,443.12 386.04,448.25 386.04,448.25 C386.04,448.25 378.66,453.38 378.66,453.38 C378.66,453.38 371.11,458.24 371.11,458.24 C371.11,458.24 363.31,462.69 363.31,462.69 C363.31,462.69 355.5,467.14 355.5,467.14 C355.5,467.14 347.4,471.02 347.4,471.02 C347.4,471.02 339.22,474.73 339.22,474.73 C339.22,474.73 330.99,478.33 330.99,478.33 C330.99,478.33 322.49,481.27 322.49,481.27 C322.49,481.27 314,484.2 314,484.2 C314,484.2 305.38,486.71 305.38,486.71 C305.38,486.71 296.65,488.83 296.65,488.83 C296.65,488.83 287.91,490.95 287.91,490.95 C287.91,490.95 279.04,492.33 279.04,492.33 C279.04,492.33 270.14,493.59 270.14,493.59 C270.14,493.59 261.23,494.69 261.23,494.69 C261.23,494.69 252.25,495.07 252.25,495.07 C252.25,495.07 243.28,495.44 243.28,495.44 C243.28,495.44 234.3,495.41 234.3,495.41 C234.3,495.41 225.33,494.88 225.33,494.88 C225.33,494.88 216.36,494.35 216.36,494.35 C216.36,494.35 207.45,493.23 207.45,493.23 C207.45,493.23 198.58,491.8 198.58,491.8 C198.58,491.8 189.71,490.37 189.71,490.37 C189.71,490.37 180.99,488.21 180.99,488.21 C180.99,488.21 172.31,485.89 172.31,485.89 C172.31,485.89 163.63,483.57 163.63,483.57 C163.63,483.57 155.19,480.5 155.19,480.5 C155.19,480.5 146.78,477.32 146.78,477.32 C146.78,477.32 138.37,474.15 138.37,474.15 C138.37,474.15 130.11,470.63 130.11,470.63 C130.11,470.63 121.86,467.06 121.86,467.06 C121.86,467.06 113.62,463.48 113.62,463.48 C113.62,463.48 105.38,459.9 105.38,459.9 C105.38,459.9 97.04,456.56 97.04,456.56 C97.04,456.56 88.63,453.39 88.63,453.39 C88.63,453.39 80.22,450.22 80.22,450.22 C80.22,450.22 71.6,447.7 71.6,447.7 C71.6,447.7 62.92,445.37 62.92,445.37 C62.92,445.37 54.24,443.05 54.24,443.05 C54.24,443.05 45.38,441.55 45.38,441.55 C45.38,441.55 36.52,440.1 36.52,440.1 C36.52,440.1 27.63,438.78 27.63,438.78 C27.63,438.78 18.66,438.2 18.66,438.2 C18.66,438.2 9.7,437.61 9.7,437.61 C9.7,437.61 0.72,437.36 0.72,437.36 C0.72,437.36 -8.26,437.65 -8.26,437.65 C-8.26,437.65 -17.24,437.95 -17.24,437.95 C-17.24,437.95 -26.18,438.77 -26.18,438.77 C-26.18,438.77 -35.09,439.94 -35.09,439.94 C-35.09,439.94 -44,441.1 -44,441.1 C-44,441.1 -52.78,442.98 -52.78,442.98 C-52.78,442.98 -61.53,445.02 -61.53,445.02 C-61.53,445.02 -70.28,447.07 -70.28,447.07 C-70.28,447.07 -78.84,449.81 -78.84,449.81 C-78.84,449.81 -87.37,452.64 -87.37,452.64 C-87.37,452.64 -95.72,455.95 -95.72,455.95 C-95.72,455.95 -104.05,459.32 -104.05,459.32 C-104.05,459.32 -112.29,462.9 -112.29,462.9 C-112.29,462.9 -120.53,466.48 -120.53,466.48 C-120.53,466.48 -128.78,470.06 -128.78,470.06 C-128.78,470.06 -137.02,473.63 -137.02,473.63 C-137.02,473.63 -145.43,476.81 -145.43,476.81 C-145.43,476.81 -153.84,479.98 -153.84,479.98 C-153.84,479.98 -162.24,483.15 -162.24,483.15 C-162.24,483.15 -170.91,485.52 -170.91,485.52 C-170.91,485.52 -179.59,487.83 -179.59,487.83 C-179.59,487.83 -188.28,490.13 -188.28,490.13 C-188.28,490.13 -197.15,491.56 -197.15,491.56 C-197.15,491.56 -206.02,492.99 -206.02,492.99 C-206.02,492.99 -214.91,494.27 -214.91,494.27 C-214.91,494.27 -223.88,494.8 -223.88,494.8 C-223.88,494.8 -232.85,495.33 -232.85,495.33 C-232.85,495.33 -241.83,495.5 -241.83,495.5 C-241.83,495.5 -250.81,495.13 -250.81,495.13 C-250.81,495.13 -259.79,494.75 -259.79,494.75 C-259.79,494.75 -268.71,493.79 -268.71,493.79 C-268.71,493.79 -277.61,492.53 -277.61,492.53 C-277.61,492.53 -286.51,491.27 -286.51,491.27 C-286.51,491.27 -295.24,489.17 -295.24,489.17 C-295.24,489.17 -303.98,487.06 -303.98,487.06 C-303.98,487.06 -312.63,484.67 -312.63,484.67 C-312.63,484.67 -321.12,481.74 -321.12,481.74 C-321.12,481.74 -329.62,478.8 -329.62,478.8 C-329.62,478.8 -337.9,475.33 -337.9,475.33 C-337.9,475.33 -346.08,471.62 -346.08,471.62 C-346.08,471.62 -354.24,467.85 -354.24,467.85 C-354.24,467.85 -362.05,463.41 -362.05,463.41 C-362.05,463.41 -369.86,458.96 -369.86,458.96 C-369.86,458.96 -377.47,454.21 -377.47,454.21 C-377.47,454.21 -384.85,449.08 -384.85,449.08 C-384.85,449.08 -392.23,443.95 -392.23,443.95 C-392.23,443.95 -399.2,438.29 -399.2,438.29 C-399.2,438.29 -406.09,432.52 -406.09,432.52 C-406.09,432.52 -412.86,426.62 -412.86,426.62 C-412.86,426.62 -419.22,420.27 -419.22,420.27 C-419.22,420.27 -425.57,413.91 -425.57,413.91 C-425.57,413.91 -431.57,407.23 -431.57,407.23 C-431.57,407.23 -437.33,400.34 -437.33,400.34 C-437.33,400.34 -443.1,393.44 -443.1,393.44 C-443.1,393.44 -448.23,386.07 -448.23,386.07 C-448.23,386.07 -453.36,378.69 -453.36,378.69 C-453.36,378.69 -458.23,371.15 -458.23,371.15 C-458.23,371.15 -462.67,363.33 -462.67,363.33 C-462.67,363.33 -467.12,355.53 -467.12,355.53 C-467.12,355.53 -471,347.43 -471,347.43 C-471,347.43 -474.72,339.25 -474.72,339.25 C-474.72,339.25 -478.32,331.02 -478.32,331.02 C-478.32,331.02 -481.25,322.52 -481.25,322.52 C-481.25,322.52 -484.19,314.03 -484.19,314.03 C-484.19,314.03 -486.71,305.42 -486.71,305.42 C-486.71,305.42 -488.82,296.68 -488.82,296.68 C-488.82,296.68 -490.94,287.95 -490.94,287.95 C-490.94,287.95 -492.32,279.07 -492.32,279.07 C-492.32,279.07 -493.58,270.18 -493.58,270.18 C-493.58,270.18 -494.69,261.27 -494.69,261.27 C-494.69,261.27 -495.07,252.29 -495.07,252.29 C-495.07,252.29 -495.44,243.31 -495.44,243.31 C-495.44,243.31 -495.42,234.33 -495.42,234.33 C-495.42,234.33 -494.89,225.36 -494.89,225.36 C-494.89,225.36 -494.36,216.39 -494.36,216.39 C-494.36,216.39 -493.23,207.49 -493.23,207.49 C-493.23,207.49 -491.8,198.61 -491.8,198.61 C-491.8,198.61 -490.37,189.74 -490.37,189.74 C-490.37,189.74 -488.22,181.02 -488.22,181.02 C-488.22,181.02 -485.9,172.34 -485.9,172.34 C-485.9,172.34 -483.58,163.66 -483.58,163.66 C-483.58,163.66 -480.51,155.22 -480.51,155.22 C-480.51,155.22 -477.34,146.81 -477.34,146.81 C-477.34,146.81 -474.17,138.41 -474.17,138.41 C-474.17,138.41 -470.65,130.14 -470.65,130.14 C-470.65,130.14 -467.07,121.9 -467.07,121.9 C-467.07,121.9 -463.49,113.65 -463.49,113.65 C-463.49,113.65 -459.91,105.41 -459.91,105.41 C-459.91,105.41 -456.57,97.07 -456.57,97.07 C-456.57,97.07 -453.4,88.66 -453.4,88.66 C-453.4,88.66 -450.23,80.25 -450.23,80.25 C-450.23,80.25 -447.7,71.64 -447.7,71.64 C-447.7,71.64 -445.38,62.96 -445.38,62.96 C-445.38,62.96 -443.06,54.28 -443.06,54.28 C-443.06,54.28 -441.56,45.42 -441.56,45.42 C-441.56,45.42 -440.1,36.55 -440.1,36.55 C-440.1,36.55 -438.78,27.67 -438.78,27.67 C-438.78,27.67 -438.2,18.7 -438.2,18.7 C-438.2,18.7 -437.62,9.73 -437.62,9.73 C-437.62,9.73 -437.36,0.76 -437.36,0.76 C-437.36,0.76 -437.66,-8.22 -437.66,-8.22 C-437.66,-8.22 -437.95,-17.2 -437.95,-17.2 C-437.95,-17.2 -438.77,-26.14 -438.77,-26.14 C-438.77,-26.14 -439.93,-35.05 -439.93,-35.05 C-439.93,-35.05 -441.1,-43.96 -441.1,-43.96 C-441.1,-43.96 -442.98,-52.75 -442.98,-52.75 C-442.98,-52.75 -445.01,-61.5 -445.01,-61.5 C-445.01,-61.5 -447.06,-70.25 -447.06,-70.25 C-447.06,-70.25 -449.8,-78.81 -449.8,-78.81 C-449.8,-78.81 -452.63,-87.33 -452.63,-87.33 C-452.63,-87.33 -455.94,-95.69 -455.94,-95.69 C-455.94,-95.69 -459.31,-104.02 -459.31,-104.02 C-459.31,-104.02 -462.89,-112.26 -462.89,-112.26 C-462.89,-112.26 -466.47,-120.5 -466.47,-120.5 C-466.47,-120.5 -470.05,-128.74 -470.05,-128.74 C-470.05,-128.74 -473.68,-137.12 -473.68,-137.12 C-473.68,-137.12 -476.85,-145.53 -476.85,-145.53 C-476.85,-145.53 -480.03,-153.94 -480.03,-153.94 C-480.03,-153.94 -483.2,-162.34 -483.2,-162.34 C-483.2,-162.34 -485.55,-171.02 -485.55,-171.02 C-485.55,-171.02 -487.86,-179.7 -487.86,-179.7 C-487.86,-179.7 -490.15,-188.39 -490.15,-188.39 C-490.15,-188.39 -491.58,-197.26 -491.58,-197.26 C-491.58,-197.26 -493.01,-206.13 -493.01,-206.13 C-493.01,-206.13 -494.28,-215.02 -494.28,-215.02 C-494.28,-215.02 -494.81,-223.99 -494.81,-223.99 C-494.81,-223.99 -495.33,-232.96 -495.33,-232.96 C-495.33,-232.96 -495.5,-241.94 -495.5,-241.94 C-495.5,-241.94 -495.12,-250.92 -495.12,-250.92 C-495.12,-250.92 -494.75,-259.9 -494.75,-259.9 C-494.75,-259.9 -493.78,-268.82 -493.78,-268.82 C-493.78,-268.82 -492.52,-277.72 -492.52,-277.72 C-492.52,-277.72 -491.26,-286.61 -491.26,-286.61 C-491.26,-286.61 -489.15,-295.35 -489.15,-295.35 C-489.15,-295.35 -487.03,-304.08 -487.03,-304.08 C-487.03,-304.08 -484.64,-312.73 -484.64,-312.73 C-484.64,-312.73 -481.7,-321.23 -481.7,-321.23 C-481.7,-321.23 -478.77,-329.72 -478.77,-329.72 C-478.77,-329.72 -475.29,-338 -475.29,-338 C-475.29,-338 -471.57,-346.18 -471.57,-346.18 C-471.57,-346.18 -467.8,-354.33 -467.8,-354.33 C-467.8,-354.33 -463.36,-362.14 -463.36,-362.14 C-463.36,-362.14 -458.91,-369.95 -458.91,-369.95 C-458.91,-369.95 -454.15,-377.56 -454.15,-377.56 C-454.15,-377.56 -449.02,-384.94 -449.02,-384.94 C-449.02,-384.94 -443.88,-392.32 -443.88,-392.32 C-443.88,-392.32 -438.22,-399.28 -438.22,-399.28 C-438.22,-399.28 -432.45,-406.18 -432.45,-406.18 C-432.45,-406.18 -426.55,-412.94 -426.55,-412.94 C-426.55,-412.94 -420.19,-419.3 -420.19,-419.3 C-420.19,-419.3 -413.84,-425.65 -413.84,-425.65 C-413.84,-425.65 -407.15,-431.64 -407.15,-431.64 C-407.15,-431.64 -400.26,-437.41 -400.26,-437.41 C-400.26,-437.41 -393.36,-443.16 -393.36,-443.16 C-393.36,-443.16 -385.98,-448.29 -385.98,-448.29 C-385.98,-448.29 -378.6,-453.43 -378.6,-453.43 C-378.6,-453.43 -371.05,-458.28 -371.05,-458.28 C-371.05,-458.28 -363.24,-462.73 -363.24,-462.73 C-363.24,-462.73 -355.43,-467.18 -355.43,-467.18 C-355.43,-467.18 -347.33,-471.05 -347.33,-471.05 C-347.33,-471.05 -339.15,-474.76 -339.15,-474.76 C-339.15,-474.76 -330.92,-478.35 -330.92,-478.35 C-330.92,-478.35 -322.42,-481.29 -322.42,-481.29 C-322.42,-481.29 -313.93,-484.23 -313.93,-484.23 C-313.93,-484.23 -305.31,-486.73 -305.31,-486.73 C-305.31,-486.73 -296.58,-488.85 -296.58,-488.85 C-296.58,-488.85 -287.85,-490.97 -287.85,-490.97 C-287.85,-490.97 -278.97,-492.34 -278.97,-492.34 C-278.97,-492.34 -270.07,-493.6 -270.07,-493.6 C-270.07,-493.6 -261.16,-494.7 -261.16,-494.7 C-261.16,-494.7 -252.18,-495.07 -252.18,-495.07 C-252.18,-495.07 -243.2,-495.44 -243.2,-495.44 C-243.2,-495.44 -234.23,-495.41 -234.23,-495.41 C-234.23,-495.41 -225.26,-494.88 -225.26,-494.88 C-225.26,-494.88 -216.29,-494.35 -216.29,-494.35 C-216.29,-494.35 -207.38,-493.22 -207.38,-493.22 C-207.38,-493.22 -198.51,-491.79 -198.51,-491.79 C-198.51,-491.79 -189.64,-490.36 -189.64,-490.36 C-189.64,-490.36 -180.92,-488.19 -180.92,-488.19 C-180.92,-488.19 -172.24,-485.87 -172.24,-485.87 C-172.24,-485.87 -163.56,-483.56 -163.56,-483.56 C-163.56,-483.56 -155.12,-480.47 -155.12,-480.47 C-155.12,-480.47 -146.72,-477.3 -146.72,-477.3 C-146.72,-477.3 -138.31,-474.13 -138.31,-474.13 C-138.31,-474.13 -130.04,-470.61 -130.04,-470.61 C-130.04,-470.61 -121.8,-467.03 -121.8,-467.03 C-121.8,-467.03 -113.55,-463.45 -113.55,-463.45 C-113.55,-463.45 -105.31,-459.87 -105.31,-459.87 C-105.31,-459.87 -96.97,-456.53 -96.97,-456.53 C-96.97,-456.53 -88.56,-453.37 -88.56,-453.37 C-88.56,-453.37 -80.15,-450.2 -80.15,-450.2 C-80.15,-450.2 -71.53,-447.68 -71.53,-447.68 C-71.53,-447.68 -62.85,-445.36 -62.85,-445.36 C-62.85,-445.36 -54.17,-443.04 -54.17,-443.04 C-54.17,-443.04 -45.31,-441.54 -45.31,-441.54 C-45.31,-441.54 -36.44,-440.09 -36.44,-440.09 C-36.44,-440.09 -27.56,-438.78 -27.56,-438.78 C-27.56,-438.78 -18.59,-438.19 -18.59,-438.19 C-18.59,-438.19 -9.62,-437.61 -9.62,-437.61 C-9.62,-437.61 -0.65,-437.37 -0.65,-437.37c " />
+                </group>
+            </group>
+            <group android:name="time_group" />
+        </vector>
+    </aapt:attr>
+</animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_media_pause_button.xml b/packages/SystemUI/res/drawable/ic_media_pause_button.xml
new file mode 100644
index 0000000..6ae89f9
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_media_pause_button.xml
@@ -0,0 +1,135 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2025 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:aapt="http://schemas.android.com/aapt">
+    <target android:name="_R_G_L_1_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="pathData"
+                    android:startOffset="0"
+                    android:valueFrom="M-5.06 -18 C-5.06,-18 -5.06,-1.24 -5.06,-1.24 C-5.06,-1.24 -5.06,17.7 -5.06,17.7 C-5.06,19.36 -6.41,20.7 -8.06,20.7 C-8.06,20.7 -16,20.7 -16,20.7 C-17.66,20.7 -19,19.36 -19,17.7 C-19,17.7 -19,-18 -19,-18 C-19,-19.66 -17.66,-21 -16,-21 C-16,-21 -8.06,-21 -8.06,-21 C-6.41,-21 -5.06,-19.66 -5.06,-18c "
+                    android:valueTo="M-4.69 -16.69 C-4.69,-16.69 20.25,-1.25 20.31,0.25 C20.38,1.75 -4.88,16.89 -4.88,16.89 C-6.94,18.25 -8.56,19.4 -9.75,19.58 C-10.94,19.75 -12.19,18.94 -12.12,16.14 C-12.09,14.76 -12.12,15.92 -12.12,14.26 C-12.12,14.26 -11.94,-16.44 -11.94,-16.44 C-11.94,-18.09 -12.09,-19.69 -10.44,-19.69 C-10.44,-19.69 -9.5,-19.56 -9.5,-19.56 C-8.62,-19.12 -6.19,-17.44 -4.69,-16.69c "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.449,0 0,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="pathData"
+                    android:startOffset="0"
+                    android:valueFrom="M-5.06 -18 C-5.06,-18 -5.06,-0.75 -5.06,-0.75 C-5.06,-0.75 -5.06,17.7 -5.06,17.7 C-5.06,19.36 -6.41,20.7 -8.06,20.7 C-8.06,20.7 -16,20.7 -16,20.7 C-17.66,20.7 -19,19.36 -19,17.7 C-19,17.7 -19,-18 -19,-18 C-19,-19.66 -17.66,-21 -16,-21 C-16,-21 -8.06,-21 -8.06,-21 C-6.41,-21 -5.06,-19.66 -5.06,-18c "
+                    android:valueTo="M-4.69 -16.69 C-4.69,-16.69 20.25,-1.25 20.31,0.25 C20.38,1.75 -4.88,16.89 -4.88,16.89 C-6.94,18.25 -8.56,19.4 -9.75,19.58 C-10.94,19.75 -12.19,18.94 -12.12,16.14 C-12.09,14.76 -12.12,15.92 -12.12,14.26 C-12.12,14.26 -11.94,-16.44 -11.94,-16.44 C-11.94,-18.09 -12.09,-19.69 -10.44,-19.69 C-10.44,-19.69 -9.5,-19.56 -9.5,-19.56 C-8.62,-19.12 -6.19,-17.44 -4.69,-16.69c "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.449,0 0,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_T_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="56"
+                    android:propertyName="translateX"
+                    android:startOffset="0"
+                    android:valueFrom="15.485"
+                    android:valueTo="12.321"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.3,0 0.8,0.15 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="278"
+                    android:propertyName="translateX"
+                    android:startOffset="56"
+                    android:valueFrom="12.321"
+                    android:valueTo="7.576"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.05,0.7 0.1,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="time_group">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="517"
+                    android:propertyName="translateX"
+                    android:startOffset="0"
+                    android:valueFrom="0"
+                    android:valueTo="1"
+                    android:valueType="floatType" />
+            </set>
+        </aapt:attr>
+    </target>
+    <aapt:attr name="android:drawable">
+        <vector
+            android:width="24dp"
+            android:height="24dp"
+            android:viewportHeight="24"
+            android:viewportWidth="24">
+            <group android:name="_R_G">
+                <group
+                    android:name="_R_G_L_1_G"
+                    android:pivotX="-12.031"
+                    android:scaleX="0.33299999999999996"
+                    android:scaleY="0.33299999999999996"
+                    android:translateX="19.524"
+                    android:translateY="12.084">
+                    <path
+                        android:name="_R_G_L_1_G_D_0_P_0"
+                        android:fillAlpha="1"
+                        android:fillColor="#ffffff"
+                        android:fillType="nonZero"
+                        android:pathData=" M-5.06 -18 C-5.06,-18 -5.06,-1.24 -5.06,-1.24 C-5.06,-1.24 -5.06,17.7 -5.06,17.7 C-5.06,19.36 -6.41,20.7 -8.06,20.7 C-8.06,20.7 -16,20.7 -16,20.7 C-17.66,20.7 -19,19.36 -19,17.7 C-19,17.7 -19,-18 -19,-18 C-19,-19.66 -17.66,-21 -16,-21 C-16,-21 -8.06,-21 -8.06,-21 C-6.41,-21 -5.06,-19.66 -5.06,-18c " />
+                </group>
+                <group
+                    android:name="_R_G_L_0_G_T_1"
+                    android:scaleX="0.33299999999999996"
+                    android:scaleY="0.33299999999999996"
+                    android:translateX="15.485"
+                    android:translateY="12.084">
+                    <group
+                        android:name="_R_G_L_0_G"
+                        android:translateX="12.031">
+                        <path
+                            android:name="_R_G_L_0_G_D_0_P_0"
+                            android:fillAlpha="1"
+                            android:fillColor="#ffffff"
+                            android:fillType="nonZero"
+                            android:pathData=" M-5.06 -18 C-5.06,-18 -5.06,-0.75 -5.06,-0.75 C-5.06,-0.75 -5.06,17.7 -5.06,17.7 C-5.06,19.36 -6.41,20.7 -8.06,20.7 C-8.06,20.7 -16,20.7 -16,20.7 C-17.66,20.7 -19,19.36 -19,17.7 C-19,17.7 -19,-18 -19,-18 C-19,-19.66 -17.66,-21 -16,-21 C-16,-21 -8.06,-21 -8.06,-21 C-6.41,-21 -5.06,-19.66 -5.06,-18c " />
+                    </group>
+                </group>
+            </group>
+            <group android:name="time_group" />
+        </vector>
+    </aapt:attr>
+</animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_media_pause_button_container.xml b/packages/SystemUI/res/drawable/ic_media_pause_button_container.xml
new file mode 100644
index 0000000..571f69d
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_media_pause_button_container.xml
@@ -0,0 +1,135 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2025 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:aapt="http://schemas.android.com/aapt">
+    <aapt:attr name="android:drawable">
+        <vector
+            android:width="88dp"
+            android:height="56dp"
+            android:viewportHeight="56"
+            android:viewportWidth="88">
+            <group android:name="_R_G">
+                <group
+                    android:name="_R_G_L_0_G"
+                    android:pivotX="0.493"
+                    android:pivotY="0.124"
+                    android:scaleX="1.05905"
+                    android:scaleY="1.0972"
+                    android:translateX="43.528999999999996"
+                    android:translateY="27.898">
+                    <path
+                        android:name="_R_G_L_0_G_D_0_P_0"
+                        android:fillAlpha="1"
+                        android:fillColor="#3d90ff"
+                        android:fillType="nonZero"
+                        android:pathData=" M34.49 -5.75 C34.49,-5.75 34.49,6 34.49,6 C34.49,14.84 27.32,22 18.49,22 C18.49,22 -17.5,22 -17.5,22 C-26.34,22 -33.5,14.84 -33.5,6 C-33.5,6 -33.5,-5.75 -33.5,-5.75 C-33.5,-14.59 -26.34,-21.75 -17.5,-21.75 C-17.5,-21.75 18.49,-21.75 18.49,-21.75 C27.32,-21.75 34.49,-14.59 34.49,-5.75c " />
+                </group>
+            </group>
+            <group android:name="time_group" />
+        </vector>
+    </aapt:attr>
+    <target android:name="_R_G_L_0_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="133"
+                    android:propertyName="pathData"
+                    android:startOffset="0"
+                    android:valueFrom="M34.49 -5.75 C34.49,-5.75 34.49,6 34.49,6 C34.49,14.84 27.32,22 18.49,22 C18.49,22 -17.5,22 -17.5,22 C-26.34,22 -33.5,14.84 -33.5,6 C-33.5,6 -33.5,-5.75 -33.5,-5.75 C-33.5,-14.59 -26.34,-21.75 -17.5,-21.75 C-17.5,-21.75 18.49,-21.75 18.49,-21.75 C27.32,-21.75 34.49,-14.59 34.49,-5.75c "
+                    android:valueTo="M34.49 -5.75 C34.49,-5.75 34.49,6 34.49,6 C34.49,14.84 27.32,22 18.49,22 C18.49,22 -17.5,22 -17.5,22 C-26.34,22 -33.5,14.84 -33.5,6 C-33.5,6 -33.5,-5.75 -33.5,-5.75 C-33.5,-14.59 -26.34,-21.75 -17.5,-21.75 C-17.5,-21.75 18.49,-21.75 18.49,-21.75 C27.32,-21.75 34.49,-14.59 34.49,-5.75c "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.473,0 0.065,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="367"
+                    android:propertyName="pathData"
+                    android:startOffset="133"
+                    android:valueFrom="M34.49 -5.75 C34.49,-5.75 34.49,6 34.49,6 C34.49,14.84 27.32,22 18.49,22 C18.49,22 -17.5,22 -17.5,22 C-26.34,22 -33.5,14.84 -33.5,6 C-33.5,6 -33.5,-5.75 -33.5,-5.75 C-33.5,-14.59 -26.34,-21.75 -17.5,-21.75 C-17.5,-21.75 18.49,-21.75 18.49,-21.75 C27.32,-21.75 34.49,-14.59 34.49,-5.75c "
+                    android:valueTo="M34.47 0.63 C34.47,0.63 34.42,0.64 34.42,0.64 C33.93,12.88 24.69,21.84 13.06,21.97 C13.06,21.97 -12.54,21.97 -12.54,21.97 C-23.11,21.84 -33.38,13.11 -33.52,-0.27 C-33.52,-0.27 -33.52,-0.05 -33.52,-0.05 C-33.5,-13.21 -21.73,-21.76 -12.9,-21.76 C-12.9,-21.76 14.59,-21.76 14.59,-21.76 C24.81,-21.88 34.49,-10.58 34.47,0.63c "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.473,0 0.065,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="167"
+                    android:propertyName="scaleX"
+                    android:startOffset="0"
+                    android:valueFrom="1.05905"
+                    android:valueTo="1.17758"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.226,0 0.667,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="167"
+                    android:propertyName="scaleY"
+                    android:startOffset="0"
+                    android:valueFrom="1.0972"
+                    android:valueTo="1.22"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.226,0 0.667,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="scaleX"
+                    android:startOffset="167"
+                    android:valueFrom="1.17758"
+                    android:valueTo="1.05905"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.213,0 0.248,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="scaleY"
+                    android:startOffset="167"
+                    android:valueFrom="1.22"
+                    android:valueTo="1.0972"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.213,0 0.248,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="time_group">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="517"
+                    android:propertyName="translateX"
+                    android:startOffset="0"
+                    android:valueFrom="0"
+                    android:valueTo="1"
+                    android:valueType="floatType" />
+            </set>
+        </aapt:attr>
+    </target>
+</animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_media_play_button.xml b/packages/SystemUI/res/drawable/ic_media_play_button.xml
new file mode 100644
index 0000000..f646902
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_media_play_button.xml
@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2025 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:aapt="http://schemas.android.com/aapt">
+    <target android:name="_R_G_L_1_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="pathData"
+                    android:startOffset="0"
+                    android:valueFrom="M-4.69 -16.69 C-4.69,-16.69 20.25,-1.25 20.31,0.25 C20.38,1.75 -4.88,16.89 -4.88,16.89 C-6.94,18.25 -8.56,19.4 -9.75,19.58 C-10.94,19.75 -12.19,18.94 -12.12,16.14 C-12.09,14.76 -12.12,15.92 -12.12,14.26 C-12.12,14.26 -11.94,-16.44 -11.94,-16.44 C-11.94,-18.09 -12.09,-19.69 -10.44,-19.69 C-10.44,-19.69 -9.5,-19.56 -9.5,-19.56 C-8.62,-19.12 -6.19,-17.44 -4.69,-16.69c "
+                    android:valueTo="M-5.06 -18 C-5.06,-18 -5.06,-1.24 -5.06,-1.24 C-5.06,-1.24 -5.06,17.7 -5.06,17.7 C-5.06,19.36 -6.41,20.7 -8.06,20.7 C-8.06,20.7 -16,20.7 -16,20.7 C-17.66,20.7 -19,19.36 -19,17.7 C-19,17.7 -19,-18 -19,-18 C-19,-19.66 -17.66,-21 -16,-21 C-16,-21 -8.06,-21 -8.06,-21 C-6.41,-21 -5.06,-19.66 -5.06,-18c "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.433,0 0,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="pathData"
+                    android:startOffset="0"
+                    android:valueFrom="M-4.69 -16.69 C-4.69,-16.69 20.25,-1.25 20.31,0.25 C20.38,1.75 -4.88,16.89 -4.88,16.89 C-6.94,18.25 -8.56,19.4 -9.75,19.58 C-10.94,19.75 -12.19,18.94 -12.12,16.14 C-12.09,14.76 -12.12,15.92 -12.12,14.26 C-12.12,14.26 -11.94,-16.44 -11.94,-16.44 C-11.94,-18.09 -12.09,-19.69 -10.44,-19.69 C-10.44,-19.69 -9.5,-19.56 -9.5,-19.56 C-8.62,-19.12 -6.19,-17.44 -4.69,-16.69c "
+                    android:valueTo="M-5.06 -18 C-5.06,-18 -5.06,-0.75 -5.06,-0.75 C-5.06,-0.75 -5.06,17.7 -5.06,17.7 C-5.06,19.36 -6.41,20.7 -8.06,20.7 C-8.06,20.7 -16,20.7 -16,20.7 C-17.66,20.7 -19,19.36 -19,17.7 C-19,17.7 -19,-18 -19,-18 C-19,-19.66 -17.66,-21 -16,-21 C-16,-21 -8.06,-21 -8.06,-21 C-6.41,-21 -5.06,-19.66 -5.06,-18c "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.433,0 0,1 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G_T_1">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="333"
+                    android:propertyName="translateX"
+                    android:startOffset="0"
+                    android:valueFrom="7.576"
+                    android:valueTo="15.485"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.583,0 0.089,0.874 1.0,1.0" />
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="time_group">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:duration="517"
+                    android:propertyName="translateX"
+                    android:startOffset="0"
+                    android:valueFrom="0"
+                    android:valueTo="1"
+                    android:valueType="floatType" />
+            </set>
+        </aapt:attr>
+    </target>
+    <aapt:attr name="android:drawable">
+        <vector
+            android:width="24dp"
+            android:height="24dp"
+            android:viewportHeight="24"
+            android:viewportWidth="24">
+            <group android:name="_R_G">
+                <group
+                    android:name="_R_G_L_1_G"
+                    android:pivotX="-12.031"
+                    android:scaleX="0.33299999999999996"
+                    android:scaleY="0.33299999999999996"
+                    android:translateX="19.524"
+                    android:translateY="12.084">
+                    <path
+                        android:name="_R_G_L_1_G_D_0_P_0"
+                        android:fillAlpha="1"
+                        android:fillColor="#ffffff"
+                        android:fillType="nonZero"
+                        android:pathData=" M-4.69 -16.69 C-4.69,-16.69 20.25,-1.25 20.31,0.25 C20.38,1.75 -4.88,16.89 -4.88,16.89 C-6.94,18.25 -8.56,19.4 -9.75,19.58 C-10.94,19.75 -12.19,18.94 -12.12,16.14 C-12.09,14.76 -12.12,15.92 -12.12,14.26 C-12.12,14.26 -11.94,-16.44 -11.94,-16.44 C-11.94,-18.09 -12.09,-19.69 -10.44,-19.69 C-10.44,-19.69 -9.5,-19.56 -9.5,-19.56 C-8.62,-19.12 -6.19,-17.44 -4.69,-16.69c " />
+                </group>
+                <group
+                    android:name="_R_G_L_0_G_T_1"
+                    android:scaleX="0.33299999999999996"
+                    android:scaleY="0.33299999999999996"
+                    android:translateX="7.576"
+                    android:translateY="12.084">
+                    <group
+                        android:name="_R_G_L_0_G"
+                        android:translateX="12.031">
+                        <path
+                            android:name="_R_G_L_0_G_D_0_P_0"
+                            android:fillAlpha="1"
+                            android:fillColor="#ffffff"
+                            android:fillType="nonZero"
+                            android:pathData=" M-4.69 -16.69 C-4.69,-16.69 20.25,-1.25 20.31,0.25 C20.38,1.75 -4.88,16.89 -4.88,16.89 C-6.94,18.25 -8.56,19.4 -9.75,19.58 C-10.94,19.75 -12.19,18.94 -12.12,16.14 C-12.09,14.76 -12.12,15.92 -12.12,14.26 C-12.12,14.26 -11.94,-16.44 -11.94,-16.44 C-11.94,-18.09 -12.09,-19.69 -10.44,-19.69 C-10.44,-19.69 -9.5,-19.56 -9.5,-19.56 C-8.62,-19.12 -6.19,-17.44 -4.69,-16.69c " />
+                    </group>
+                </group>
+            </group>
+            <group android:name="time_group" />
+        </vector>
+    </aapt:attr>
+</animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_media_play_button_container.xml b/packages/SystemUI/res/drawable/ic_media_play_button_container.xml
new file mode 100644
index 0000000..aa4e09fa
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_media_play_button_container.xml
@@ -0,0 +1,135 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+  ~ Copyright (C) 2025 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:aapt="http://schemas.android.com/aapt">
+    <aapt:attr name="android:drawable">
+        <vector
+            android:height="56dp"
+            android:width="88dp"
+            android:viewportHeight="56"
+            android:viewportWidth="88">
+            <group android:name="_R_G">
+                <group
+                    android:name="_R_G_L_0_G"
+                    android:translateX="43.528999999999996"
+                    android:translateY="27.898"
+                    android:pivotX="0.493"
+                    android:pivotY="0.124"
+                    android:scaleX="1.05905"
+                    android:scaleY="1.0972">
+                    <path
+                        android:name="_R_G_L_0_G_D_0_P_0"
+                        android:fillColor="#3d90ff"
+                        android:fillAlpha="1"
+                        android:fillType="nonZero"
+                        android:pathData=" M34.47 0.63 C34.47,0.63 34.42,0.64 34.42,0.64 C33.93,12.88 24.69,21.84 13.06,21.97 C13.06,21.97 -12.54,21.97 -12.54,21.97 C-23.11,21.84 -33.38,13.11 -33.52,-0.27 C-33.52,-0.27 -33.52,-0.05 -33.52,-0.05 C-33.5,-13.21 -21.73,-21.76 -12.9,-21.76 C-12.9,-21.76 14.59,-21.76 14.59,-21.76 C24.81,-21.88 34.49,-10.58 34.47,0.63c "/>
+                </group>
+            </group>
+            <group android:name="time_group"/>
+        </vector>
+    </aapt:attr>
+    <target android:name="_R_G_L_0_G_D_0_P_0">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:propertyName="pathData"
+                    android:duration="167"
+                    android:startOffset="0"
+                    android:valueFrom="M34.47 0.63 C34.47,0.63 34.42,0.64 34.42,0.64 C33.93,12.88 24.69,21.84 13.06,21.97 C13.06,21.97 -12.54,21.97 -12.54,21.97 C-23.11,21.84 -33.38,13.11 -33.52,-0.27 C-33.52,-0.27 -33.52,-0.05 -33.52,-0.05 C-33.5,-13.21 -21.73,-21.76 -12.9,-21.76 C-12.9,-21.76 14.59,-21.76 14.59,-21.76 C24.81,-21.88 34.49,-10.58 34.47,0.63c "
+                    android:valueTo="M34.47 0.63 C34.47,0.63 34.42,0.64 34.42,0.64 C33.93,12.88 24.69,21.84 13.06,21.97 C13.06,21.97 -12.54,21.97 -12.54,21.97 C-23.11,21.84 -33.38,13.11 -33.52,-0.27 C-33.52,-0.27 -33.52,-0.05 -33.52,-0.05 C-33.5,-13.21 -21.73,-21.76 -12.9,-21.76 C-12.9,-21.76 14.59,-21.76 14.59,-21.76 C24.81,-21.88 34.49,-10.58 34.47,0.63c "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.493,0 0,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:propertyName="pathData"
+                    android:duration="333"
+                    android:startOffset="167"
+                    android:valueFrom="M34.47 0.63 C34.47,0.63 34.42,0.64 34.42,0.64 C33.93,12.88 24.69,21.84 13.06,21.97 C13.06,21.97 -12.54,21.97 -12.54,21.97 C-23.11,21.84 -33.38,13.11 -33.52,-0.27 C-33.52,-0.27 -33.52,-0.05 -33.52,-0.05 C-33.5,-13.21 -21.73,-21.76 -12.9,-21.76 C-12.9,-21.76 14.59,-21.76 14.59,-21.76 C24.81,-21.88 34.49,-10.58 34.47,0.63c "
+                    android:valueTo="M34.49 -5.75 C34.49,-5.75 34.49,6 34.49,6 C34.49,14.84 27.32,22 18.49,22 C18.49,22 -17.5,22 -17.5,22 C-26.34,22 -33.5,14.84 -33.5,6 C-33.5,6 -33.5,-5.75 -33.5,-5.75 C-33.5,-14.59 -26.34,-21.75 -17.5,-21.75 C-17.5,-21.75 18.49,-21.75 18.49,-21.75 C27.32,-21.75 34.49,-14.59 34.49,-5.75c "
+                    android:valueType="pathType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.493,0 0,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="_R_G_L_0_G">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:propertyName="scaleX"
+                    android:duration="167"
+                    android:startOffset="0"
+                    android:valueFrom="1.05905"
+                    android:valueTo="1.17758"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.226,0 0.667,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:propertyName="scaleY"
+                    android:duration="167"
+                    android:startOffset="0"
+                    android:valueFrom="1.0972"
+                    android:valueTo="1.22"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.226,0 0.667,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:propertyName="scaleX"
+                    android:duration="333"
+                    android:startOffset="167"
+                    android:valueFrom="1.17758"
+                    android:valueTo="1.05905"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.213,0 0.248,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+                <objectAnimator
+                    android:propertyName="scaleY"
+                    android:duration="333"
+                    android:startOffset="167"
+                    android:valueFrom="1.22"
+                    android:valueTo="1.0972"
+                    android:valueType="floatType">
+                    <aapt:attr name="android:interpolator">
+                        <pathInterpolator android:pathData="M 0.0,0.0 c0.213,0 0.248,1 1.0,1.0"/>
+                    </aapt:attr>
+                </objectAnimator>
+            </set>
+        </aapt:attr>
+    </target>
+    <target android:name="time_group">
+        <aapt:attr name="android:animation">
+            <set android:ordering="together">
+                <objectAnimator
+                    android:propertyName="translateX"
+                    android:duration="517"
+                    android:startOffset="0"
+                    android:valueFrom="0"
+                    android:valueTo="1"
+                    android:valueType="floatType"/>
+            </set>
+        </aapt:attr>
+    </target>
+</animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/volume_dialog_background.xml b/packages/SystemUI/res/drawable/volume_dialog_background.xml
index 25d78e3..cd0e2c0 100644
--- a/packages/SystemUI/res/drawable/volume_dialog_background.xml
+++ b/packages/SystemUI/res/drawable/volume_dialog_background.xml
@@ -17,6 +17,6 @@
 <shape xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     android:shape="rectangle">
-    <corners android:radius="@dimen/volume_dialog_background_corner_radius" />
+    <corners android:radius="@dimen/volume_dialog_background_corner_radius"/>
     <solid android:color="@androidprv:color/materialColorSurface" />
 </shape>
diff --git a/packages/SystemUI/res/drawable/volume_dialog_floating_slider_background.xml b/packages/SystemUI/res/drawable/volume_dialog_floating_slider_background.xml
index 2694435..d7607cc 100644
--- a/packages/SystemUI/res/drawable/volume_dialog_floating_slider_background.xml
+++ b/packages/SystemUI/res/drawable/volume_dialog_floating_slider_background.xml
@@ -17,5 +17,5 @@
     xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
     android:shape="rectangle">
     <corners android:radius="20dp" />
-    <solid android:color="?androidprv:attr/colorSurface" />
+    <solid android:color="@androidprv:color/materialColorSurface" />
 </shape>
diff --git a/packages/SystemUI/res/drawable/volume_dialog_ringer_background.xml b/packages/SystemUI/res/drawable/volume_dialog_ringer_background.xml
new file mode 100644
index 0000000..6a706f7
--- /dev/null
+++ b/packages/SystemUI/res/drawable/volume_dialog_ringer_background.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+    Copyright (C) 2024 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+    android:shape="rectangle">
+    <corners android:radius="@dimen/volume_dialog_background_corner_radius"/>
+    <solid android:color="@androidprv:color/materialColorSurface" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/accessibility_floating_menu_item.xml b/packages/SystemUI/res/layout/accessibility_floating_menu_item.xml
index 2067f85..b1e4272 100644
--- a/packages/SystemUI/res/layout/accessibility_floating_menu_item.xml
+++ b/packages/SystemUI/res/layout/accessibility_floating_menu_item.xml
@@ -15,18 +15,60 @@
     limitations under the License.
 -->
 
-<LinearLayout
+<androidx.constraintlayout.widget.ConstraintLayout
     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="wrap_content"
     android:paddingStart="@dimen/accessibility_floating_menu_small_padding"
     android:paddingEnd="@dimen/accessibility_floating_menu_small_padding"
-    android:orientation="vertical"
-    android:gravity="center">
+    android:orientation="vertical">
 
     <View
         android:id="@+id/icon_view"
         android:layout_width="@dimen/accessibility_floating_menu_small_width_height"
-        android:layout_height="@dimen/accessibility_floating_menu_small_width_height"/>
+        android:layout_height="@dimen/accessibility_floating_menu_small_width_height"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintTop_toTopOf="parent"/>
 
-</LinearLayout>
\ No newline at end of file
+    <androidx.constraintlayout.widget.Guideline
+        android:id="@+id/app_icon_constraint_right_badge_vertical"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        app:layout_constraintGuide_percent="@dimen/accessibility_floating_menu_badge_position" />
+    <androidx.constraintlayout.widget.Guideline
+        android:id="@+id/app_icon_constraint_badge_horizontal"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        app:layout_constraintGuide_percent="@dimen/accessibility_floating_menu_badge_position" />
+    <androidx.constraintlayout.widget.Guideline
+        android:id="@+id/app_icon_constraint_left_badge_vertical"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        app:layout_constraintGuide_percent="@dimen/accessibility_floating_menu_left_badge_x_position" />
+
+    <View
+        android:id="@+id/right_badge_view"
+        android:layout_width="@dimen/accessibility_floating_menu_small_badge_width_height"
+        android:layout_height="@dimen/accessibility_floating_menu_small_badge_width_height"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintLeft_toLeftOf="@id/app_icon_constraint_right_badge_vertical"
+        app:layout_constraintRight_toRightOf="parent"
+        app:layout_constraintTop_toTopOf="@id/app_icon_constraint_badge_horizontal"
+        android:visibility="invisible" />
+
+    <View
+        android:id="@+id/left_badge_view"
+        android:layout_width="@dimen/accessibility_floating_menu_small_badge_width_height"
+        android:layout_height="@dimen/accessibility_floating_menu_small_badge_width_height"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintLeft_toLeftOf="@id/app_icon_constraint_left_badge_vertical"
+        app:layout_constraintRight_toRightOf="parent"
+        app:layout_constraintTop_toTopOf="@id/app_icon_constraint_badge_horizontal"
+        android:visibility="invisible" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/volume_dialog.xml b/packages/SystemUI/res/layout/volume_dialog.xml
index bad5711..58f2d3c 100644
--- a/packages/SystemUI/res/layout/volume_dialog.xml
+++ b/packages/SystemUI/res/layout/volume_dialog.xml
@@ -19,31 +19,32 @@
     android:id="@+id/volume_dialog_root"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
+    android:clipChildren="false"
     app:layoutDescription="@xml/volume_dialog_scene">
 
     <View
         android:id="@+id/volume_dialog_background"
         android:layout_width="@dimen/volume_dialog_width"
         android:layout_height="0dp"
-        android:layout_marginTop="@dimen/volume_dialog_background_vertical_margin"
+        android:layout_marginTop="@dimen/volume_dialog_background_top_margin"
         android:layout_marginBottom="@dimen/volume_dialog_background_vertical_margin"
         android:background="@drawable/volume_dialog_background"
         app:layout_constraintBottom_toBottomOf="@id/volume_dialog_settings"
         app:layout_constraintEnd_toEndOf="@id/volume_dialog_main_slider_container"
         app:layout_constraintStart_toStartOf="@id/volume_dialog_main_slider_container"
-        app:layout_constraintTop_toTopOf="@id/volume_ringer_drawer" />
+        app:layout_constraintTop_toTopOf="@id/volume_dialog_main_slider_container" />
 
     <include
         android:id="@id/volume_ringer_drawer"
         layout="@layout/volume_ringer_drawer"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
+        android:layout_width="0dp"
+        android:layout_height="0dp"
+        android:layout_marginEnd="@dimen/volume_dialog_ringer_drawer_diff_end_margin"
         android:layout_marginBottom="@dimen/volume_dialog_components_spacing"
         app:layout_constraintBottom_toTopOf="@id/volume_dialog_main_slider_container"
         app:layout_constraintEnd_toEndOf="@id/volume_dialog_main_slider_container"
-        app:layout_constraintStart_toStartOf="@id/volume_dialog_main_slider_container"
-        app:layout_constraintTop_toTopOf="parent"
-        app:layout_constraintVertical_bias="1" />
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="parent" />
 
     <include
         android:id="@+id/volume_dialog_main_slider_container"
diff --git a/packages/SystemUI/res/layout/volume_dialog_slider.xml b/packages/SystemUI/res/layout/volume_dialog_slider.xml
index 0acf410..6eb7b73 100644
--- a/packages/SystemUI/res/layout/volume_dialog_slider.xml
+++ b/packages/SystemUI/res/layout/volume_dialog_slider.xml
@@ -14,15 +14,18 @@
      limitations under the License.
 -->
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content">
+    android:layout_width="0dp"
+    android:layout_height="0dp"
+    android:maxHeight="@dimen/volume_dialog_slider_height">
 
     <com.google.android.material.slider.Slider
         android:id="@+id/volume_dialog_slider"
         style="@style/SystemUI.Material3.Slider.Volume"
-        android:layout_width="@dimen/volume_dialog_slider_width"
-        android:layout_height="@dimen/volume_dialog_slider_height"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
         android:layout_gravity="center"
+        android:layout_marginTop="-20dp"
+        android:layout_marginBottom="-20dp"
         android:orientation="vertical"
         android:theme="@style/Theme.Material3.DayNight" />
 </FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/volume_ringer_button.xml b/packages/SystemUI/res/layout/volume_ringer_button.xml
index e65d0b9..6748cfa 100644
--- a/packages/SystemUI/res/layout/volume_ringer_button.xml
+++ b/packages/SystemUI/res/layout/volume_ringer_button.xml
@@ -20,10 +20,9 @@
 
     <ImageButton
         android:id="@+id/volume_drawer_button"
-        android:layout_width="@dimen/volume_dialog_ringer_drawer_button_size"
-        android:layout_height="@dimen/volume_dialog_ringer_drawer_button_size"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
         android:padding="@dimen/volume_dialog_ringer_drawer_button_icon_radius"
-        android:layout_marginBottom="@dimen/volume_dialog_components_spacing"
         android:contentDescription="@string/volume_ringer_mode"
         android:gravity="center"
         android:tint="@androidprv:color/materialColorOnSurface"
diff --git a/packages/SystemUI/res/layout/volume_ringer_drawer.xml b/packages/SystemUI/res/layout/volume_ringer_drawer.xml
index cd8f18f..8f51dbc 100644
--- a/packages/SystemUI/res/layout/volume_ringer_drawer.xml
+++ b/packages/SystemUI/res/layout/volume_ringer_drawer.xml
@@ -18,13 +18,22 @@
     xmlns:app="http://schemas.android.com/apk/res-auto"
     android:id="@+id/volume_ringer_drawer"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content"
+    android:layout_height="match_parent"
     android:clipChildren="false"
     android:clipToPadding="false"
     android:gravity="center"
     android:layoutDirection="ltr"
     app:layoutDescription="@xml/volume_dialog_ringer_drawer_motion_scene">
 
+    <View
+        android:id="@+id/ringer_buttons_background"
+        android:layout_width="@dimen/volume_dialog_width"
+        android:layout_height="0dp"
+        android:visibility="gone"
+        android:layout_marginTop="@dimen/volume_dialog_background_vertical_margin"
+        android:layout_marginBottom="@dimen/volume_dialog_background_vertical_margin"
+        android:background="@drawable/volume_dialog_ringer_background" />
+
     <!-- add ringer buttons here -->
 
 </androidx.constraintlayout.motion.widget.MotionLayout>
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index ebc1935..0e8b2d5 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Deurlopende kennisgewing vir \'n skermopnamesessie"</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>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"Neem hierdie skerm op"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"Neem %s 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>
@@ -417,20 +415,13 @@
     <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="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Gekies"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Omgewing"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Links"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Regs"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Vou uit na links- en regsgeskeide kontroles"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Vou in na verenigde kontrole"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Demp omgewing"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Ontdemp omgewing"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Nutsgoed"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Intydse Onderskrifte"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Nota"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"Wys laeprioriteit-kennisgewingikone"</string>
     <string name="other" msgid="429768510980739978">"Ander"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"verwyder teël"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"voeg teël by die laaste posisie"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Skuif teël"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Voeg teël by die verlangde posisie"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Skuif na <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Voeg by posisie <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Posisie is ongeldig."</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"kies gebruiker"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"Geen internet nie"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"Maak <xliff:g id="ID_1">%s</xliff:g>-instellings oop."</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"Wysig volgorde van Kitsinstellings."</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Aan/af-kieslys"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Bladsy <xliff:g id="ID_1">%1$d</xliff:g> van <xliff:g id="ID_2">%2$d</xliff:g>"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"Sluitskerm"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"gaan by toestel in"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Gebruik vingerafdruk om oop te maak"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Stawing word vereis. Raak die vingerafdruksensor om te staaf."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Oproep aan die gang"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobiele data"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Gekoppel"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Tydelik gekoppel"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 18bffac..15286a6 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"ለአንድ የማያ ገፅ ቀረጻ ክፍለ-ጊዜ በመካሄድ ያለ ማሳወቂያ"</string>
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"ማያ ገፅዎን ይቀዳሉ?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"አንድ መተግበሪያ ቅዳ"</string>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"ይህን ማያ ገፅ ይቅዱ"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"%s ይቅዱ"</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>
@@ -417,20 +415,13 @@
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"ቅድመ-ቅምጥን ማዘመን አልተቻለም"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"ቅድመ-ቅምጥ"</string>
     <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"ተመርጧል"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"በዙሪያ ያሉ"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"ግራ"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"ቀኝ"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"ወደ ግራ እና ቀኝ የተለያዩ ቁጥጥሮች ዘርጋ"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"ወደ የተዋሃደ ቁጥጥር ሰብስብ"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"በዙሪያ ያሉትን ድምፀ-ከል አድርግ"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"በዙሪያ ያሉትን ድምፅ-ከል አንሳ"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"መሣሪያዎች"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"የቀጥታ መግለጫ ጽሑፍ"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"ማስታወሻ"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"አነስተኛ ቅድሚያ ያላቸው የማሳወቂያ አዶዎችን አሳይ"</string>
     <string name="other" msgid="429768510980739978">"ሌላ"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ሰቅ አስወግድ"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"በመጨረሻው ቦታ ላይ ሰቅ ያክሉ"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ሰቁን ውሰድ"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"ወደተፈለገው ቦታ ሰቅ ያክሉ"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"ወደ <xliff:g id="POSITION">%1$d</xliff:g> ውሰድ"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"ወደ <xliff:g id="POSITION">%1$d</xliff:g> ቦታ አክል"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"አቀማመጡ ተቀባይነት የለውም።"</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"ተጠቃሚ ይምረጡ"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"ምንም በይነመረብ የለም"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"የ<xliff:g id="ID_1">%s</xliff:g> ቅንብሮችን ክፈት።"</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"የፈጣን ቅንብሮችን ቅደም ተከተል ያርትዑ።"</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"የኃይል ምናሌ"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"ገፅ <xliff:g id="ID_1">%1$d</xliff:g> ከ <xliff:g id="ID_2">%2$d</xliff:g>"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"ማያ ገፅ ቁልፍ"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"መሣሪያን ያስገቡ"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"ለመክፈት የጣት አሻራ ይጠቀሙ"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"ማረጋገጥ ያስፈልጋል። ለማረጋገጥ የጣት አሻራ ዳሳሹን ይንኩ።"</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"በመካሄድ ላይ የስልክ ጥሪ"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"የተንቀሳቃሽ ስልክ ውሂብ"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"ተገናኝቷል"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"በጊዜያዊነት ተገናኝቷል"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 20668f8..d68c432 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"إشعار مستمر لجلسة تسجيل شاشة"</string>
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"هل تريد تسجيل الشاشة؟"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"تسجيل شاشة تطبيق واحد"</string>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"تسجيل محتوى هذه الشاشة"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"‏تسجيل محتوى \"%s\""</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>
@@ -417,20 +415,13 @@
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"تعذَّر تعديل الإعداد المسبق"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"الإعدادات المسبقة"</string>
     <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"تمّ اختياره"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"الأصوات المحيطة"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"الجهاز الأيسر"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"الجهاز الأيمن"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"توسيع لوحة التحكّم الموحّدة إلى عناصر تحكُّم منفصلة على اليسار واليمين"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"تصغير عناصر التحكّم في الصوت إلى لوحة تحكُّم موحّدة"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"كتم الأصوات المحيطة"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"إعادة الأصوات المحيطة"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"الأدوات"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"النسخ النصي التلقائي"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"ملاحظات"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"إظهار رموز الإشعارات ذات الأولوية المنخفضة"</string>
     <string name="other" msgid="429768510980739978">"غير ذلك"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"إزالة بطاقة"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"إضافة مربّع إلى الموضع الأخير"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"نقل بطاقة"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"إضافة مربّع إلى الموضع المطلوب"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"الانتقال إلى <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"الإضافة إلى الموضع <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"الموضِع غير صالح."</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"اختيار مستخدم"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"لا يتوفر اتصال إنترنت."</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"فتح إعدادات <xliff:g id="ID_1">%s</xliff:g>."</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"تعديل ترتيب الإعدادات السريعة"</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"قائمة زر التشغيل"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"الصفحة <xliff:g id="ID_1">%1$d</xliff:g> من <xliff:g id="ID_2">%2$d</xliff:g>"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"شاشة القفل"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"الدخول إلى الجهاز"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"يمكنك استخدام بصمة الإصبع للفتح"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"المصادقة مطلوبة. المس مستشعر بصمات الإصبع للمصادقة."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"مكالمة هاتفية جارية"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"بيانات الجوّال"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"متصلة بالإنترنت"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"متصلة مؤقتًا"</string>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index fb0030f..4c4579f 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"স্ক্রীন ৰেকৰ্ডিং ছেশ্বন চলি থকা সময়ত পোৱা জাননী"</string>
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"আপোনাৰ স্ক্ৰীনখন ৰেকৰ্ড কৰিবনে?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"এটা এপ্ ৰেকৰ্ড কৰক"</string>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"এই স্ক্ৰীনখন ৰেকর্ড কৰক"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"%s ৰেকর্ড কৰক"</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>
@@ -417,20 +415,13 @@
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"প্ৰিছেট আপডে’ট কৰিব পৰা নগ’ল"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"প্ৰিছেট"</string>
     <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"বাছনি কৰা হৈছে"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"আশ-পাশ"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"বাওঁফাল"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"সোঁফাল"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"বাওঁ আৰু সোঁফালৰ পৃথক কৰা নিয়ন্ত্ৰণলৈ সংকোচন কৰক"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"একত্ৰিত নিয়ন্ত্ৰণলৈ সংকোচন কৰক"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"আশ-পাশৰ ধ্বনি মিউট কৰক"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"আশ-পাশৰ ধ্বনি আনমিউট কৰক"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"সঁজুলি"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"লাইভ কেপশ্বন"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"টোকা"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"কম গুৰুত্বপূৰ্ণ জাননীৰ আইকনসমূহ দেখুৱাওক"</string>
     <string name="other" msgid="429768510980739978">"অন্যান্য"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"টাইল আঁতৰাবলৈ"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"অন্তিম স্থানত টাইল যোগ দিয়ক"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"টাইল স্থানান্তৰ কৰক"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"বিচৰা স্থানত টাইল যোগ দিয়ক"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> নম্বৰলৈ স্থানান্তৰ কৰক"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> নম্বৰ স্থানত যোগ দিয়ক"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"স্থান অমান্য।"</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"ব্যৱহাৰকাৰী বাছনি কৰক"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"ইণ্টাৰনেট সংযোগ নাই"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"<xliff:g id="ID_1">%s</xliff:g>ৰ ছেটিং খোলক।"</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"ক্ষিপ্ৰ ছেটিঙৰ ক্ৰম সম্পাদনা কৰক।"</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"পাৱাৰ মেনু"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_2">%2$d</xliff:g>ৰ পৃষ্ঠা <xliff:g id="ID_1">%1$d</xliff:g>"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"লক স্ক্ৰীন"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"ডিভাইচ আনলক কৰক"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"খুলিবলৈ ফিংগাৰপ্ৰিণ্ট ব্যৱহাৰ কৰক"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"বিশ্বাসযোগ্যতা প্ৰমাণীকৰণৰ আৱশ্যক। বিশ্বাসযোগ্যতা প্ৰমাণীকৰণ কৰিবলৈ ফিংগাৰপ্ৰিণ্ট ছেন্সৰটো স্পৰ্শ কৰক।"</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"চলি থকা ফ’ন কল"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"ম’বাইল ডেটা"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"সংযোজিত হৈ আছে"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"অস্থায়ীভাৱে সংযোগ কৰা হৈছে"</string>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index 532fceb..08452d35 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Ekranın video çəkimi ərzində silinməyən bildiriş"</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>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"Bu ekranı qeydə alın"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"Qeydə alın: %s"</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>
@@ -417,20 +415,13 @@
     <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="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Seçilib"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Ətraf mühit"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Sol"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Sağ"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Sola və sağa ayrılmış idarəetmələr üçün genişləndirin"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Vahid nəzarət üçün yığcamlaşdırın"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Ətraf mühiti səssiz edin"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Ətraf mühiti səssiz rejimdən çıxarın"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Alətlər"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Canlı Altyazı"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Qeyd"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"Aşağı prioritet bildiriş işarələrini göstərin"</string>
     <string name="other" msgid="429768510980739978">"Digər"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"lövhəni silin"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"son mövqeyə mozaik əlavə edin"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Lövhəni köçürün"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"İstənilən mövqeyə mozaik əlavə edin"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> mövqeyinə köçürün"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> mövqeyinə əlavə edin"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Mövqe yanlışdır."</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"istifadəçi seçin"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"İnternet yoxdur"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"<xliff:g id="ID_1">%s</xliff:g> ayarlarını açın."</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"Sürətli Ayarlarda ardıcıllığı redaktə edin."</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Qidalanma düyməsi menyusu"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_2">%2$d</xliff:g> səhifədən <xliff:g id="ID_1">%1$d</xliff:g> səhifə"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"Ekran kilidi"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"cihaz daxil edin"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Açmaq üçün barmaq izindən istifadə edin"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Doğrulanma tələb olunur. Doğrulamaq üçün barmaq izi sensoruna toxunun."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Davam edən zəng"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobil data"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Qoşulub"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Müvəqqəti qoşulub"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index d0c3fa3..8d9495a 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Obaveštenje o sesiji snimanja ekrana je aktivno"</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>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"Snimi ovaj ekran"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"Snimi %s"</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>
@@ -417,20 +415,13 @@
     <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="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Izabrano"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Okruženje"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Levo"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Desno"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Proširi na kontrole razdvojene na levu i desnu stranu"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Skupi u jedinstvenu kontrolu"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Isključi zvuk okruženja"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Uključi zvuk okruženja"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Alatke"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Titl uživo"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Beleška"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"Prikaži ikone obaveštenja niskog prioriteta"</string>
     <string name="other" msgid="429768510980739978">"Drugo"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"uklonili pločicu"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"dodali pločicu na poslednju poziciju"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Premestite pločicu"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Dodajte pločicu na željenu poziciju"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Premestite na <xliff:g id="POSITION">%1$d</xliff:g>. poziciju"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Dodajte na <xliff:g id="POSITION">%1$d</xliff:g>. poziciju"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Pozicija je nevažeća."</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"odabrali korisnika"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"Nema interneta"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"Otvori podešavanja za <xliff:g id="ID_1">%s</xliff:g>."</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"Izmenite redosled Brzih podešavanja."</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Meni dugmeta za uključivanje"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_1">%1$d</xliff:g>. strana od <xliff:g id="ID_2">%2$d</xliff:g>"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"Zaključan ekran"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"unesite uređaj"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Otvorite pomoću otiska prsta"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Potrebna je potvrda identiteta. Dodirnite senzor za otisak prsta da biste potvrdili identitet."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Aktuelni telefonski poziv"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobilni podaci"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Povezano"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Privremeno povezano"</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index b40801d..3431862 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Бягучае апавяшчэнне для сеанса запісу экрана"</string>
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Запісаць экран?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Запісаць адну праграму"</string>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"Запісаць гэты экран"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"Запісаць: %s"</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>
@@ -417,20 +415,13 @@
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Не ўдалося абнавіць набор налад"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Набор налад"</string>
     <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Выбрана"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Навакольныя гукі"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Левы бок"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Правы бок"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Зрабіць левую і правую панэлі кіравання"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Зрабіць адну панэль кіравання"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Выключыць навакольныя гукі"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Уключыць навакольныя гукі"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Інструменты"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Аўтаматычныя субцітры"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Нататка"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"Паказваць значкі апавяшчэнняў з нізкім прыярытэтам"</string>
     <string name="other" msgid="429768510980739978">"Іншае"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"выдаліць плітку"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"дадаць плітку ў апошнюю пазіцыю"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Перамясціць плітку"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Дадаць плітку ў патрэбную пазіцыю"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Перамясціць на пазіцыю <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Дадаць на пазіцыю <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Няправільнае месца."</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"выбраць карыстальніка"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"Няма падключэння да інтэрнэту"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"Адкрыць налады <xliff:g id="ID_1">%s</xliff:g>."</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"Змяніць парадак хуткіх налад."</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Меню кнопкі сілкавання"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Старонка <xliff:g id="ID_1">%1$d</xliff:g> з <xliff:g id="ID_2">%2$d</xliff:g>"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"Экран блакіроўкі"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"адкрыць галоўны экран прылады"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Каб адкрыць, скарыстайце адбітак пальца"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Патрабуецца аўтэнтыфікацыя. Дакраніцеся да сканера адбіткаў пальцаў."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Бягучы тэлефонны выклік"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Мабільная перадача даных"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Падключана"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Падключана часова"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 83aa4e5..1128d03 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Текущо известие за сесия за записване на екрана"</string>
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Да се записва ли екранът?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Записване на едно приложение"</string>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"Записване на този екран"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"Записване на %s"</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>
@@ -417,20 +415,13 @@
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Предварително зададените настройки не бяха актуализирани"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Предварително зададено"</string>
     <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Избрано"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Околни звуци"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Ляво"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Дясно"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Разгъване до отделни контроли за ляво и дясно"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Свиване до обединена контрола"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Спиране на околните звуци"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Включване на околните звуци"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Инструменти"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Надписи на живо"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Бележка"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"Показване на иконите за известията с нисък приоритет"</string>
     <string name="other" msgid="429768510980739978">"Друго"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"премахване на панел"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"добавяне на панела на последната позиция"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Преместване на панел"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Добавяне на панела на желаната позиция"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Преместване към позиция <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Добавяне към позиция <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Невалидна позиция."</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"изберете потребител"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"Няма връзка с интернет"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"Отваряне на настройките за <xliff:g id="ID_1">%s</xliff:g>."</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"Редактиране на подредбата на бързите настройки."</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Меню за включване/изключване"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Страница <xliff:g id="ID_1">%1$d</xliff:g> от <xliff:g id="ID_2">%2$d</xliff:g>"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"Заключен екран"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"вход в устройството"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Използвайте отпечатък за отваряне"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Изисква се удостоверяване на самоличността. За целта докоснете сензора за отпечатъци."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Текущо телефонно обаждане"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Мобилни данни"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Свързано"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Установена е временна връзка"</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index b927ad1..23a693b 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"স্ক্রিন রেকর্ডিং সেশন চলার বিজ্ঞপ্তি"</string>
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"আপনার স্ক্রিন রেকর্ড করবেন?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"একটি অ্যাপ রেকর্ড করুন"</string>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"এই স্ক্রিন রেকর্ড করুন"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"%s রেকর্ড করুন"</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>
@@ -355,7 +353,7 @@
     <string name="quick_settings_color_correction_label" msgid="5636617913560474664">"রঙ সংশোধন"</string>
     <string name="quick_settings_font_scaling_label" msgid="5289001009876936768">"ফন্ট সাইজ"</string>
     <string name="quick_settings_more_user_settings" msgid="7634653308485206306">"ব্যবহারকারীদের ম্যানেজ করুন"</string>
-    <string name="quick_settings_done" msgid="2163641301648855793">"সম্পন্ন হয়েছে"</string>
+    <string name="quick_settings_done" msgid="2163641301648855793">"হয়ে গেছে"</string>
     <string name="quick_settings_close_user_panel" msgid="5599724542275896849">"বন্ধ করুন"</string>
     <string name="quick_settings_connected" msgid="3873605509184830379">"সংযুক্ত হয়েছে"</string>
     <string name="quick_settings_connected_battery_level" msgid="1322075669498906959">"সংযুক্ত হয়েছে, ব্যাটারি <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
@@ -417,20 +415,13 @@
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"প্রিসেট আপডেট করা যায়নি"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"প্রিসেট"</string>
     <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"বেছে নেওয়া হয়েছে"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"সারাউন্ডিং"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"বাঁদিক"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"ডানদিক"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"বাঁদিক ও ডানদিকের আলাদা করা কন্ট্রোল বড় করুন"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"ইউনিফায়েড কন্ট্রোল আড়াল করুন"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"সারাউন্ডিং মিউট করুন"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"সারাউন্ডিং আনমিউট করুন"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"টুল"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"লাইভ ক্যাপশন"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"মনে রাখবেন"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"কম-গুরুত্বপূর্ণ বিজ্ঞপ্তির আইকন দেখুন"</string>
     <string name="other" msgid="429768510980739978">"অন্যান্য"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"টাইল সরান"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"শেষ জায়গাতে টাইল যোগ করুন"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"টাইল সরান"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"আপনার পছন্দের জায়গাতে টাইল যোগ করুন"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g>-এ সরান"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"অবস্থান <xliff:g id="POSITION">%1$d</xliff:g>-এ যোগ করুন"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"পজিশন সঠিক নয়।"</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"ব্যবহারকারী বেছে নিন"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"ইন্টারনেট কানেকশন নেই"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"<xliff:g id="ID_1">%s</xliff:g> সেটিংস খুলুন৷"</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"দ্রুত সেটিংসের ক্রম এডিট করুন"</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"পাওয়ার মেনু"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_2">%2$d</xliff:g>টির মধ্যে <xliff:g id="ID_1">%1$d</xliff:g> নং পৃষ্ঠা"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"লক স্ক্রিন"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"ডিভাইস আনলক করুন"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"খুলতে ফিঙ্গারপ্রিন্ট ব্যবহার করুন"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"যাচাইকরণ করতে হবে। যাচাইকরণ করতে আঙুলের ছাপের সেন্সরে টাচ করুন।"</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"ব্যবহারকারী এখন ফোনে কথা বলছেন"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"মোবাইল ডেটা"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"কানেক্ট করা আছে"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"সাময়িকভাবে কানেক্ট করা হয়েছে"</string>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index defc62e..d7b3f60 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Obavještenje za sesiju snimanja ekrana je u toku"</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>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"Snimaj ovaj ekran"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"Snimaj ekran %s"</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, audio 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, audio i videozapisi."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Snimaj ekran"</string>
@@ -417,20 +415,13 @@
     <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="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Odabrano"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Okruženje"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Lijevo"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Desno"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Proširivanje u zasebne kontrole ulijevo i udesno"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Sužavanje u objedinjenu kontrolu"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Isključivanje zvuka okruženja"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Uključivanje zvuka okruženja"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Alati"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Automatski titlovi"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Bilješka"</string>
@@ -978,7 +969,7 @@
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"uklanjanje kartice"</string>
     <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"dodavanje kartice na posljednji položaj"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Pomjeranje kartice"</string>
-    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Dodajte karticu na željeni položaj"</string>
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Dodavanje kartice na željeni položaj"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Pomjeranje u položaj <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Dodavanje u položaj <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Nevažeći položaj."</string>
@@ -994,7 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"odaberete korisnika"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"Nema internetske veze"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"Otvori postavke za: <xliff:g id="ID_1">%s</xliff:g>."</string>
-    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"Uredite redoslijed brzih postavki."</string>
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"Uređivanje redoslijeda Brzih postavki."</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Meni napajanja"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Stranica <xliff:g id="ID_1">%1$d</xliff:g> od <xliff:g id="ID_2">%2$d</xliff:g>"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"Zaključani ekran"</string>
@@ -1298,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"pristup uređaju"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Otvorite pomoću otiska prsta"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Potrebna je autentifikacija. Dodirnite senzor za otisak prsta da autentificirate."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Telefonski poziv u toku"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Prijenos podataka na mobilnoj mreži"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Povezano"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Privremeno povezano"</string>
@@ -1444,7 +1436,7 @@
     <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Prečice aplikacije"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Trenutna aplikacija"</string>
     <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_title" msgid="8567500639300970049">"Prečice na tastaturi"</string>
     <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Prilagodite prečice na tastaturi"</string>
     <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Ukloniti prečicu?"</string>
     <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Vratiti na zadano?"</string>
@@ -1456,7 +1448,7 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona sužavanja"</string>
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Ikona tipke radnji ili meta tipka"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Ikona znaka plus"</string>
-    <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Prilagođavanje"</string>
+    <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Prilagodite"</string>
     <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Poništavanje"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Gotovo"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona proširivanja"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index 05c9d81..09f63c5 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -111,10 +111,8 @@
     <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="7415261783188749730">"Vols gravar la pantalla?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Grava una aplicació"</string>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"Grava aquesta pantalla"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"Grava %s"</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>
@@ -417,20 +415,13 @@
     <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="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Seleccionat"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Entorn"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Esquerra"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Dreta"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Desplega els controls separats d\'esquerra i dreta"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Replega per unificar el control"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Silencia l\'entorn"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Deixa de silenciar l\'entorn"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Eines"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Subtítols instantanis"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Nota"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"Mostra les icones de notificació amb prioritat baixa"</string>
     <string name="other" msgid="429768510980739978">"Altres"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"suprimir el mosaic"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"afegir una icona a la darrera posició"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mou el mosaic"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Afegeix una icona a la posició que vulguis"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mou a la posició <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Afegeix a la posició <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"La posició no és vàlida."</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"triar un usuari"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"Sense connexió a Internet"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"Obre la configuració per a <xliff:g id="ID_1">%s</xliff:g>."</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"Edita l\'ordre de la configuració ràpida."</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Menú d\'engegada"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Pàgina <xliff:g id="ID_1">%1$d</xliff:g> (<xliff:g id="ID_2">%2$d</xliff:g> en total)"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"Pantalla de bloqueig"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"accedir al dispositiu"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Utilitza l\'empremta digital per obrir"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Autenticació necessària. Toca el sensor d\'empremtes digitals per autenticar-te."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Trucada en curs"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Dades mòbils"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Connectat"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Connexió temporal"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 7ae4416..0758764 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Trvalé oznámení o relaci nahrávání"</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>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"Nahrávat tuhle obrazovku"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"Nahrávat obrazovku %s"</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í, když jde o 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í, když jde o hesla, platební údaje, zprávy, fotografie, zvuk a video."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Nahrát obrazovku"</string>
@@ -417,20 +415,13 @@
     <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="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Vybráno"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Okolí"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Vlevo"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Vpravo"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Rozbalit na samostatné ovládání levé a pravé strany"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Sbalit na sjednocené ovládání"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Ztlumit okolí"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Zapnout zvuk okolí"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Nástroje"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Okamžité titulky"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Poznámka"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"Zobrazit ikony oznámení s nízkou prioritou"</string>
     <string name="other" msgid="429768510980739978">"Jiné"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"odstranit dlaždici"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"přidat dlaždici na poslední pozici"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Přesunout dlaždici"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Přidat dlaždici na požadované místo"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Přesunout na pozici <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Přidat dlaždici na pozici <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Pozice není platná."</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"zvolit uživatele"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"Nejste připojeni k internetu"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"Otevřít nastavení aplikace <xliff:g id="ID_1">%s</xliff:g>."</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"Upravit pořadí Rychlého nastavení."</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Nabídka vypínače"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Stránka <xliff:g id="ID_1">%1$d</xliff:g> z <xliff:g id="ID_2">%2$d</xliff:g>"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"Obrazovka uzamčení"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"zadáte zařízení"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"K otevření použijte otisk prstu"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Je vyžadováno ověření. Dotkněte se snímače otisků prstů."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Probíhající hovor"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobilní data"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Připojeno"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Dočasně připojeno"</string>
@@ -1449,7 +1438,7 @@
     <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_customize_mode_title" msgid="1467657117101096033">"Přizpůsobení klávesových zkratek"</string>
-    <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Odstrabit zkratku?"</string>
+    <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Odstranit zkratku?"</string>
     <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Resetovat do výchozího nastavení?"</string>
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Nastavte zkratku stisknutím klávesy"</string>
     <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Vlastní zkratka se trvale smaže."</string>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index c168a30..bcf9365 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Konstant notifikation om skærmoptagelse"</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>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"Optag denne skærm"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"Optag %s"</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>
@@ -417,20 +415,13 @@
     <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="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Valgt"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Omgivelser"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Venstre"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Højre"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Udvid til adskilte styringselementer til venstre og højre"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Minimer til samlet styringselement"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Ignorer omgivelser"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Ignorer ikke omgivelser"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Værktøjer"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Livetekstning"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Note"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"Vis ikoner for notifikationer med lav prioritet"</string>
     <string name="other" msgid="429768510980739978">"Andet"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"fjern felt"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"føj handlingsfeltet til den sidste position"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Flyt felt"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Føj handlingsfeltet til den ønskede placering"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Flyt til <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Føj til lokation <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Positionen er ugyldig."</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"vælge bruger"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"Intet internet"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"Åbn <xliff:g id="ID_1">%s</xliff:g>-indstillinger."</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"Rediger rækkefølgen af kvikmenuen."</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Menu for afbryderknappen"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Side <xliff:g id="ID_1">%1$d</xliff:g> af <xliff:g id="ID_2">%2$d</xliff:g>"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"Låseskærm"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"få adgang til enheden"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Brug fingeraftryk for at åbne"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Godkendelse er påkrævet. Sæt fingeren på fingeraftrykssensoren for at godkende."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Igangværende telefonopkald"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobildata"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Forbundet"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Midlertidigt forbundet"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index eaa849d..670f5a7 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Fortlaufende Benachrichtigung für eine Bildschirmaufzeichnung"</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>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"Diesen Bildschirm aufnehmen"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"%s 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>
@@ -417,20 +415,13 @@
     <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="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Ausgewählt"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Umgebungsgeräusche"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Links"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Rechts"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"In ein linkes und ein rechtes Steuerfeld maximieren"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Zu einem einzigen Steuerfeld minimieren"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Umgebungsgeräusche stummschalten"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Stummschaltung der Umgebungsgeräusche aufheben"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Tools"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Automatische Untertitel"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Notiz"</string>
@@ -839,7 +830,7 @@
     <string name="keyboard_key_button_template" msgid="8005673627272051429">"Taste <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="keyboard_key_home" msgid="3734400625170020657">"Pos1"</string>
     <string name="keyboard_key_back" msgid="4185420465469481999">"Zurück"</string>
-    <string name="keyboard_key_tab" msgid="4592772350906496730">"Tabulator­taste"</string>
+    <string name="keyboard_key_tab" msgid="4592772350906496730">"Tab"</string>
     <string name="keyboard_key_space" msgid="6980847564173394012">"Leer­taste"</string>
     <string name="keyboard_key_enter" msgid="8633362970109751646">"Eingabetaste"</string>
     <string name="keyboard_key_backspace" msgid="4095278312039628074">"Rücktaste"</string>
@@ -885,7 +876,7 @@
     <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Tasten­kürzel anzeigen"</string>
     <string name="group_system_go_back" msgid="2730322046244918816">"Zurück"</string>
     <string name="group_system_access_home_screen" msgid="4130366993484706483">"Zum Startbildschirm wechseln"</string>
-    <string name="group_system_overview_open_apps" msgid="5659958952937994104">"Letzte Apps aufrufen"</string>
+    <string name="group_system_overview_open_apps" msgid="5659958952937994104">"Zuletzt verwendete Apps aufrufen"</string>
     <string name="group_system_cycle_forward" msgid="5478663965957647805">"Zuletzt verwendete Apps vorwärts durchgehen"</string>
     <string name="group_system_cycle_back" msgid="8194102916946802902">"Zuletzt verwendete Apps rückwärts durchgehen"</string>
     <string name="group_system_access_all_apps_search" msgid="1553588630154197469">"Liste der Apps öffnen"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"Symbole für Benachrichtigungen mit einer niedrigen Priorität anzeigen"</string>
     <string name="other" msgid="429768510980739978">"Sonstiges"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"Entfernen der Kachel"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"Kachel an letzter Position hinzufügen"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Kachel verschieben"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Kachel an gewünschter Position hinzufügen"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Auf Position <xliff:g id="POSITION">%1$d</xliff:g> verschieben"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Zur Position <xliff:g id="POSITION">%1$d</xliff:g> hinzufügen"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Position ist ungültig."</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"Auswählen des Nutzers"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"Kein Internet"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"Einstellungen für <xliff:g id="ID_1">%s</xliff:g> öffnen."</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"Reihenfolge der Schnelleinstellungen bearbeiten"</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Ein-/Aus-Menü"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Seite <xliff:g id="ID_1">%1$d</xliff:g> von <xliff:g id="ID_2">%2$d</xliff:g>"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"Sperrbildschirm"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"Eingeben des Geräts"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Mit Fingerabdruck öffnen"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Authentifizierung erforderlich. Tippe dazu einfach auf den Fingerabdrucksensor."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Aktiver Anruf"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobile Daten"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Verbunden"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Vorübergehend verbunden"</string>
@@ -1444,16 +1433,16 @@
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Multitasking"</string>
     <string name="shortcutHelper_category_split_screen" msgid="1159669813444812244">"Splitscreen"</string>
     <string name="shortcut_helper_category_input" msgid="8674018654124839566">"Eingabe"</string>
-    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"App-Verknüp­fungen"</string>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Tastaturkürzel für Apps"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aktuelle App"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Bedienungshilfen"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Tastenkürzel"</string>
-    <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Tastenkombinationen anpassen"</string>
-    <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Tastenkombination entfernen?"</string>
+    <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Tastenkürzel anpassen"</string>
+    <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Tastaturkürzel entfernen?"</string>
     <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Auf Standardeinstellung zurücksetzen?"</string>
-    <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Drücke eine Taste, um eine Tastenkombination festzulegen"</string>
-    <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Dadurch wird die benutzerdefinierte Tastenkombination endgültig gelöscht."</string>
-    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Dadurch werden alle deine benutzerdefinierten Tastenkombinationen endgültig gelöscht."</string>
+    <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Drücke eine Taste, um das Tastaturkürzel einzurichten"</string>
+    <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Das benutzerdefinierte Tastenkürzel wird endgültig gelöscht."</string>
+    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Alle deine benutzerdefinierten Tastenkürzel werden endgültig gelöscht."</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>
@@ -1468,7 +1457,7 @@
     <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"Schrägstrich"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Ziehpunkt"</string>
     <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Tastatureinstellungen"</string>
-    <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Tastenkombination festlegen"</string>
+    <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Speichern"</string>
     <string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Entfernen"</string>
     <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Ja, zurücksetzen"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Abbrechen"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index dab393cf..31e3567 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Ειδοποίηση σε εξέλιξη για μια περίοδο λειτουργίας εγγραφής οθόνης"</string>
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Να γίνει εγγραφή της οθόνης σας;"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Εγγραφή μίας εφαρμογής"</string>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"Εγγραφή αυτής της οθόνης"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"Εγγραφή %s"</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>
@@ -417,20 +415,13 @@
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Δεν ήταν δυνατή η ενημέρωση της προεπιλογής"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Προεπιλογή"</string>
     <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Έχει επιλεγεί"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Ήχοι περιβάλλοντος"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Αριστερά"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Δεξιά"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Ανάπτυξη σε ξεχωριστά στοιχεία ελέγχου αριστερά και δεξιά"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Σύμπτυξη σε ενοποιημένο στοιχείο ελέγχου"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Σίγαση ήχων περιβάλλοντος"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Κατάργηση σίγασης ήχων περιβάλλοντος"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Εργαλεία"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Ζωντανοί υπότιτλοι"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Σημείωση"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"Εμφάνιση εικονιδίων ειδοποιήσεων χαμηλής προτεραιότητας"</string>
     <string name="other" msgid="429768510980739978">"Άλλο"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"κατάργηση πλακιδίου"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"προσθήκη πλακιδίου στην τελευταία θέση"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Μετακίνηση πλακιδίου"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Προσθήκη πλακιδίου στην επιθυμητή θέση"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Μετακίνηση στη θέση <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Προσθήκη στη θέση <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Μη έγκυρη θέση."</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"επιλογή χρήστη"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"Δεν υπάρχει σύνδεση στο διαδίκτυο"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"Άνοιγμα ρυθμίσεων <xliff:g id="ID_1">%s</xliff:g>."</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"Επεξεργασία σειράς Γρήγορων ρυθμίσεων."</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Μενού λειτουργίας"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Σελίδα <xliff:g id="ID_1">%1$d</xliff:g> από <xliff:g id="ID_2">%2$d</xliff:g>"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"Οθόνη κλειδώματος"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"εισαγωγή συσκευής"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Χρήση δακτυλικού αποτυπώματος για άνοιγμα"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Απαιτείται έλεγχος ταυτότητας. Αγγίξτε τον αισθητήρα δακτυλικών αποτυπωμάτων για έλεγχο ταυτότητας."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Τηλεφωνική κλήση σε εξέλιξη"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Δεδομένα κινητής τηλεφωνίας"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Συνδέθηκε"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Προσωρινή σύνδεση"</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index efab948..30eb21f 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Ongoing notification for a screen record session"</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>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"Record this screen"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"Record %s"</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>
@@ -417,20 +415,13 @@
     <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="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Selected"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Surroundings"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Left"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Right"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Expand to left and right separated controls"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Collapse to unified control"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Mute surroundings"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Unmute surroundings"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Tools"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Live Caption"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Note"</string>
@@ -1298,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"enter device"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Use fingerprint to open"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Authentication required. Touch the fingerprint sensor to authenticate."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Ongoing phone call"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobile data"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Connected"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Temporarily connected"</string>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 8486688..4b4b7b8 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -1289,7 +1289,7 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"enter device"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Use fingerprint to open"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Authentication required. Touch the fingerprint sensor to authenticate."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Ongoing phone call"</string>
+    <string name="ongoing_call_content_description" msgid="6394763878322348560">"Ongoing call"</string>
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobile data"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Connected"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Temporarily connected"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index efab948..30eb21f 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Ongoing notification for a screen record session"</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>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"Record this screen"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"Record %s"</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>
@@ -417,20 +415,13 @@
     <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="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Selected"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Surroundings"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Left"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Right"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Expand to left and right separated controls"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Collapse to unified control"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Mute surroundings"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Unmute surroundings"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Tools"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Live Caption"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Note"</string>
@@ -1298,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"enter device"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Use fingerprint to open"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Authentication required. Touch the fingerprint sensor to authenticate."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Ongoing phone call"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobile data"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Connected"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Temporarily connected"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index efab948..30eb21f 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Ongoing notification for a screen record session"</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>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"Record this screen"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"Record %s"</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>
@@ -417,20 +415,13 @@
     <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="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Selected"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Surroundings"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Left"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Right"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Expand to left and right separated controls"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Collapse to unified control"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Mute surroundings"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Unmute surroundings"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Tools"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Live Caption"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Note"</string>
@@ -1298,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"enter device"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Use fingerprint to open"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Authentication required. Touch the fingerprint sensor to authenticate."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Ongoing phone call"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobile data"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Connected"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Temporarily connected"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index e9786e3..2b3c54d 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -111,10 +111,8 @@
     <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="7415261783188749730">"¿Quieres grabar la pantalla?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Grabar una app"</string>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"Grabar esta pantalla"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"Grabar %s"</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 grabará 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>
@@ -417,20 +415,13 @@
     <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="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Seleccionado"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Sonido envolvente"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Izquierda"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Derecha"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Expandir los controles separados a la izquierda y a la derecha"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Contraer al control unificado"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Silenciar el sonido envolvente"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Activar el sonido envolvente"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Herramientas"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Subtitulado instantáneo"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Nota"</string>
@@ -886,8 +877,8 @@
     <string name="group_system_go_back" msgid="2730322046244918816">"Atrás"</string>
     <string name="group_system_access_home_screen" msgid="4130366993484706483">"Ir a la pantalla principal"</string>
     <string name="group_system_overview_open_apps" msgid="5659958952937994104">"Ver apps recientes"</string>
-    <string name="group_system_cycle_forward" msgid="5478663965957647805">"Desplazar por las apps recientes (adelante)"</string>
-    <string name="group_system_cycle_back" msgid="8194102916946802902">"Desplazar por las apps recientes (atrás)"</string>
+    <string name="group_system_cycle_forward" msgid="5478663965957647805">"Desplazarse por las apps recientes (adelante)"</string>
+    <string name="group_system_cycle_back" msgid="8194102916946802902">"Desplazarse por las apps recientes (atrás)"</string>
     <string name="group_system_access_all_apps_search" msgid="1553588630154197469">"Abrir lista de apps"</string>
     <string name="group_system_access_system_settings" msgid="8731721963449070017">"Abrir configuración"</string>
     <string name="group_system_access_google_assistant" msgid="7210074957915968110">"Abrir Asistente"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"Mostrar íconos de notificaciones con prioridad baja"</string>
     <string name="other" msgid="429768510980739978">"Otros"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"quitar tarjeta"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"agregar tarjeta a la última posición"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mover la tarjeta"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Agregar tarjeta a la posición deseada"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mover a <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Agregar a la posición <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Posición no válida"</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"elegir usuario"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"Sin Internet"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"Abrir configuración de <xliff:g id="ID_1">%s</xliff:g>"</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"Editar el orden de la Configuración rápida."</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Menú de encendido"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Página <xliff:g id="ID_1">%1$d</xliff:g> de <xliff:g id="ID_2">%2$d</xliff:g>"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"Pantalla de bloqueo"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"ingresar al dispositivo"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Usa la huella dactilar para abrir"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Se requiere de una autenticación. Toca el sensor de huellas dactilares para autenticarte."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Llamada en curso"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Datos móviles"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Conexión establecida"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Conectado temporalmente"</string>
@@ -1449,11 +1438,11 @@
     <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_customize_mode_title" msgid="1467657117101096033">"Personaliza las combinaciones de teclas"</string>
-    <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"¿Quieres quitar el acceso directo?"</string>
+    <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"¿Quieres quitar la combinación?"</string>
     <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"¿Quieres restablecer la configuración predeterminada?"</string>
-    <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Presiona la tecla para asignar el acceso directo"</string>
-    <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Esta acción borrará tu acceso directo personalizado de forma permanente."</string>
-    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Esta acción borrará todos tus accesos directos personalizados de forma permanente."</string>
+    <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Presiona una tecla para asignar la combinación"</string>
+    <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Esta acción borrará tu combinación personalizada de forma permanente."</string>
+    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Esta acción borrará todas tus combinaciones personalizadas de forma permanente."</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>
@@ -1468,7 +1457,7 @@
     <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"barra diagonal"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Controlador de arrastre"</string>
     <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Configuración del teclado"</string>
-    <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Establecer combinación de teclas"</string>
+    <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Establecer"</string>
     <string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Quitar"</string>
     <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Sí, restablecer"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Cancelar"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 9460874..69fab5a 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -111,10 +111,8 @@
     <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="7415261783188749730">"¿Grabar la pantalla?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Grabar una aplicación"</string>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"Grabar esta pantalla"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"Grabar %s"</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>
@@ -417,20 +415,13 @@
     <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="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Seleccionado"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Alrededores"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Izquierda"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Derecha"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Expandir a los controles separados de izquierda y derecha"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Contraer al control unificado"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Silenciar alrededores"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Dejar de silenciar alrededores"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Herramientas"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Subtítulos automáticos"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Nota"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"Mostrar iconos de notificaciones con prioridad baja"</string>
     <string name="other" msgid="429768510980739978">"Otros"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"quitar recuadro"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"añadir el recuadro a la última posición"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mover recuadro"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Añadir recuadro a la posición deseada"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mover a <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Añadir a la posición <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Posición no válida."</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"elegir un usuario"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"Sin Internet"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"Abrir ajustes de <xliff:g id="ID_1">%s</xliff:g>."</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"Editar orden de los ajustes rápidos."</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Menú de encendido"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Página <xliff:g id="ID_1">%1$d</xliff:g> de <xliff:g id="ID_2">%2$d</xliff:g>"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"Pantalla de bloqueo"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"acceder al dispositivo"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Usa la huella digital para abrir"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Autenticación obligatoria. Toca el sensor de huellas digitales para autenticarte."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Llamada en curso"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Datos móviles"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Conectado"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Conectada temporalmente"</string>
@@ -1454,7 +1443,7 @@
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Pulsa una tecla para asignar una combinación de teclas"</string>
     <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Se eliminará tu combinación de teclas personalizada de forma permanente."</string>
     <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Se eliminarán todos tus accesos directos personalizados de forma permanente."</string>
-    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Atajos de búsqueda"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Buscar accesos directos"</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_meta_key" msgid="3989315044342124818">"Icono de la tecla de acción o de la tecla Meta"</string>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 3999042..2ba5005 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Pooleli märguanne ekraanikuva salvestamise seansi puhul"</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>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"Ekraanikuva jäädvustamine"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"Kuva %s jäädvustamine"</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">"Salvesta ekraanikuva"</string>
@@ -417,20 +415,13 @@
     <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="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Valitud"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Ümbritsevad helid"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Vasakule"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Paremale"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Vasaku ja parema poole eraldi juhtimine"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Mõlema poole ühtne juhtimine"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Ümbritsevate helide vaigistamine"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Ümbritsevate helide vaigistuse tühistamine"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Tööriistad"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Reaalajas subtiitrid"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Märkus"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"Kuva madala prioriteediga märguande ikoonid"</string>
     <string name="other" msgid="429768510980739978">"Muu"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"paani eemaldamiseks"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"lisage paan viimasesse asukohta"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Teisalda paan"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Lisage paan soovitud asukohta"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Teisaldamine asendisse <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Lisamine asendisse <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Sobimatu asukoht."</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"valige kasutaja"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"Interneti-ühendus puudub"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"Ava teenuse <xliff:g id="ID_1">%s</xliff:g> seaded."</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"Muutke kiirseadete järjekorda"</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Toitemenüü"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Leht <xliff:g id="ID_1">%1$d</xliff:g>/<xliff:g id="ID_2">%2$d</xliff:g>"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"Lukustuskuva"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"seadmesse sisenemiseks"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Kasutage avamiseks sõrmejälge"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Vajalik on autentimine. Puudutage autentimiseks sõrmejäljeandurit."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Käimasolev telefonikõne"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobiilne andmeside"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Ühendatud"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Ajutiselt ühendatud"</string>
@@ -1459,7 +1448,7 @@
     <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ahendamisikoon"</string>
     <string name="shortcut_helper_content_description_meta_key" msgid="3989315044342124818">"Toiming või metaklahv"</string>
     <string name="shortcut_helper_content_description_plus_icon" msgid="6152683734278299020">"Pluss-ikoon"</string>
-    <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Kohandamine"</string>
+    <string name="shortcut_helper_customize_button_text" msgid="3124983502748069338">"Kohanda"</string>
     <string name="shortcut_helper_reset_button_text" msgid="2548243844050633472">"Lähtesta"</string>
     <string name="shortcut_helper_done_button_text" msgid="7249905942125386191">"Valmis"</string>
     <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Laiendamisikoon"</string>
@@ -1468,12 +1457,12 @@
     <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"kaldkriips"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Lohistamispide"</string>
     <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Klaviatuuri seaded"</string>
-    <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Määrake otsetee"</string>
+    <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Määra otsetee"</string>
     <string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Eemalda"</string>
-    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Jah, lähtesta"</string>
+    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Lähtesta"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Tühista"</string>
     <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Vajutage klahvi"</string>
-    <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Klahvikombinatsioon on juba kasutusel. Proovige mõnda muud klahvi."</string>
+    <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Kombinatsioon on juba kasutusel. Proovige mõnda muud klahvi."</string>
     <string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"Otseteed ei saa seadistada."</string>
     <string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Navigeerige klaviatuuri abil"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index 88a46c1..89ec1dd 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Pantailaren grabaketa-saioaren jakinarazpen jarraitua"</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>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"Grabatu pantaila hau"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"Grabatu %s"</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>
@@ -417,20 +415,13 @@
     <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="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Hautatuta"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Ingurunea"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Ezkerrekoa"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Eskuinekoa"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Zabaldu ezkerreko eta eskuineko kontrolatzeko aukerak bereiz erabiltzeko"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Tolestu kontrolatzeko aukerak bateratuta erabiltzeko"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Desaktibatu ingurunearen audioa"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Aktibatu ingurunearen audioa"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Tresnak"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Istanteko azpitituluak"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Oharra"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"Erakutsi lehentasun txikiko jakinarazpenen ikonoak"</string>
     <string name="other" msgid="429768510980739978">"Beste bat"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"kendu lauza"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"gehitu lauza azken posizioan"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mugitu lauza"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Gehitu lauza nahi duzun posizioan"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Eraman <xliff:g id="POSITION">%1$d</xliff:g>garren lekura"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Gehitu <xliff:g id="POSITION">%1$d</xliff:g>garren lekuan"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Kokapenak ez du balio."</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"erabiltzailea aukeratzeko"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"Ez dago Interneteko konexiorik"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"Ireki <xliff:g id="ID_1">%s</xliff:g> ezarpenak."</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"Editatu ezarpen bizkorren ordena."</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Itzaltzeko menua"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_1">%1$d</xliff:g>/<xliff:g id="ID_2">%2$d</xliff:g> orria"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"Pantaila blokeatua"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"sartu gailuan"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Erabili hatz-marka irekitzeko"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Autentifikazioa behar da. Autentifikatzeko, ukitu hatz-marken sentsorea."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Telefono-dei bat abian da"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Datu-konexioa"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Konektatuta"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Aldi baterako konektatuta"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 5e851dc..ae15b2f 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"اعلان درحال انجام برای جلسه ضبط صفحه‌نمایش"</string>
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"صفحه‌نمایش ضبط شود؟"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"ضبط یک برنامه"</string>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"ضبط کردن این صفحه"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"‏ضبط کردن %s"</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>
@@ -417,20 +415,13 @@
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"پیش‌تنظیم به‌روزرسانی نشد"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"پیش‌تنظیم"</string>
     <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"انتخاب‌شده"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"پیرامون"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"چپ"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"راست"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"ازهم بازکردن برای کنترل‌های جداگانه چپ و راست"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"جمع کردن برای کنترل یکپارچه"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"بی‌صدا کردن پیرامون"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"صدادار کردن پیرامون"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"ابزارها"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"زیرنویس زنده ناشنوایان"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"یادداشت"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"نمایش نمادهای اعلان کم‌اهمیت"</string>
     <string name="other" msgid="429768510980739978">"موارد دیگر"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"برداشتن کاشی"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"افزودن کاشی به آخرین جایگاه"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"انتقال کاشی"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"افزودن کاشی به جایگاه دلخواه"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"انتقال به <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"افزودن به موقعیت <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"موقعیت نامعتبر است."</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"انتخاب کاربر"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"عدم اتصال به اینترنت"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"باز کردن تنظیمات <xliff:g id="ID_1">%s</xliff:g>."</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"ویرایش ترتیب «تنظیمات فوری»."</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"منوی روشن/خاموش"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"صفحه <xliff:g id="ID_1">%1$d</xliff:g> از <xliff:g id="ID_2">%2$d</xliff:g>"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"صفحه قفل"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"وارد شدن به دستگاه"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"از اثر انگشت برای باز کردن قفل استفاده کنید"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"اصالت‌سنجی لازم است. برای اصالت‌سنجی، حسگر اثر انگشت را لمس کنید."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"تماس تلفنی درحال انجام"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"داده تلفن همراه"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"متصل است"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"موقتاً متصل است"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index b257a89..3220bfc 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -111,8 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Pysyvä ilmoitus näytön tallentamisesta"</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>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"Tallenna tämä näyttö"</string>
+    <!-- String.format failed for translation -->
     <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
     <skip />
     <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>
@@ -417,20 +417,13 @@
     <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="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Valittu"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Ympäristö"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Vasen"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Oikea"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Laajenna vasemmalle ja oikealle erilliset ohjaimet"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Tiivistä yhtenäiseksi säätimeksi"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Mykistä ympäristö"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Poista ympäristön mykistys"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Työkalut"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Livetekstitys"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Muistiinpano"</string>
@@ -976,11 +969,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"Näytä vähemmän tärkeät ilmoituskuvakkeet"</string>
     <string name="other" msgid="429768510980739978">"Muu"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"poista kiekko"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"lisää laatta viimeiseen kohtaan"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Siirrä kiekkoa"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Lisää laatta haluttuun kohtaan"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Siirrä paikkaan <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Lisää paikkaan <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Virheellinen sijainti."</string>
@@ -996,8 +987,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"valitse käyttäjä"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"Ei internetyhteyttä"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"Avaa kohteen <xliff:g id="ID_1">%s</xliff:g> asetukset."</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"Muokkaa pika-asetusten järjestystä."</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Virtavalikko"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Sivu <xliff:g id="ID_1">%1$d</xliff:g>/<xliff:g id="ID_2">%2$d</xliff:g>"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"Lukitusnäyttö"</string>
@@ -1301,7 +1291,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"avataksesi laitteen"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Avaa sormenjäljellä"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Todennus vaaditaan. Todenna koskettamalla sormenjälkitunnistinta."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Puhelu käynnissä"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobiilidata"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Yhdistetty"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Väliaikaisesti yhdistetty"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 8481f0fb..367c759 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -111,10 +111,8 @@
     <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="7415261783188749730">"Enregistrer votre écran?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Enregistrer une appli"</string>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"Enregistrer cet écran"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"Enregistrer %s"</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>
@@ -417,20 +415,13 @@
     <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="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Sélectionné"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Environnement"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Gauche"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Droit"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Développer les commandes distinctes à gauche et à droite"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Passer au contrôle unifié"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Ignorer les sons de l\'environnement"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Réactiver les sons de l\'environnement"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Outils"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Sous-titres instantanés"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Note"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"Afficher les icônes de notification de faible priorité"</string>
     <string name="other" msgid="429768510980739978">"Autre"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"retirer la tuile"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"Ajouter une tuile à la dernière position"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Déplacer la tuile"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Ajouter une tuile à la position désirée"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Déplacer vers <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Ajouter à la position <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Position incorrecte."</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"choisir un utilisateur"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"Aucune connexion Internet"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"Ouvrir les paramètres <xliff:g id="ID_1">%s</xliff:g>."</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"Modifier l\'ordre des Paramètres rapides."</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Menu de l\'interrupteur"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Page <xliff:g id="ID_1">%1$d</xliff:g> sur <xliff:g id="ID_2">%2$d</xliff:g>"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"Écran de verrouillage"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"accéder à l\'appareil"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Servez-vous de votre empreinte digitale pour ouvrir"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Authentification requise. Touchez le capteur d\'empreintes digitales pour vous authentifier."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Appel téléphonique en cours…"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Données cellulaires"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Connexion active"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Connectée temporairement"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 80f9cd9..dc10bcb 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -111,10 +111,8 @@
     <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="7415261783188749730">"Enregistrer l\'écran ?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Enregistrer une appli"</string>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"Enregistrer cet écran"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"Enregistrer %s"</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 de 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>
@@ -180,7 +178,7 @@
     <string name="accessibility_phone_button" msgid="4256353121703100427">"Téléphoner"</string>
     <string name="accessibility_voice_assist_button" msgid="6497706615649754510">"Assistance vocale"</string>
     <string name="accessibility_wallet_button" msgid="1458258783460555507">"Wallet"</string>
-    <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Lecteur code QR"</string>
+    <string name="accessibility_qr_code_scanner_button" msgid="7521277927692910795">"Lecteur QR code"</string>
     <string name="accessibility_unlock_button" msgid="3613812140816244310">"Déverrouillé"</string>
     <string name="accessibility_lock_icon" msgid="661492842417875775">"Appareil verrouillé"</string>
     <string name="accessibility_scanning_face" msgid="3093828357921541387">"Analyse du visage en cours"</string>
@@ -417,20 +415,13 @@
     <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="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Sélectionné"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Sons environnants"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Gauche"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Droite"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Développer les commandes gauche et droite"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Réduire en une commande unifiée"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Couper le mode Sons environnants"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Réactiver le mode Sons environnants"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Outils"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Sous-titres instantanés"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Note"</string>
@@ -755,7 +746,7 @@
     <string name="wallet_secondary_label_device_locked" msgid="5175862019125370506">"Déverrouiller pour utiliser"</string>
     <string name="wallet_error_generic" msgid="257704570182963611">"Problème de récupération de vos cartes. Réessayez plus tard"</string>
     <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"Paramètres de l\'écran de verrouillage"</string>
-    <string name="qr_code_scanner_title" msgid="1938155688725760702">"Lecteur code QR"</string>
+    <string name="qr_code_scanner_title" msgid="1938155688725760702">"Lecteur QR code"</string>
     <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"Mise à jour"</string>
     <string name="status_bar_work" msgid="5238641949837091056">"Profil professionnel"</string>
     <string name="status_bar_airplane" msgid="4848702508684541009">"Mode Avion"</string>
@@ -1239,7 +1230,7 @@
     <string name="media_output_first_broadcast_title" msgid="6292237789860753022">"Fonctionnement des annonces"</string>
     <string name="media_output_broadcast" msgid="3555580945878071543">"Annonce"</string>
     <string name="media_output_first_notify_broadcast_message" msgid="6353857724136398494">"Les personnes à proximité équipées d\'appareils Bluetooth compatibles peuvent écouter le contenu multimédia que vous diffusez"</string>
-    <string name="media_output_broadcasting_message" msgid="4150299923404886073">"Pour écouter votre annonce, les personnes à proximité équipées d\'appareils Bluetooth compatibles peuvent scanner votre code QR ou utiliser le nom et le mot de passe de votre annonce"</string>
+    <string name="media_output_broadcasting_message" msgid="4150299923404886073">"Pour écouter votre annonce, les personnes à proximité équipées d\'appareils Bluetooth compatibles peuvent scanner votre QR code ou utiliser le nom et le mot de passe de votre annonce"</string>
     <string name="media_output_broadcast_name" msgid="8786127091542624618">"Nom de l\'annonce"</string>
     <string name="media_output_broadcast_code" msgid="870795639644728542">"Mot de passe"</string>
     <string name="media_output_broadcast_dialog_save" msgid="7910865591430010198">"Enregistrer"</string>
@@ -1298,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"accéder à l\'appareil"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Utilisez votre empreinte pour ouvrir"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Authentification requise. Appuyez sur le lecteur d\'empreintes digitales pour vous authentifier."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Appel téléphonique en cours"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Données mobiles"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Connecté"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Connexion temporaire"</string>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 4837954..eef9147 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -111,10 +111,8 @@
     <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="7415261783188749730">"Queres gravar a túa pantalla?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Gravar unha aplicación"</string>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"Gravar esta pantalla"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"Gravar %s"</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>
@@ -417,20 +415,13 @@
     <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="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Elemento seleccionado"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Ambiente"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Esquerdo"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Dereito"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Despregar para controis separados do lado esquerdo e do dereito"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Contraer para control unificado"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Silenciar o ambiente"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Activar o son ambiental"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Ferramentas"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Subtítulos instantáneos"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Nota"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"Mostrar iconas das notificacións que teñan baixa prioridade"</string>
     <string name="other" msgid="429768510980739978">"Outros"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"quitar tarxeta"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"engadir o atallo á última posición"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mover tarxeta"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Engadir o atallo á última posición"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mover a <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Engadir á posición <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Posición non válida."</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"escoller usuario"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"Non hai conexión a Internet"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"Abrir configuración de <xliff:g id="ID_1">%s</xliff:g>."</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"Editar a orde de Configuración rápida."</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Menú de acendido"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Páxina <xliff:g id="ID_1">%1$d</xliff:g> de <xliff:g id="ID_2">%2$d</xliff:g>"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"Pantalla de bloqueo"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"poñer o dispositivo"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Usa a impresión dixital para abrir"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Requírese autenticación. Para autenticarte, toca o sensor de impresión dixital."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Chamada telefónica en curso"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Datos móbiles"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Conectada"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Conectada temporalmente"</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index ceefccc..88b4f34 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"સ્ક્રીન રેકોર્ડિંગ સત્ર માટે ચાલુ નોટિફિકેશન"</string>
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"તમારી સ્ક્રીન રેકોર્ડ કરીએ?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"એક ઍપ રેકોર્ડ કરો"</string>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"આ સ્ક્રીન રેકોર્ડ કરો"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"%s રેકોર્ડ કરો"</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>
@@ -417,20 +415,13 @@
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"પ્રીસેટ અપડેટ કરી શક્યા નથી"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"પ્રીસેટ"</string>
     <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"પસંદ કરી છે"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"આસપાસના અવાજો"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"ડાબે"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"જમણે"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"ડાબે અને જમણે અલગ કરેલા નિયંત્રણો સુધી વિસ્તૃત કરો"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"એકીકૃત નિયંત્રણ સુધી નાનું કરો"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"આસપાસના અવાજો મ્યૂટ કરો"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"આસપાસના અવાજો અનમ્યૂટ કરો"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"ટૂલ"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"લાઇવ કૅપ્શન"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"નોંધ"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"ઓછી પ્રાધાન્યતાનું નોટિફિકેશન આઇકન બતાવો"</string>
     <string name="other" msgid="429768510980739978">"અન્ય"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ટાઇલ કાઢી નાખો"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"છેલ્લા સ્થાનમાં ટાઇલ ઉમેરો"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ટાઇલ ખસેડો"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"ઇચ્છિત સ્થાનમાં ટાઇલ ઉમેરો"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> પર ખસેડો"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"જગ્યા પર <xliff:g id="POSITION">%1$d</xliff:g> ઉમેરો"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"સ્થિતિ અમાન્ય છે."</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"વપરાશકર્તા પસંદ કરો"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"કોઈ ઇન્ટરનેટ નથી"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"<xliff:g id="ID_1">%s</xliff:g> સેટિંગ ખોલો."</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"ઝડપી સેટિંગના ક્રમમાં ફેરફાર કરો."</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"પાવર મેનૂ"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_2">%2$d</xliff:g> માંથી <xliff:g id="ID_1">%1$d</xliff:g> પૃષ્ઠ"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"લૉક સ્ક્રીન"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"ડિવાઇસ અનલૉક કરો"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"ખોલવા માટે ફિંગરપ્રિન્ટનો ઉપયોગ કરો"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"પ્રમાણીકરણ આવશ્યક છે. પ્રમાણિત કરવા માટે ફિંગરપ્રિન્ટ સેન્સરને ટચ કરો."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"ફોન કૉલ ચાલુ છે"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"મોબાઇલ ડેટા"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"કનેક્ટ કરેલું"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"હંગામી રીતે કનેક્ટ કર્યું"</string>
@@ -1473,7 +1462,7 @@
     <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"હા, રીસેટ કરો"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"રદ કરો"</string>
     <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"કી દબાવો"</string>
-    <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"કી સંયોજન પેહલેથી ઉપયોગમાં છે. અન્ય કી અજમાવી જુઓ."</string>
+    <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"કી સંયોજન પહેલેલેથી ઉપયોગમાં છે. અન્ય કી અજમાવી જુઓ."</string>
     <string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"શૉર્ટકટ સેટ કરી શકાતો નથી."</string>
     <string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"તમારા કીબોર્ડ વડે નૅવિગેટ કરો"</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 91e17df..1f7066e 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"स्क्रीन रिकॉर्ड सेशन के लिए जारी सूचना"</string>
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"क्या आपको स्क्रीन को रिकॉर्ड करना है?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"एक ऐप्लिकेशन रिकॉर्ड करें"</string>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"इस स्क्रीन को रिकॉर्ड करें"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"%s को रिकॉर्ड करें"</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>
@@ -417,20 +415,13 @@
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"प्रीसेट अपडेट नहीं किया जा सका"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"प्रीसेट"</string>
     <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"चुना गया"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"आस-पास का वॉल्यूम"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"बाईं ओर के वॉल्यूम के लिए"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"दाईं ओर के वॉल्यूम के लिए"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"दाईं और बाईं ओर के वॉल्यूम को अलग-अलग मैनेज करने के लिए, वॉल्यूम पैनल को बड़ा करें"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"यूनिफ़ाइड कंट्रोल पर जाने के लिए पैनल को छोटा करें"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"आस-पास के वॉल्यूम को म्यूट करें"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"आस-पास के वॉल्यूम को अनम्यूट करें"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"टूल"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"लाइव कैप्शन"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"नोट"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"कम प्राथमिकता वाली सूचना के आइकॉन दिखाएं"</string>
     <string name="other" msgid="429768510980739978">"अन्य"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"टाइल हटाएं"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"आखिरी जगह पर टाइल जोड़ें"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"टाइल को किसी और पोज़िशन पर ले जाएं"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"अपनी पसंदीदा जगह पर टाइल जोड़ें"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"टाइल को <xliff:g id="POSITION">%1$d</xliff:g> पोज़िशन पर ले जाएं"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"टाइल को <xliff:g id="POSITION">%1$d</xliff:g> पोज़िशन पर जोड़ें"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"मौजूदा जगह अमान्य है."</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"उपयोगकर्ता चुनें"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"इंटरनेट कनेक्शन नहीं है"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"<xliff:g id="ID_1">%s</xliff:g> सेटिंग खोलें."</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"क्विक सेटिंग के क्रम में बदलाव करें."</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"पावर मेन्यू"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"पेज <xliff:g id="ID_2">%2$d</xliff:g> में से <xliff:g id="ID_1">%1$d</xliff:g>"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"लॉक स्‍क्रीन"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"डिवाइस की होम स्क्रीन पर जाएं"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"खोलने के लिए, फ़िंगरप्रिंट का इस्तेमाल करें"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"पुष्टि करना ज़रूरी है. पुष्टि करने के लिए, फ़िंगरप्रिंट सेंसर को छुएं."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"फ़ोन कॉल चल रहा है"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"मोबाइल डेटा"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"कनेक्ट हो गया"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"इंटरनेट कनेक्शन कुछ समय के लिए है"</string>
@@ -1452,8 +1441,8 @@
     <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"क्या आपको शॉर्टकट हटाना है?"</string>
     <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"क्या आपको फिर से डिफ़ॉल्ट सेटिंग चालू करनी है?"</string>
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"शॉर्टकट असाइन करने के लिए बटन दबाएं"</string>
-    <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"ऐसा करने से, आपका कस्टम शॉर्टकट हमेशा के लिए मिट जाएगा."</string>
-    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"ऐसा करने पर, आपके सभी कस्टम शॉर्टकट हमेशा के लिए मिट जाएंगे."</string>
+    <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"ऐसा करने पर, पसंद के मुताबिक बनाया गया आपका शॉर्टकट हमेशा के लिए मिट जाएगा."</string>
+    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"ऐसा करने पर, पसंद के मुताबिक बनाए गए आपके सभी शॉर्टकट हमेशा के लिए मिट जाएंगे."</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>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 0696dbf..5c40a51 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Tekuća obavijest za sesiju snimanja zaslona"</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>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"Snimi ovaj zaslon"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"Snimi %s"</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>
@@ -417,20 +415,13 @@
     <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="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Odabrano"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Okruženje"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Lijevo"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Desno"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Proširi u zasebne kontrole slijeva i zdesna"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Sažmi u objedinjenu kontrolu"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Isključi zvuk okruženja"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Uključi zvuk okruženja"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Alati"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Automatski titlovi"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Napomena"</string>
@@ -884,7 +875,7 @@
     <string name="group_system_full_screenshot" msgid="5742204844232667785">"Snimanje zaslona"</string>
     <string name="group_system_access_system_app_shortcuts" msgid="8562482996626694026">"Prikaz prečaca"</string>
     <string name="group_system_go_back" msgid="2730322046244918816">"Natrag"</string>
-    <string name="group_system_access_home_screen" msgid="4130366993484706483">"Otvaranje početnog zaslona"</string>
+    <string name="group_system_access_home_screen" msgid="4130366993484706483">"Otvori početni zaslon"</string>
     <string name="group_system_overview_open_apps" msgid="5659958952937994104">"Prikaz nedavnih aplikacija"</string>
     <string name="group_system_cycle_forward" msgid="5478663965957647805">"Listajte nedavne aplikacije (prema naprijed)"</string>
     <string name="group_system_cycle_back" msgid="8194102916946802902">"Listajte nedavne aplikacije (unatrag)"</string>
@@ -1298,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"pristupili uređaju"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Otvorite pomoću otiska prsta"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Potrebna je autentifikacija. Dodirnite senzor otiska prsta da biste se autentificirali."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Telefonski poziv u tijeku"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobilni podaci"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Povezano"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Privremeno povezano"</string>
@@ -1467,10 +1459,10 @@
     <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Postavke tipkovnice"</string>
     <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Postavite prečac"</string>
     <string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Ukloni"</string>
-    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Da, vrati na zadano"</string>
+    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Da, vrati"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Odustani"</string>
     <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Pritisnite tipku"</string>
-    <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Kombinacija tipki već se upotrebljava. Pokušajte s drugom tipkom."</string>
+    <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Ta se kombinacija već koristi. Pokušajte s nekom drugom."</string>
     <string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"Prečac se ne može postaviti."</string>
     <string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Krećite se pomoću tipkovnice"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index d2f6e8fc..50c6083 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -111,10 +111,8 @@
     <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="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>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"A képernyő felvétele"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"%s felvétele"</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>
@@ -417,20 +415,13 @@
     <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="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Kiválasztva"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Környezet"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Bal"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Jobb"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Kibontás a balra és jobbra elválasztott vezérlőkhöz"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Összecsukás az egységes vezérléshez"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Környezet némítása"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Környezet némításának feloldása"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Eszközök"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Élő feliratozás"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Megjegyzés"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"Alacsony prioritású értesítési ikonok mutatása"</string>
     <string name="other" msgid="429768510980739978">"Egyéb"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"mozaik eltávolításához"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"mozaik hozzáadása az utolsó pozícióhoz"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mozaik áthelyezése"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Mozaik hozzáadása a kívánt pozícióhoz"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Áthelyezés ide: <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Hozzáadás a következő pozícióhoz: <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Érvénytelen pozíció."</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"felhasználó kiválasztása"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"Nincs internetkapcsolat"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"A(z) <xliff:g id="ID_1">%s</xliff:g> beállításainak megnyitása."</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"A Gyorsbeállítások mozaiksorrendjének szerkesztése."</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Bekapcsológombhoz tartozó menü"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_1">%1$d</xliff:g>. oldal, összesen: <xliff:g id="ID_2">%2$d</xliff:g>"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"Lezárási képernyő"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"eszköz megadásához"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Ujjlenyomat használata a megnyitáshoz"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Hitelesítés szükséges. Érintse meg az ujjlenyomat-érzékelőt a hitelesítéshez."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Folyamatban lévő telefonhívás"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobiladat"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Csatlakozva"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Ideiglenesen csatlakoztatva"</string>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index f65f604..2e42ecd 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Էկրանի տեսագրման աշխատաշրջանի ընթացիկ ծանուցում"</string>
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Տեսագրե՞լ ձեր էկրանը"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Տեսագրել մեկ հավելված"</string>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"Տեսագրել էկրանը"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"Տեսագրել %s էկրանը"</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>
@@ -417,20 +415,13 @@
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Չհաջողվեց թարմացնել կարգավորումների հավաքածուն"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Կարգավորումների հավաքածու"</string>
     <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Ընտրված է"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Շրջակայք"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Ձախ"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Աջ"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Ծավալել՝ դարձնելով կառավարման աջ և ձախ առանձնացված տարրեր"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Ծալել՝ դարձնելով կառավարման մեկ միասնական տարր"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Անջատել շրջակայքի ձայները"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Միացնել շրջակայքի ձայները"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Գործիքներ"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Կենդանի ենթագրեր"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Նշում"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"Ցուցադրել ցածր առաջնահերթության ծանուցումների պատկերակները"</string>
     <string name="other" msgid="429768510980739978">"Այլ"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"հեռացնել սալիկը"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"ավելացնել սալիկը վերջին դիրքում"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Տեղափոխել սալիկը"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Ավելացնել սալիկը նախընտրած դիրքում"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Տեղափոխել դիրք <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Ավելացնել դիրք <xliff:g id="POSITION">%1$d</xliff:g>-ում"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Դիրքն անվավեր է։"</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"ընտրել օգտատեր"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"Ինտերնետ կապ չկա"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"Բացել <xliff:g id="ID_1">%s</xliff:g> կարգավորումները:"</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"Փոփոխել Արագ կարգավորումների հերթականությունը"</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Սնուցման կոճակի ընտրացանկ"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Էջ <xliff:g id="ID_1">%1$d</xliff:g> / <xliff:g id="ID_2">%2$d</xliff:g>"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"Կողպէկրան"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"նշել սարքը"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Բացելու համար օգտագործեք մատնահետքը"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Պահանջվում է նույնականացում։ Դրա համար մատը հպեք մատնահետքի սկաներին։"</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Ընթացիկ հեռախոսազանգ"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Բջջային ինտերնետ"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Միացած է"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Ժամանակավոր կապ"</string>
@@ -1470,10 +1459,10 @@
     <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Ստեղնաշարի կարգավորումներ"</string>
     <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Ստեղծել դյուրանցում"</string>
     <string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Հեռացնել"</string>
-    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Այո, վերականգնել"</string>
+    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Վերականգնել"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Չեղարկել"</string>
     <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Սեղմեք որևէ ստեղն"</string>
-    <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Ստեղների համակցությունն արդեն օգտագործվում է։ Ընտրեք այլ ստեղն։"</string>
+    <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Ստեղների համակցությունն արդեն օգտագործվում է։ Ընտրեք ուրիշը։"</string>
     <string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"Դյուրանցումը հնարավոր չէ ստեղծել։"</string>
     <string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Կողմնորոշվեք ձեր ստեղնաշարի օգնությամբ"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 527c8e8..2740ce5 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Notifikasi yang sedang berjalan untuk sesi rekaman layar"</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>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"Rekam layar ini"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"Rekam %s"</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>
@@ -417,20 +415,13 @@
     <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="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Dipilih"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Suara sekitar"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Kiri"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Kanan"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Luaskan ke kontrol terpisah kiri dan kanan"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Ciutkan ke kontrol terpadu"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Bisukan suara sekitar"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Bunyikan suara sekitar"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Alat"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Teks Otomatis"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Catatan"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"Tampilkan ikon notifikasi prioritas rendah"</string>
     <string name="other" msgid="429768510980739978">"Lainnya"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"menghapus kartu"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"menambahkan kartu ke posisi terakhir"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Pindahkan kartu"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Tambahkan kartu ke posisi yang diinginkan"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Pindahkan ke <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Tambahkan ke posisi <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Posisi tidak valid."</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"memilih pengguna"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"Tidak ada internet"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"Buka setelan <xliff:g id="ID_1">%s</xliff:g>."</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"Edit urutan Setelan Cepat."</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Menu daya"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Halaman <xliff:g id="ID_1">%1$d</xliff:g> dari <xliff:g id="ID_2">%2$d</xliff:g>"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"Layar kunci"</string>
@@ -1246,7 +1234,7 @@
     <string name="media_output_broadcast_name" msgid="8786127091542624618">"Nama Siaran"</string>
     <string name="media_output_broadcast_code" msgid="870795639644728542">"Sandi"</string>
     <string name="media_output_broadcast_dialog_save" msgid="7910865591430010198">"Simpan"</string>
-    <string name="media_output_broadcast_starting" msgid="8130153654166235557">"Memulai …"</string>
+    <string name="media_output_broadcast_starting" msgid="8130153654166235557">"Memulai…"</string>
     <string name="media_output_broadcast_start_failed" msgid="3670835946856129775">"Tidak dapat menyiarkan"</string>
     <string name="media_output_broadcast_update_error" msgid="1420868236079122521">"Tidak dapat menyimpan. Coba lagi."</string>
     <string name="media_output_broadcast_last_update_error" msgid="5484328807296895491">"Tidak dapat menyimpan."</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"masukkan perangkat"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Gunakan sidik jari untuk membuka"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Perlu autentikasi. Sentuh sensor sidik jari untuk melakukan autentikasi."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Panggilan telepon sedang berlangsung"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Data seluler"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Terhubung"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Terhubung sementara"</string>
@@ -1448,7 +1437,7 @@
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Aplikasi Saat Ini"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Aksesibilitas"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Pintasan keyboard"</string>
-    <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Menyesuaikan pintasan keyboard"</string>
+    <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Sesuaikan pintasan keyboard"</string>
     <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Hapus pintasan?"</string>
     <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Reset kembali ke pintasan default?"</string>
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Tekan tombol untuk menetapkan pintasan"</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 3d1b025..a6de1d7 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Áframhaldandi tilkynning fyrir skjáupptökulotu"</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>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"Taka upp þennan skjá"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"Taka upp %s"</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 myndskeið."</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 myndskeið."</string>
     <string name="screenrecord_permission_dialog_continue_entire_screen" msgid="5557974446773486600">"Taka upp skjá"</string>
@@ -417,20 +415,13 @@
     <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="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Valið"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Umhverfi"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Vinstri"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Hægri"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Aðskilja stýringar til vinstri og hægri"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Taka saman í eina stýringu"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Þagga umhverfi"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Kveikja á hljóði umhverfis"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Verkfæri"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Skjátextar í rauntíma"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Glósa"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"Sýna tákn fyrir tilkynningar með litlum forgangi"</string>
     <string name="other" msgid="429768510980739978">"Annað"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"fjarlægja flís"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"bæta reit við síðustu stöðu"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Færa flís"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Bæta reit við óskaða stöðu"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Færa í <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Bæta við í stöðu <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Staða ógild."</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"velja notanda"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"Engin nettenging"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"Opna <xliff:g id="ID_1">%s</xliff:g> stillingar."</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"Breyta röð flýtistillinga."</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Aflrofavalmynd"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Blaðsíða <xliff:g id="ID_1">%1$d</xliff:g> af <xliff:g id="ID_2">%2$d</xliff:g>"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"Lásskjár"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"opna tæki"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Opna með fingrafari"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Auðkenningar krafist. Auðkenndu með því að snerta fingrafaralesarann."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Símtal í gangi"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Farsímagögn"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Tengt"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Tímabundin tenging"</string>
@@ -1468,7 +1457,7 @@
     <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"rétt skástrik"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Dragkló"</string>
     <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Stillingar lyklaborðs"</string>
-    <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Stilltu flýtileið"</string>
+    <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Stilla flýtileið"</string>
     <string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Fjarlægja"</string>
     <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Já, endurstilla"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Hætta við"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index b605920..86aeae4 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Notifica costante per una sessione di registrazione dello schermo"</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>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"Registra questa schermata"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"Registra %s"</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>
@@ -969,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"Mostra icone di notifiche con priorità bassa"</string>
     <string name="other" msgid="429768510980739978">"Altro"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"rimuovere il riquadro"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"aggiungere il riquadro all\'ultima posizione"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Sposta riquadro"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Aggiungi il riquadro alla posizione desiderata"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Sposta nella posizione <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Aggiungi alla posizione <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Posizione non valida."</string>
@@ -989,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"selezionare l\'utente"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"Nessuna connessione a Internet"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"Apri le impostazioni <xliff:g id="ID_1">%s</xliff:g>."</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"Modifica l\'ordine delle Impostazioni rapide."</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Menu del tasto di accensione"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Pagina <xliff:g id="ID_1">%1$d</xliff:g> di <xliff:g id="ID_2">%2$d</xliff:g>"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"Schermata di blocco"</string>
@@ -1294,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"accedere al dispositivo"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Usa l\'impronta per aprire"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Autenticazione obbligatoria. Eseguila toccando il sensore di impronte digitali."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Telefonata in corso"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Dati mobili"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Connessa"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Connessa temporaneamente"</string>
@@ -1443,11 +1439,11 @@
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Scorciatoie da tastiera"</string>
     <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Personalizza scorciatoie da tastiera"</string>
     <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Rimuovere scorciatoia?"</string>
-    <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Vuoi ripristinare il valore predefinito?"</string>
+    <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Vuoi ripristinare le impostazioni predefinite?"</string>
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Premi un tasto per assegnare una scorciatoia"</string>
     <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"La scorciatoia personalizzata verrà eliminata definitivamente."</string>
     <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Tutte le tue scorciatoie personalizzate verranno eliminate definitivamente."</string>
-    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Scorciatoie per la ricerca"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Cerca scorciatoie"</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_meta_key" msgid="3989315044342124818">"Icona tasto Azione o Meta"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index d0f26ba..605a1fb 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"התראה מתמשכת לסשן הקלטת מסך"</string>
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"להקליט את המסך?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"הקלטה של אפליקציה אחת"</string>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"הקלטת המסך שמוצג עכשיו"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"‏הקלטה של %s"</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>
@@ -417,20 +415,13 @@
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"לא ניתן לעדכן את ההגדרה הקבועה מראש"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"הגדרה קבועה מראש"</string>
     <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"נבחר"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"הרעשים בסביבה"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"שמאל"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"ימין"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"הרחבה לאמצעי בקרה נפרדים לצד שמאל ולצד ימין"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"כיווץ לאמצעי בקרה מאוחד"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"השתקת הרעשים בסביבה"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"ביטול השתקת הרעשים בסביבה"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"כלים"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"כתוביות מיידיות"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"פתק"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"הצגת סמלי התראות בעדיפות נמוכה"</string>
     <string name="other" msgid="429768510980739978">"אחר"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"הסרת הלחצן"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"הוספת הלחצן במיקום האחרון"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"העברת הלחצן"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"הוספת הלחצן במיקום הרצוי"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"העברה למיקום <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"הוספה למיקום <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"המיקום לא תקין."</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"בחירת משתמש"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"אין אינטרנט"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"פתיחת הגדרות של <xliff:g id="ID_1">%s</xliff:g>."</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"שינוי הסדר של ההגדרות המהירות."</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"תפריט הפעלה"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"דף <xliff:g id="ID_1">%1$d</xliff:g> מתוך <xliff:g id="ID_2">%2$d</xliff:g>"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"מסך נעילה"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"הזנת מכשיר"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"שימוש בטביעת אצבע כדי לפתוח"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"נדרש אימות. יש לגעת בחיישן טביעות האצבע כדי לבצע אימות."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"מתקיימת שיחה"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"חבילת גלישה"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"מחובר"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"מחובר באופן זמני"</string>
@@ -1452,8 +1441,8 @@
     <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"להסיר את קיצור הדרך?"</string>
     <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"לאפס לברירת המחדל?"</string>
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"צריך להקיש על מקש כדי להקצות מקש קיצור"</string>
-    <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"קיצור הדרך יימחק באופן סופי."</string>
-    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"הפעולה הזו תמחק באופן סופי את כל קיצורי הדרך המותאמים אישית."</string>
+    <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"קיצור הדרך יימחק לתמיד."</string>
+    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"הפעולה הזו תמחק לתמיד את כל קיצורי הדרך שמותאמים אישית."</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>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index dd9eb2a..4634a50 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"画面の録画セッション中の通知"</string>
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"画面を録画しますか?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"1 つのアプリを録画"</string>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"この画面の録画"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"%s の録画"</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>
@@ -417,20 +415,13 @@
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"プリセットを更新できませんでした"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"プリセット"</string>
     <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"選択中"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"周囲の音"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"左"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"右"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"開く - 左右それぞれで制御する"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"閉じる - まとめて制御する"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"周囲の音をミュート"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"周囲の音のミュートを解除"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"ツール"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"自動字幕起こし"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"注"</string>
@@ -1298,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"デバイスを入力"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"指紋を使って開いてください"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"認証が必要です。指紋認証センサーをタッチして認証してください。"</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"通話中"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"モバイルデータ"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"接続済み"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"一時的に接続されています"</string>
@@ -1445,13 +1437,13 @@
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"現在のアプリ"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"ユーザー補助"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"キーボード ショートカット"</string>
-    <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"キーボード ショートカットをカスタマイズする"</string>
+    <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"キーボード ショートカットのカスタマイズ"</string>
     <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"ショートカットを削除しますか?"</string>
     <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"デフォルトにリセットしますか?"</string>
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"ショートカットを割り当てるキーを押してください"</string>
     <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"この操作を行うと、カスタム ショートカットが完全に削除されます。"</string>
     <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"この操作を行うと、すべてのカスタム ショートカットが完全に削除されます。"</string>
-    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"検索ショートカット"</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_meta_key" msgid="3989315044342124818">"アクションキーまたはメタキーのアイコン"</string>
@@ -1467,7 +1459,7 @@
     <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"キーボードの設定"</string>
     <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"ショートカットの設定"</string>
     <string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"削除"</string>
-    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"リセットする"</string>
+    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"リセット"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"キャンセル"</string>
     <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"キーを押してください"</string>
     <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"このキーの組み合わせはすでに使用されています。別のキーを試してください。"</string>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index e2459f3..fc0b81d 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"უწყვეტი შეტყობინება ეკრანის ჩაწერის სესიისთვის"</string>
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"გსურთ თქვენი ეკრანის ჩაწერა?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"ერთი აპის ჩაწერა"</string>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"ამ ეკრანის ჩაწერა"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"%s-ის ჩანაწერი"</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>
@@ -417,20 +415,13 @@
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"წინასწარ დაყენებული პარამეტრების განახლება ვერ მოხერხდა"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"წინასწარ დაყენებული"</string>
     <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"არჩეულია"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"გარემოცვა"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"მარცხენა"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"მარჯვენა"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"განცალკევებული მართვის საშუალებების გაფართოება მარცხნივ და მარჯვნივ"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"ერთიანი მართვის ჩაკეცვა"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"გარემოცვის დადუმება"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"გარემოცვის დადუმების მოხსნა"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"ხელსაწყოები"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"ავტოსუბტიტრები"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"ჩანიშვნა"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"დაბალი პრიორიტეტის მქონე შეტყობინებების ხატულების ჩვენება"</string>
     <string name="other" msgid="429768510980739978">"სხვა"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"მოზაიკის ფილის წაშლა"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"მოზაიკის ფილის ბოლო პოზიციაზე დამატება"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"მოზაიკის გადატანა"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"მოზაიკის ფილის სასურველ პოზიციაზე დამატება"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"გადატანა <xliff:g id="POSITION">%1$d</xliff:g>-ზე"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"დამატება პოზიციაზე <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"პოზიცია არასწორია."</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"მომხმარებლის არჩევა"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"ინტერნეტ-კავშირი არ არის"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"<xliff:g id="ID_1">%s</xliff:g> პარამეტრების გახსნა."</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"სწრაფი პარამეტრების თანმიმდევრობის რედაქტირება"</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"ჩართვის მენიუ"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"გვერდი <xliff:g id="ID_1">%1$d</xliff:g> / <xliff:g id="ID_2">%2$d</xliff:g>-დან"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"ჩაკეტილი ეკრანი"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"მოწყობილობის შეყვანა"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"გასახსნელად გამოიყენეთ თითის ანაბეჭდი"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"საჭიროა ავტორიზაცია. ავტორიზაციისთვის შეეხეთ თითის ანაბეჭდის სენსორს."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"მიმდინარე სატელეფონო ზარი"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"მობილური ინტერნეტი"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"დაკავშირებული"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"დროებით დაკავშირებული"</string>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index c325c96..643a1c2 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Экранды бейнеге жазудың ағымдағы хабарландыруы"</string>
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Қолданба экранын жазасыз ба?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Бір қолданба экранын жазу"</string>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"Осы экранды жазу"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"%s экранын жазу"</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>
@@ -417,20 +415,13 @@
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Параметрлер жинағын жаңарту мүмкін болмады."</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Параметрлер жинағы"</string>
     <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Таңдалды"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Айнала"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Сол жақ"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Оң жақ"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Сол жақ және оң жақ бөлек бақылау құралдарына жаю"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Біріктірілген бақылау құралына жию"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Айналаның дыбысын өшіру"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Айналаның дыбысын қосу"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Құралдар"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Live Caption"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Ескертпе"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"Маңызды емес хабарландыру белгішелерін көрсету"</string>
     <string name="other" msgid="429768510980739978">"Басқа"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"бөлшекті өшіру"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"соңғы позицияға бөлшек қосу"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Бөлшекті жылжыту"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Қалаған позицияға бөлшек қосу"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> орнына жылжыту"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> орнына қосу"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Орын жарамсыз."</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"пайдаланушыны таңдаңыз"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"Интернетпен байланыс жоқ"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"<xliff:g id="ID_1">%s</xliff:g> параметрлерін ашу."</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"Жылдам параметрлердің ретін өзгерту."</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Қуат мәзірі"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_2">%2$d</xliff:g> ішінен <xliff:g id="ID_1">%1$d</xliff:g>"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"Құлыптаулы экран"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"құрылғыны енгізу"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Ашу үшін саусақ ізін пайдаланыңыз."</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Аутентификациядан өту қажет. Ол үшін саусақ ізін оқу сканерін түртіңіз."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Телефон қоңырауы бар"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Мобильдік интернет"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Жалғанды"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Уақытша байланыс орнатылды."</string>
@@ -1449,11 +1438,11 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Арнайы мүмкіндіктер"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"Перне тіркесімдері"</string>
     <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Пернелер тіркесімін бейімдеу"</string>
-    <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Жылдам пәрменді өшіру керек пе?"</string>
-    <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Әдепкі таңбашаларға қайтару керек пе?"</string>
-    <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Жылдам пәрменді тағайындау үшін пернені басыңыз."</string>
-    <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Арнаулы жылдам пәрменіңіз біржола жойылады."</string>
-    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Мұндайда барлық арнаулы таңбашалар біржола жойылады."</string>
+    <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Тіркесімді өшіру керек пе?"</string>
+    <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Әдепкі тіркесімге қайтару керек пе?"</string>
+    <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Тіркесім тағайындау үшін пернені басыңыз."</string>
+    <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Арнаулы тіркесіміңіз біржола жойылады."</string>
+    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Мұндайда барлық арнаулы тіркесім біржола жойылады."</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>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index a6e25d1..239e2a9 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"ការជូនដំណឹង​ដែល​កំពុង​ដំណើរការ​សម្រាប់​រយៈពេលប្រើ​ការថត​សកម្មភាព​អេក្រង់"</string>
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"ថត​អេក្រង់​របស់អ្នកឬ?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"ថត​កម្មវិធី​ទោល"</string>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"ថតអេក្រង់នេះ"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"ថត %s"</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>
@@ -417,20 +415,13 @@
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"មិនអាច​ប្ដូរ​ការកំណត់ជាមុន​បានទេ"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"កំណត់ជាមុន"</string>
     <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"បានជ្រើសរើស"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"មជ្ឈដ្ឋានជុំវិញ"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"ឆ្វេង"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"ស្ដាំ"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"ពង្រីកទៅជាការគ្រប់គ្រងខាងឆ្វេង និងខាងស្ដាំដាច់ដោយឡែកពីគ្នា"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"បង្រួមទៅជាការគ្រប់គ្រងដែលបានរួមបញ្ចូលគ្នា"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"បិទសំឡេងមជ្ឈដ្ឋានជុំវិញ"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"បើកសំឡេងមជ្ឈដ្ឋានជុំវិញ"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"ឧបករណ៍"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"អក្សររត់ក្នុងពេលជាក់ស្ដែង"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"កំណត់ចំណាំ"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"បង្ហាញ​រូប​ការជូនដំណឹង​ដែលមានអាទិភាពទាប"</string>
     <string name="other" msgid="429768510980739978">"ផ្សេងៗ"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ដកប្រអប់ចេញ"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"បញ្ចូលប្រអប់ទៅទីតាំងចុងក្រោយ"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ផ្លាស់ទី​ប្រអប់"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"បញ្ចូលប្រអប់ទៅទីតាំងដែលចង់បាន"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"ផ្លាស់​ទីទៅ <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"បញ្ចូលទៅ​ទីតាំងទី <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"ទីតាំងគ្មានសុពលភាព។"</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"ជ្រើសរើស​អ្នកប្រើប្រាស់"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"គ្មាន​អ៊ីនធឺណិតទេ"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"បើការកំណត់ <xliff:g id="ID_1">%s</xliff:g>"</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"កែលំដាប់នៃការកំណត់រហ័ស។"</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"ម៉ឺនុយ​ថាមពល"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"ទំព័រ <xliff:g id="ID_1">%1$d</xliff:g> នៃ <xliff:g id="ID_2">%2$d</xliff:g>"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"អេក្រង់​ចាក់សោ"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"បញ្ចូល​ឧបករណ៍"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"ប្រើស្នាមម្រាមដៃ ដើម្បីបើក"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"តម្រូវឱ្យ​មាន​ការផ្ទៀងផ្ទាត់។ សូមចុច​ឧបករណ៍​ចាប់ស្នាមម្រាមដៃ ដើម្បី​ផ្ទៀងផ្ទាត់​។"</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"ការហៅទូរសព្ទ​ដែលកំពុង​ដំណើរការ"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"ទិន្នន័យ​ទូរសព្ទចល័ត"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"បានភ្ជាប់"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"បានភ្ជាប់ជាបណ្ដោះអាសន្ន"</string>
@@ -1473,7 +1462,7 @@
     <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"បាទ/ចាស កំណត់ឡើងវិញ"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"បោះបង់"</string>
     <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"ចុចគ្រាប់ចុច"</string>
-    <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"កំពុងប្រើបន្សំគ្រាប់ចុចស្រាប់ហើយ។ សាកល្បងប្រើគ្រាប់ចុចផ្សេង។"</string>
+    <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"បន្សំគ្រាប់ចុចនេះត្រូវបានប្រើប្រាស់ហើយ។ សាកល្បងគ្រាប់ចុចផ្សេង។"</string>
     <string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"មិនអាចកំណត់ផ្លូវកាត់បានទេ។"</string>
     <string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"រុករកដោយប្រើក្ដារចុចរបស់អ្នក"</string>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 2ddb1a8..0b0caa4 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"ಸ್ಕ್ರೀನ್ ರೆಕಾರ್ಡಿಂಗ್ ಸೆಶನ್‌ಗಾಗಿ ಚಾಲ್ತಿಯಲ್ಲಿರುವ ನೋಟಿಫಿಕೇಶನ್"</string>
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"ನಿಮ್ಮ ಸ್ಕ್ರೀನ್‌ ಅನ್ನು ರೆಕಾರ್ಡ್ ಮಾಡಬೇಕೇ?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"ಒಂದು ಆ್ಯಪ್ ಅನ್ನು ರೆಕಾರ್ಡ್ ಮಾಡಿ"</string>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"ಸ್ಕ್ರೀನ್ ಅನ್ನು ರೆಕಾರ್ಡ್ ಮಾಡಿ"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"%s ರೆಕಾರ್ಡ್ ಮಾಡಿ"</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>
@@ -417,20 +415,13 @@
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"ಪ್ರಿಸೆಟ್ ಅನ್ನು ಅಪ್‌ಡೇಟ್ ಮಾಡಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"ಪ್ರಿಸೆಟ್‌"</string>
     <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"ಆಯ್ಕೆಮಾಡಲಾಗಿದೆ"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"ಆ್ಯಂಬಿಯೆಂಟ್ ವಾಲ್ಯೂಮ್"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"ಎಡ"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"ಬಲ"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"ಪ್ರತ್ಯೇಕ ಎಡ ಮತ್ತು ಬಲ ಕಂಟ್ರೋಲ್‌ಗಳಿಗಾಗಿ ವಿಸ್ತರಿಸಿ"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"ಯೂನಿಫೈಡ್ ಕಂಟ್ರೋಲ್‌ಗಾಗಿ ಕುಗ್ಗಿಸಿ"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"ಆ್ಯಂಬಿಯೆಂಟ್ ವಾಲ್ಯೂಮ್ ಅನ್ನು ಮ್ಯೂಟ್ ಮಾಡಿ"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"ಆ್ಯಂಬಿಯೆಂಟ್ ವಾಲ್ಯೂಮ್ ಅನ್ನು ಅನ್‌ಮ್ಯೂಟ್ ಮಾಡಿ"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"ಟೂಲ್‌ಗಳು"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"ಲೈವ್ ಕ್ಯಾಪ್ಶನ್"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"ಟಿಪ್ಪಣಿ"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"ಕಡಿಮೆ-ಆದ್ಯತೆ ಸೂಚನೆಯ ಐಕಾನ್‌ಗಳನ್ನು ತೋರಿಸಿ"</string>
     <string name="other" msgid="429768510980739978">"ಇತರ"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ಟೈಲ್ ಅನ್ನು ತೆಗೆದುಹಾಕಿ"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"ಕೊನೆಯ ಸ್ಥಾನಕ್ಕೆ ಟೈಲ್ ಅನ್ನು ಸೇರಿಸಿ"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ಟೈಲ್ ಸರಿಸಿ"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"ಬಯಸಿದ ಸ್ಥಾನಕ್ಕೆ ಟೈಲ್ ಅನ್ನು ಸೇರಿಸಿ"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"ಇಲ್ಲಿಗೆ ಸರಿಸಿ <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> ಸ್ಥಾನಕ್ಕೆ ಸೇರಿಸಿ"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"ಸ್ಥಾನವು ಅಮಾನ್ಯವಾಗಿದೆ."</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"ಬಳಕೆದಾರರನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"ಇಂಟರ್ನೆಟ್ ಇಲ್ಲ"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"<xliff:g id="ID_1">%s</xliff:g> ಸೆಟ್ಟಿಂಗ್‌ಗಳನ್ನು ತೆರೆಯಿರಿ."</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"ತ್ವರಿತ ಸೆಟ್ಟಿಂಗ್‌ಗಳ ಕ್ರಮವನ್ನು ಎಡಿಟ್ ಮಾಡಿ."</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"ಪವರ್ ಮೆನು"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_2">%2$d</xliff:g> ರಲ್ಲಿ <xliff:g id="ID_1">%1$d</xliff:g> ಪುಟ"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"ಲಾಕ್ ಸ್ಕ್ರೀನ್"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"ಸಾಧನವನ್ನು ಪ್ರವೇಶಿಸಿ"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"ತೆರೆಯುವುದಕ್ಕಾಗಿ ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಅನ್ನು ಬಳಸಿ"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"ದೃಢೀಕರಣದ ಅವಶ್ಯಕತೆಯಿದೆ. ದೃಢೀಕರಿಸಲು ಫಿಂಗರ್‌ಪ್ರಿಂಟ್ ಸೆನ್ಸರ್ ಅನ್ನು ಸ್ಪರ್ಶಿಸಿ."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"ಚಾಲ್ತಿಯಲ್ಲಿರುವ ಫೋನ್ ಕರೆ"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"ಮೊಬೈಲ್ ಡೇಟಾ"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"ಕನೆಕ್ಟ್ ಆಗಿದೆ"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"ತಾತ್ಕಾಲಿಕವಾಗಿ ಕನೆಕ್ಟ್ ಮಾಡಲಾಗಿದೆ"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index caa7634..1473660 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"화면 녹화 세션에 관한 지속적인 알림"</string>
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"화면을 녹화하시겠습니까?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"단일 앱 녹화"</string>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"이 화면 녹화"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"%s 녹화"</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>
@@ -417,20 +415,13 @@
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"사전 설정을 업데이트할 수 없음"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"미리 설정"</string>
     <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"선택됨"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"주변 소리"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"왼쪽"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"오른쪽"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"왼쪽 및 오른쪽 개별 제어로 확장"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"통합 제어로 축소"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"주변 소리 음소거"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"주변 소리 음소거 해제"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"도구"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"실시간 자막"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"메모"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"우선순위가 낮은 알림 아이콘 표시"</string>
     <string name="other" msgid="429768510980739978">"기타"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"타일 삭제"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"마지막 위치에 타일 추가"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"타일 이동"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"원하는 위치에 타일 추가"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> 위치로 이동"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> 위치에 추가"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"위치가 잘못되었습니다."</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"사용자 선택"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"인터넷 연결 없음"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"<xliff:g id="ID_1">%s</xliff:g> 설정 열기"</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"빠른 설정 순서 수정"</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"전원 메뉴"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_2">%2$d</xliff:g>페이지 중 <xliff:g id="ID_1">%1$d</xliff:g>페이지"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"잠금 화면"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"기기 입력"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"지문으로 열기"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"인증이 필요합니다. 지문 센서를 터치하여 인증하세요."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"진행 중인 전화 통화"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"모바일 데이터"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"연결됨"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"일시적으로 연결됨"</string>
@@ -1449,12 +1438,12 @@
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"접근성"</string>
     <string name="shortcut_helper_title" msgid="8567500639300970049">"단축키"</string>
     <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"단축키 맞춤설정"</string>
-    <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"바로가기를 제거하시겠습니까?"</string>
+    <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"단축키를 삭제하시겠어요?"</string>
     <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"기본값으로 재설정하시겠어요?"</string>
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"키를 눌러 단축키 지정"</string>
-    <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"맞춤 단축어가 영구적으로 삭제됩니다."</string>
-    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"모든 맞춤 바로가기가 완전히 삭제됩니다."</string>
-    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"검색 바로가기"</string>
+    <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"맞춤 단축키가 영구적으로 삭제됩니다."</string>
+    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"모든 맞춤 단축키가 완전히 삭제됩니다."</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_meta_key" msgid="3989315044342124818">"작업 또는 메타 키 아이콘"</string>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index 836179e..ad70e79 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Экранды жаздыруу сеансы боюнча учурдагы билдирме"</string>
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Экранды жаздырасызбы?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Бир колдонмону жаздыруу"</string>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"Бул экранды жаздыруу"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"%s жаздыруу"</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>
@@ -417,20 +415,13 @@
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Алдын ала коюлган параметрлер жаңыртылган жок"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Алдын ала коюлган параметрлер"</string>
     <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Тандалды"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Айланадагы үндөр"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Сол"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Оң"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Cол жана оң жактагы өзүнчө башкаруу элементтерине жайып көрсөтүү"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Бирдиктүү башкаруу элементине жыйыштыруу"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Айланадагы үндөрдү басуу"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Айланадагы үндөрдү чыгаруу"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Куралдар"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Ыкчам коштомо жазуулар"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Учкай маалымат"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"Анча маанилүү эмес билдирменин сүрөтчөлөрүн көрсөтүү"</string>
     <string name="other" msgid="429768510980739978">"Башка"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ыкчам баскычты өчүрүү"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"аягына карта кошуу"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Ыкчам баскычты жылдыруу"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Керектүү жерге карта кошуу"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Төмөнкүгө жылдыруу: <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g>-позицияга кошуу"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Абал жараксыз."</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"колдонуучуну тандоо"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"Интернет жок"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"<xliff:g id="ID_1">%s</xliff:g> параметрлерин ачуу."</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"Ыкчам параметрлердин иретин өзгөртүү."</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Кубат баскычынын менюсу"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_2">%2$d</xliff:g> ичинен <xliff:g id="ID_1">%1$d</xliff:g>-бет"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"Кулпуланган экран"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"түзмөккө кирүү"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Манжаңыздын изи менен ачыңыз"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Аныктыкты текшерүү талап кылынат. Аныктыгын текшерүү үчүн манжа изинин сенсоруна тийип коюңуз."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Учурдагы телефон чалуу"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Мобилдик трафик"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Туташты"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Убактылуу туташып турат"</string>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 47a431b..1c3d411 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"ການແຈ້ງເຕືອນສຳລັບເຊດຊັນການບັນທຶກໜ້າຈໍໃດໜຶ່ງ"</string>
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"ບັນທຶກໜ້າຈໍຂອງທ່ານບໍ?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"ບັນທຶກແອັບດຽວ"</string>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"ບັນທຶກໜ້າຈໍນີ້"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"ບັນທຶກ %s"</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>
@@ -417,20 +415,13 @@
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"ບໍ່ສາມາດອັບເດດການຕັ້ງຄ່າລ່ວງໜ້າໄດ້"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"ຄ່າທີ່ກຳນົດລ່ວງໜ້າ"</string>
     <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"ເລືອກແລ້ວ"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"ສຽງແວດລ້ອມ"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"ຊ້າຍ"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"ຂວາ"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"ຂະຫຍາຍເປັນການຄວບຄຸມທີ່ແຍກເບື້ອງຊ້າຍ ແລະ ຂວາ"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"ຫຍໍ້ລົງເປັນການຄວບຄຸມແບບຮວມ"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"ປິດສຽງແວດລ້ອມ"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"ເຊົາປິດສຽງແວດລ້ອມ"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"ເຄື່ອງມື"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"ຄຳບັນຍາຍສົດ"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"ບັນທຶກ"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"ສະແດງໄອຄອນການແຈ້ງເຕືອນຄວາມສຳຄັນຕ່ຳ"</string>
     <string name="other" msgid="429768510980739978">"ອື່ນໆ"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ລຶບແຜ່ນອອກ"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"ເພີ່ມແຜ່ນໃສ່ຕຳແໜ່ງສຸດທ້າຍ"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ຍ້າຍແຜ່ນ"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"ເພີ່ມແຜ່ນໃສ່ຕຳແໜ່ງທີ່ຕ້ອງການ"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"ຍ້າຍໄປ <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"ເພີ່ມໃສ່ຕຳແໜ່ງ <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"ຕຳແໜ່ງບໍ່ຖືກຕ້ອງ."</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"ເລືອກຜູ້ໃຊ້"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"ບໍ່ມີອິນເຕີເນັດ"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"ເປີດການຕັ້ງຄ່າ <xliff:g id="ID_1">%s</xliff:g>."</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"ແກ້ໄຂລຳດັບຂອງການ​ຕັ້ງ​ຄ່າ​ດ່ວນ."</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"ເມນູເປີດປິດ"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_1">%1$d</xliff:g> ຈາກທັງໝົດ <xliff:g id="ID_2">%2$d</xliff:g>"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"ໜ້າຈໍລັອກ"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"ເຂົ້າອຸປະກອນ"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"ໃຊ້ລາຍນິ້ວມືເພື່ອເປີດ"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"ຕ້ອງພິສູດຢືນຢັນ. ແຕະໃສ່ເຊັນເຊີລາຍນິ້ວມືເພື່ອພິສູດຢືນຢັນ."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"ສາຍໂທອອກ"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"ອິນເຕີເນັດມືຖື"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"ເຊື່ອມຕໍ່ແລ້ວ"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"ເຊື່ອມຕໍ່ແລ້ວຊົ່ວຄາວ"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 5fba5b2..eb6fd2d 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Šiuo metu rodomas ekrano įrašymo sesijos pranešimas"</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>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"Įrašyti šį ekraną"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"Įrašyti ekraną „%s“"</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>
@@ -417,20 +415,13 @@
     <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="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Pasirinkta"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Aplinka"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Kairė"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Dešinė"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Išskleisti į atskirus kairįjį ir dešinįjį valdiklius"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Sutraukti į bendrą valdiklį"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Nutildyti aplinką"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Įjungti aplinkos garsą"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Įrankiai"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Subtitrai realiuoju laiku"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Pastaba"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"Rodyti mažo prioriteto pranešimų piktogramas"</string>
     <string name="other" msgid="429768510980739978">"Kita"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"pašalintumėte išklotinės elementą"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"pridėti išklotinės elementą paskutinėje pozicijoje"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Perkelti išklotinės elementą"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Pridėti išklotinės elementą norimoje pozicijoje"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Perkelkite į <xliff:g id="POSITION">%1$d</xliff:g> poziciją"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Pridėkite <xliff:g id="POSITION">%1$d</xliff:g> pozicijoje"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Padėtis netinkama."</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"pasirinktumėte naudotoją"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"Nėra interneto ryšio"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"Atidaryti „<xliff:g id="ID_1">%s</xliff:g>“ nustatymus."</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"Redaguoti sparčiųjų nustatymų tvarką."</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Įjungimo meniu"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_1">%1$d</xliff:g> psl. iš <xliff:g id="ID_2">%2$d</xliff:g>"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"Užrakinimo ekranas"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"pasiektumėte įrenginį"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Naudokite kontrolinį kodą, kad atidarytumėte"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Reikia nustatyti tapatybę. Nustatykite tapatybę palietę kontrolinio kodo jutiklį."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Vykstantis telefono skambutis"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobiliojo ryšio duomenys"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Prisijungta"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Laikinai prijungta"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index a407750..7740d2d 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -111,10 +111,8 @@
     <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="7415261783188749730">"Vai ierakstīt ekrānu?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Ierakstīt vienu lietotni"</string>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"Ierakstīt šo ekrānu"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"Ierakstīt ekrānu %s"</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>
@@ -417,20 +415,13 @@
     <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="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Atlasīts"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Apkārtnes skaņas"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Pa kreisi"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Pa labi"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Izvērst, lai rādītu atsevišķu kreiso un labo vadīklu"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Sakļaut, lai rādītu vienotu vadīklu"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Izslēgt apkārtnes skaņas"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Ieslēgt apkārtnes skaņas"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Rīki"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Subtitri reāllaikā"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Piezīme"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"Rādīt zemas prioritātes paziņojumu ikonas"</string>
     <string name="other" msgid="429768510980739978">"Citi"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"noņemt elementu"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"pievienotu elementu pēdējā pozīcijā"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Pārvietot elementu"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Pievienot elementu vēlamajā pozīcijā"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Pārvietot uz pozīciju numur <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Pievienot elementu pozīcijā numur <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Nederīga pozīcija."</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"izvēlēties lietotāju"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"Nav piekļuves internetam"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"Atvērt <xliff:g id="ID_1">%s</xliff:g> iestatījumus."</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"Rediģēt ātro iestatījumu secību."</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Barošanas izvēlne"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_1">%1$d</xliff:g>. lpp. no <xliff:g id="ID_2">%2$d</xliff:g>"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"Bloķēšanas ekrāns"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"izmantotu ierīci"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Atvēršanai izmantojiet pirksta nospiedumu"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Nepieciešama autentifikācija. Pieskarieties pirksta nospieduma sensoram, lai veiktu autentificēšanu."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Notiekošs tālruņa zvans"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobilie dati"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Ir izveidots savienojums"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Īslaicīgi izveidots savienojums"</string>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index f71ca44..e4064e2 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Тековно известување за сесија за снимање на екранот"</string>
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Да се снима екранот?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Снимање на една апликација"</string>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"Снимај го екранов"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"Снимај го екранот %s"</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>
@@ -417,20 +415,13 @@
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Не можеше да се ажурира зададената вредност"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Зададени вредности"</string>
     <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Избрано"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Опкружување"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Лево"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Десно"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Прошири на одвоените контроли одлево и оддесно"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Собери на унифицирана контрола"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Исклучи го звукот на опкружувањето"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Вклучи го звукот на опкружувањето"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Алатки"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Автоматски титлови"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Белешка"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"Прикажувај икони за известувања со низок приоритет"</string>
     <string name="other" msgid="429768510980739978">"Друго"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"отстранување на плочката"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"додајте плочка на последната позиција"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Преместување на плочката"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Додајте плочка на саканата позиција"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Преместување на <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Додавање на позиција <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Позицијата е погрешна."</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"изберете корисник"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"Нема интернет"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"Отворете ги поставките на <xliff:g id="ID_1">%s</xliff:g>."</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"Изменете го редот на „Брзи поставки“"</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Мени на копчето за вклучување"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Страница <xliff:g id="ID_1">%1$d</xliff:g> од <xliff:g id="ID_2">%2$d</xliff:g>"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"Заклучен екран"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"внесете уред"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Користете отпечаток за да се отвори"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Потребна е проверка. Допрете го сензорот за отпечаток за да автентицирате."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Тековен телефонски повик"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Мобилен интернет"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Поврзано"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Привремено поврзано"</string>
@@ -1451,7 +1440,7 @@
     <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Приспособете ги кратенките од тастатурата"</string>
     <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Да се отстрани кратенката?"</string>
     <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Да се ресетира на стандардните поставки?"</string>
-    <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Притиснете го копчето за да доделите кратенка"</string>
+    <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Притиснете копче за да доделите кратенка"</string>
     <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Ова ќе ја избрише вашата приспособена кратенка трајно."</string>
     <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Ова ќе ги избрише трајно сите ваши приспособени кратенки."</string>
     <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Пребарувајте кратенки"</string>
@@ -1468,11 +1457,11 @@
     <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"коса црта"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Рачка за влечење"</string>
     <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Поставки за тастатурата"</string>
-    <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Поставете кратенка"</string>
+    <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Постави"</string>
     <string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Отстрани"</string>
     <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Да, ресетирај"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Откажи"</string>
-    <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Притиснете го копчето"</string>
+    <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Притиснете копче"</string>
     <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Комбинацијата на копчиња веќе се користи. Обидете се со друго копче."</string>
     <string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"Кратенката не може да се постави."</string>
     <string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index db3e91a..601e6a1 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"ഒരു സ്ക്രീൻ റെക്കോർഡിംഗ് സെഷനായി നിലവിലുള്ള അറിയിപ്പ്"</string>
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"നിങ്ങളുടെ സ്ക്രീൻ റെക്കോർഡ് ചെയ്യണോ?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"ഒരു ആപ്പ് റെക്കോർഡ് ചെയ്യുക"</string>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"ഈ സ്ക്രീൻ റെക്കോർഡ് ചെയ്യുക"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"%s റെക്കോർഡ് ചെയ്യുക"</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>
@@ -417,20 +415,13 @@
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"പ്രീസെറ്റ് അപ്ഡേറ്റ് ചെയ്യാനായില്ല"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"പ്രീസെറ്റ്"</string>
     <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"തിരഞ്ഞെടുത്തു"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"സറൗണ്ടിംഗ്‌സ്"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"ഇടത്"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"വലത്"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"വേർതിരിച്ച ഇടത്, വലത് നിയന്ത്രണങ്ങളിലേക്ക് വികസിപ്പിക്കുക"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"ഏകീകൃത നിയന്ത്രണത്തിലേക്ക് ചുരുക്കുക"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"സറൗണ്ടിംഗ്‌സ് മ്യൂട്ട് ചെയ്യുക"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"സറൗണ്ടിംഗ്‌സ് അൺമ്യൂട്ട് ചെയ്യുക"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"ടൂളുകൾ"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"തത്സമയ ക്യാപ്ഷൻ"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"കുറിപ്പ്"</string>
@@ -1298,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"ഉപകരണം നൽകുക"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"തുറക്കുന്നതിന് നിങ്ങളുടെ ഫിംഗർപ്രിന്റ് ഉപയോഗിക്കുക"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"പരിശോധിച്ചുറപ്പിക്കേണ്ടതുണ്ട്. പരിശോധിച്ചുറപ്പിക്കാൻ, വിരലടയാള സെൻസറിൽ സ്‌പർശിക്കുക."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"സജീവമായ ഫോൺ കോൾ"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"മൊബൈൽ ഡാറ്റ"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"കണക്റ്റ് ചെയ്തു"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"താൽക്കാലികമായി കണക്റ്റ് ചെയ്തിരിക്കുന്നു"</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 75471ba..b865eed 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Дэлгэц бичих горимын үргэлжилж буй мэдэгдэл"</string>
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Дэлгэцээ бичих үү?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Нэг аппыг бичих"</string>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"Энэ дэлгэцийг бичих"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"%s-г бичих"</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>
@@ -417,20 +415,13 @@
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Урьдчилсан тохируулгыг шинэчилж чадсангүй"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Урьдчилсан тохируулга"</string>
     <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Сонгосон"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Орчин тойрон"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Зүүн"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Баруун"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Зүүн, баруун талын тусдаа тохиргоо руу дэлгэх"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Нэгдсэн тохиргоо руу хураах"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Орчин тойрны дууг хаах"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Орчин тойрны дууг нээх"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Хэрэгсэл"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Шууд тайлбар"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Тэмдэглэл"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"Бага ач холбогдолтой мэдэгдлийн дүрс тэмдгийг харуулах"</string>
     <string name="other" msgid="429768510980739978">"Бусад"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"хавтанг хасна уу"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"хавтанг сүүлийн байрлалд нэмэх"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Хавтанг зөөх"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Хавтанг хүссэн байрлалд нэмэх"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> руу зөөнө үү"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> байрлалд нэмнэ үү"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Байрлал буруу байна."</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"хэрэглэгчийг сонгох"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"Интернэт алга"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"<xliff:g id="ID_1">%s</xliff:g> тохиргоог нээнэ үү."</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"Шуурхай тохиргооны дарааллыг засна уу."</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Асаах/унтраах цэс"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_2">%2$d</xliff:g>-н <xliff:g id="ID_1">%1$d</xliff:g>-р хуудас"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"Түгжээтэй дэлгэц"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"төхөөрөмж оруулах"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Нээхийн тулд хурууны хээг ашиглана уу"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Баталгаажуулалт шаардлагатай. Баталгаажуулахын тулд хурууны хээ мэдрэгчид хүрнэ үү."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Үргэлжилж буй утасны дуудлага"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Мобайл дата"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Холбогдсон"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Түр зуур холбогдсон"</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index bd6cb13..e257ba1 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"स्क्रीन रेकॉर्ड सत्रासाठी सुरू असलेली सूचना"</string>
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"तुमची स्क्रीन रेकॉर्ड करायची आहे का?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"एक अ‍ॅप रेकॉर्ड करा"</string>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"ही स्क्रीन रेकॉर्ड करा"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"%s रेकॉर्ड करा"</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>
@@ -417,20 +415,13 @@
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"प्रीसेट अपडेट करता आले नाही"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"प्रीसेट"</string>
     <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"निवडला आहे"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"जवळपासचे आवाज"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"डावे"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"उजवे"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"डाव्या आणि उजव्या स्वतंत्र नियंत्रणांचा विस्तार करा"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"युनिफाइड नियंत्रणासाठी कोलॅप्स करा"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"जवळपासचे आवाज म्यूट करा"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"जवळपासचे आवाज अनम्यूट करा"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"टूल"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"लाइव्ह कॅप्शन"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"टीप"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"कमी प्राधान्य सूचना आयकन दर्शवा"</string>
     <string name="other" msgid="429768510980739978">"अन्य"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"टाइल काढून टाका"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"टाइल शेवटच्या स्थानावर जोडा"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"टाइल हलवा"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"हव्या असलेल्या स्थानावर टाइल जोडा"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> यावर हलवा"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> स्थानावर जोडा"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"स्थान चुकीचे आहे."</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"वापरकर्ता निवडा"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"इंटरनेट नाही"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"<xliff:g id="ID_1">%s</xliff:g> सेटिंग्ज उघडा."</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"क्विक सेटिंग्ज चा क्रम संपादित करा."</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"पॉवर मेनू"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_2">%2$d</xliff:g> पैकी <xliff:g id="ID_1">%1$d</xliff:g> पेज"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"लॉक स्‍क्रीन"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"डिव्हाइस एंटर करा"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"उघडण्यासाठी फिंगरप्रिंट वापरा"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"ऑथेंटिकेशन आवश्यक आहे. ऑथेंटिकेट करण्यासाठी फिंगरप्रिंट सेन्सरला स्पर्श करा."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"फोन कॉल सुरू आहे"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"मोबाइल डेटा"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"कनेक्ट केले आहे"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"तात्पुरते कनेक्ट केलेले"</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 3af5957..bf65859 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Pemberitahuan breterusan untuk sesi rakaman skrin"</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>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"Rakam skrin ini"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"Rakam %s"</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>
@@ -417,20 +415,13 @@
     <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="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Dipilih"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Persekitaran"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Kiri"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Kanan"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Kembangkan kepada kawalan berasingan sebelah kiri dan kanan"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Kuncupkan kepada kawalan yang disatukan"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Redamkan persekitaran"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Nyahredam persekitaran"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Alatan"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Sari Kata Langsung"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Nota"</string>
@@ -1298,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"akses peranti"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Gunakan cap jari untuk membuka"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Pengesahan diperlukan. Sentuh penderia cap jari untuk pengesahan."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Panggilan telefon yang sedang berjalan"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Data mudah alih"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Disambungkan"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Disambungkan buat sementara waktu"</string>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index a8a2eaa..1c40556 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"ဖန်သားပြင် ရိုက်ကူးသည့် စက်ရှင်အတွက် ဆက်တိုက်လာနေသော အကြောင်းကြားချက်"</string>
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"ဖန်သားပြင်ကို ရိုက်ကူးမလား။"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"အက်ပ်တစ်ခုကို ရိုက်ကူးရန်"</string>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"ဤစခရင်ကို ရိုက်ကူးရန်"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"%s ကို ရိုက်ကူးရန်"</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>
@@ -417,20 +415,13 @@
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"အသင့်သုံးကို အပ်ဒိတ်လုပ်၍မရပါ"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"ကြိုတင်သတ်မှတ်ချက်"</string>
     <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"ရွေးထားသည်"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"ဝန်းကျင်အသံ"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"ဘယ်"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"ညာ"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"ဘယ်ညာခွဲထားသော ထိန်းချုပ်မှုများအဖြစ် ပိုပြပါ"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"ပေါင်းစည်းထားသော ထိန်းချုပ်မှုအဖြစ် လျှော့ပြပါ"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"ဝန်းကျင်အသံ ပိတ်ရန်"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"ဝန်းကျင်အသံ ပြန်ဖွင့်ရန်"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"တူးလ်များ"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"တိုက်ရိုက်စာတန်း"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"မှတ်စု"</string>
@@ -869,7 +860,7 @@
     <string name="keyboard_shortcut_join" msgid="3578314570034512676">"သို့မဟုတ်"</string>
     <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"ရှာဖွေစာလုံး ရှင်းထုတ်ရန်"</string>
     <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"လက်ကွက်ဖြတ်လမ်းများ"</string>
-    <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"ဖြတ်လမ်းများ ရှာရန်"</string>
+    <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"ရှာဖွေစာလုံး ဖြတ်လမ်း"</string>
     <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"ဖြတ်လမ်းလင့်ခ် မတွေ့ပါ"</string>
     <string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"စနစ်"</string>
     <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"စာရိုက်ခြင်း"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"အရေးမကြီးသော အကြောင်းကြားချက် သင်္ကေတများ ပြရန်"</string>
     <string name="other" msgid="429768510980739978">"အခြား"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"အကွက်ငယ်ကို ဖယ်ရှားရန်"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"နောက်ဆုံးနေရာတွင် အကွက်ငယ် ထည့်ရန်"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"အကွက်ငယ်ကို ရွှေ့ရန်"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"ဆန္ဒရှိရာ နေရာတွင် အကွက်ငယ် ထည့်ရန်"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> သို့ ရွှေ့ရန်"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> အနေအထားသို့ ပေါင်းထည့်ရန်"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"နေရာ မမှန်ပါ။"</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"အသုံးပြုသူရွေးရန်"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"အင်တာနက် မရှိပါ"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"<xliff:g id="ID_1">%s</xliff:g> ဆက်တင်များကို ဖွင့်ပါ။"</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"‘အမြန်ဆက်တင်များ’ ၏ အစီအစဉ်ကို ပြင်ရန်။"</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"ပါဝါမီနူး"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"စာမျက်နှာ <xliff:g id="ID_2">%2$d</xliff:g> အနက်မှ စာမျက်နှာ <xliff:g id="ID_1">%1$d</xliff:g>"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"လော့ခ်မျက်နှာပြင်"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"စက်ပစ္စည်းသို့ ဝင်ရန်"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"ဖွင့်ရန် လက်ဗွေကို သုံးပါ"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"အထောက်အထားစိစစ်ခြင်း လိုအပ်သည်။ အထောက်အထားစိစစ်ရန် လက်ဗွေ အာရုံခံကိရိယာကို ထိပါ။"</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"လက်ရှိ ဖုန်းခေါ်ဆိုမှု"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"မိုဘိုင်းဒေတာ"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"ချိတ်ဆက်ထားသည်"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"ယာယီချိတ်ဆက်ထားသည်"</string>
@@ -1454,7 +1443,7 @@
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"ဖြတ်လမ်းလင့်ခ်သတ်မှတ်ရန် ကီးကို နှိပ်ပါ"</string>
     <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"၎င်းသည် သင့်စိတ်ကြိုက် ဖြတ်လမ်းလင့်ခ်ကို အပြီးဖျက်ပါမည်။"</string>
     <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"၎င်းသည် သင့်စိတ်ကြိုက်ဖြတ်လမ်းလင့်ခ်အားလုံးကို အပြီးဖျက်မည်။"</string>
-    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ဖြတ်လမ်းများ ရှာရန်"</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_meta_key" msgid="3989315044342124818">"လုပ်ဆောင်ချက် (သို့) Meta ကီးသင်္ကေတ"</string>
@@ -1470,7 +1459,7 @@
     <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"ကီးဘုတ်ဆက်တင်များ"</string>
     <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"ဖြတ်လမ်း သတ်မှတ်ရန်"</string>
     <string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"ဖယ်ရှားရန်"</string>
-    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"ပြန်လည်ပြင်ဆင်သတ်မှတ်မည်"</string>
+    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"ပြင်ဆင်ရန်"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"မလုပ်တော့"</string>
     <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"ကီးကို နှိပ်ပါ"</string>
     <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"ကီးပေါင်းစပ်ခြင်းကို သုံးနေပြီးဖြစ်သည်။ အခြားကီးကို စမ်းကြည့်ပါ။"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index ae135cc..0b68bd9 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Vedvarende varsel for et skjermopptak"</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>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"Ta opp denne skjermen"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"Ta opp %s"</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>
@@ -417,20 +415,13 @@
     <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="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Valgt"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Omgivelser"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Venstre"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Høyre"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Utvid til separate kontroller for venstre og høyre"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Skjul til samlet kontroll"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Kutt lyden for omgivelsene"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Slå på lyden for omgivelsene"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Verktøy"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Direkteteksting"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Merknad"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"Vis ikoner for varsler med lav prioritet"</string>
     <string name="other" msgid="429768510980739978">"Annet"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"fjerne infobrikken"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"legge til en brikke på den siste posisjonen"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Flytt infobrikken"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Legg til brikken på ønsket posisjon"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Flytt til <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Legg til posisjonen <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Posisjonen er ugyldig."</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"velge en bruker"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"Ingen internettilkobling"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"Åpne <xliff:g id="ID_1">%s</xliff:g>-innstillingene."</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"Endre rekkefølgen på hurtiginnstillingene."</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Av/på-meny"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Side <xliff:g id="ID_1">%1$d</xliff:g> av <xliff:g id="ID_2">%2$d</xliff:g>"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"Låseskjerm"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"åpne enheten"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Bruk fingeravtrykk for å åpne"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Autentisering kreves. Trykk på fingeravtrykkssensoren for å autentisere."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Pågående telefonsamtale"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobildata"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Tilkoblet"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Koblet til midlertidig"</string>
@@ -1470,7 +1459,7 @@
     <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Tastaturinnstillinger"</string>
     <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Angi hurtigtast"</string>
     <string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Fjern"</string>
-    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Ja, tilbakestill"</string>
+    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Tilbakestill"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Avbryt"</string>
     <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Trykk på tasten"</string>
     <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Tastekombinasjonen brukes allerede. Prøv en annen tast."</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index c701ebc..818809b 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"कुनै स्क्रिन रेकर्ड गर्ने सत्रका लागि चलिरहेको सूचना"</string>
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"तपाईंको स्क्रिन रेकर्ड गर्ने हो?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"एउटा एप रेकर्ड गर्नुहोस्"</string>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"यो स्क्रिन रेकर्ड गर्नुहोस्"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"%s रेकर्ड गर्नुहोस्"</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>
@@ -417,20 +415,13 @@
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"प्रिसेट अपडेट गर्न सकिएन"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"पूर्वनिर्धारित"</string>
     <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"चयन गरिएको छ"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"वरपरका आवाज"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"बायाँ"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"दायाँ"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"दायाँ र बायाँतर्फको भोल्युम छुट्टाछुट्टै व्यवस्थापन गर्न भोल्युम प्यानल छुट्ट्याउनुहोस्"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"कोल्याप्स गरी एउटै कन्ट्रोल बनाउनुहोस्"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"वरपरका आवाज म्युट गर्नुहोस्"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"वरपरका आवाज अनम्युट गर्नुहोस्"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"टुल"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"लाइभ क्याप्सन"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"नोट"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"कम प्राथमिकताका सूचना आइकनहरू देखाउनुहोस्"</string>
     <string name="other" msgid="429768510980739978">"अन्य"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"टाइल हटाउनुहोस्"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"अन्तिम स्थानमा टाइल हाल्नुहोस्"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"टाइल सार्नुहोस्"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"आफूले चाहेको स्थानमा टाइल हाल्नुहोस्"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"टाइल सारेर <xliff:g id="POSITION">%1$d</xliff:g> मा लैजानुहोस्"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"टाइल यो अवस्था <xliff:g id="POSITION">%1$d</xliff:g> मा हाल्नुहोस्"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"पोजिसन अवैध छ।"</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"प्रयोगकर्ता छान्नुहोस्"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"इन्टरनेट छैन"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"<xliff:g id="ID_1">%s</xliff:g> सम्बन्धी सेटिङहरूलाई खोल्नुहोस्।"</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"द्रुत सेटिङमा भएका विकल्पहरूको क्रम बदल्नुहोस्।"</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"पावर मेनु"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_2">%2$d</xliff:g> मध्ये पृष्ठ <xliff:g id="ID_1">%1$d</xliff:g>"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"लक स्क्रिन"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"डिभाइस हाल्नुहोस्"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"फिंगरप्रिन्ट प्रयोग गरी खोल्नुहोस्"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"पुष्टि गर्नु पर्ने हुन्छ। पुष्टि गर्न फिंगरप्रिन्ट सेन्सर छुनुहोस्।"</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"जारी फोन कल"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"मोबाइल डेटा"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"इन्टरनेटमा कनेक्ट गरिएको छ"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"यसमा केही समयका लागि कनेक्ट गरिएको हो"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 55b701e..546cd08 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Doorlopende melding voor een schermopname-sessie"</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>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"Dit scherm opnemen"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"%s 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>
@@ -417,20 +415,13 @@
     <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="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Geselecteerd"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Omgeving"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Links"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Rechts"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Uitvouwen naar gescheiden bediening voor links en rechts"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Samenvouwen tot geïntegreerde bediening"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Omgevingsgeluid uitzetten"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Omgevingsgeluid aanzetten"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Tools"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Live ondertiteling"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Notitie"</string>
@@ -1298,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"apparaat opgeven"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Gebruik vingerafdruk om te openen"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Verificatie vereist. Raak de vingerafdruksensor aan om de verificatie uit te voeren."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Actief telefoongesprek"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobiele data"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Verbonden"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Tijdelijk verbonden"</string>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 1ac2170..d035f6a 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"ଏକ ସ୍କ୍ରି‍ନ୍‍ ରେକର୍ଡ୍‍ ସେସନ୍‍ ପାଇଁ ଚାଲୁଥିବା ବିଜ୍ଞପ୍ତି"</string>
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"ଆପଣଙ୍କ ସ୍କ୍ରିନକୁ ରେକର୍ଡ କରିବେ?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"ଗୋଟିଏ ଆପ ରେକର୍ଡ କରନ୍ତୁ"</string>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"ଏହି ସ୍କ୍ରିନକୁ ରେକର୍ଡ କରନ୍ତୁ"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"%sକୁ ରେକର୍ଡ କରନ୍ତୁ"</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>
@@ -417,20 +415,13 @@
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"ପ୍ରିସେଟକୁ ଅପଡେଟ କରାଯାଇପାରିଲା ନାହିଁ"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"ପ୍ରିସେଟ"</string>
     <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"ଚୟନ କରାଯାଇଛି"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"ପରିପାର୍ଶ୍ୱ"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"ବାମ"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"ଡାହାଣ"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"ବାମ ଏବଂ ଡାହାଣ ଅଲଗା ନିୟନ୍ତ୍ରଣକୁ ବିସ୍ତାର କରନ୍ତୁ"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"ଏକତ୍ରିତ ନିୟନ୍ତ୍ରଣକୁ ସଙ୍କୁଚିତ କରନ୍ତୁ"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"ପରିପାର୍ଶ୍ୱକୁ ମ୍ୟୁଟ କରନ୍ତୁ"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"ପରିପାର୍ଶ୍ୱକୁ ଅନମ୍ୟୁଟ କରନ୍ତୁ"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"ଟୁଲ"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"ଲାଇଭ କେପ୍ସନ"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"ନୋଟ"</string>
@@ -914,7 +905,7 @@
     <string name="keyboard_shortcut_group_applications_assist" msgid="6772492350416591448">"Assistant"</string>
     <string name="keyboard_shortcut_group_applications_browser" msgid="2776211137869809251">"ବ୍ରାଉଜର୍"</string>
     <string name="keyboard_shortcut_group_applications_contacts" msgid="2807268086386201060">"କଣ୍ଟାକ୍ଟ"</string>
-    <string name="keyboard_shortcut_group_applications_email" msgid="7852376788894975192">"ଇମେଲ୍"</string>
+    <string name="keyboard_shortcut_group_applications_email" msgid="7852376788894975192">"ଇମେଲ"</string>
     <string name="keyboard_shortcut_group_applications_sms" msgid="6912633831752843566">"SMS"</string>
     <string name="keyboard_shortcut_group_applications_music" msgid="9032078456666204025">"ମ୍ୟୁଜିକ୍‍"</string>
     <string name="keyboard_shortcut_group_applications_calendar" msgid="4229602992120154157">"Calendar"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"କମ୍‍-ଅଗ୍ରାଧିକାର ବିଜ୍ଞପ୍ତି ଆଇକନ୍‍ ଦେଖାନ୍ତୁ"</string>
     <string name="other" msgid="429768510980739978">"ଅନ୍ୟ"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ଟାଇଲ୍ କାଢ଼ି ଦିଅନ୍ତୁ"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"ଶେଷ ପୋଜିସନରେ ଟାଇଲ ଯୋଗ କରିବା"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ଟାଇଲ୍ ମୁଭ୍ କରନ୍ତୁ"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"ଇଚ୍ଛିତ ପୋଜିସନରେ ଟାଇଲ ଯୋଗ କରନ୍ତୁ"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g>କୁ ମୁଭ୍ କରନ୍ତୁ"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> ଅବସ୍ଥିତିରେ ଯୋଗ କରନ୍ତୁ"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"ଅବସ୍ଥିତି ଅବୈଧ ଅଟେ।"</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"ୟୁଜର ବାଛନ୍ତୁ"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"କୌଣସି ଇଣ୍ଟରନେଟ୍‌ କନେକ୍ସନ୍ ନାହିଁ"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"<xliff:g id="ID_1">%s</xliff:g> ସେଟିଙ୍ଗ ଖୋଲନ୍ତୁ।"</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"କୁଇକ ସେଟିଂସର କ୍ରମକୁ ଏଡିଟ କରନ୍ତୁ।"</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"ପାୱାର ମେନୁ"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"ପୃଷ୍ଠା <xliff:g id="ID_1">%1$d</xliff:g> ମୋଟ <xliff:g id="ID_2">%2$d</xliff:g>"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"ଲକ ସ୍କ୍ରିନ"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"ଡିଭାଇସ୍ ବିଷୟରେ ସୂଚନା ଲେଖନ୍ତୁ"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"ଖୋଲିବାକୁ ଟିପଚିହ୍ନ ବ୍ୟବହାର କରନ୍ତୁ"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"ପ୍ରମାଣୀକରଣ ଆବଶ୍ୟକ। ପ୍ରମାଣୀକରଣ କରିବାକୁ ଟିପଚିହ୍ନ ସେନ୍ସରକୁ ସ୍ପର୍ଶ କରନ୍ତୁ।"</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"ଚାଲୁଥିବା ଫୋନ୍ କଲ୍"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"ମୋବାଇଲ ଡାଟା"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"ସଂଯୋଗ କରାଯାଇଛି"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"ଅସ୍ଥାୟୀ ରୂପେ କନେକ୍ଟ କରାଯାଇଛି"</string>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index 6eda17a..7b718de 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"ਕਿਸੇ ਸਕ੍ਰੀਨ ਰਿਕਾਰਡ ਸੈਸ਼ਨ ਲਈ ਚੱਲ ਰਹੀ ਸੂਚਨਾ"</string>
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"ਕੀ ਤੁਹਾਡੀ ਸਕ੍ਰੀਨ ਨੂੰ ਰਿਕਾਰਡ ਕਰਨਾ ਹੈ?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"ਇੱਕ ਐਪ ਨੂੰ ਰਿਕਾਰਡ ਕਰੋ"</string>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"ਇਸ ਸਕ੍ਰੀਨ ਨੂੰ ਰਿਕਾਰਡ ਕਰੋ"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"%s ਨੂੰ ਰਿਕਾਰਡ ਕਰੋ"</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>
@@ -417,20 +415,13 @@
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"ਪ੍ਰੀਸੈੱਟ ਨੂੰ ਅੱਪਡੇਟ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"ਪ੍ਰੀਸੈੱਟ"</string>
     <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"ਚੁਣਿਆ ਗਿਆ"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"ਆਲੇ-ਦੁਆਲੇ"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"ਖੱਬੇ"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"ਸੱਜੇ"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"ਖੱਬੇ ਅਤੇ ਸੱਜੇ ਪਾਸੇ ਦੇ ਸ਼ੋਰ ਨੂੰ ਵੱਖ-ਵੱਖ ਕੰਟਰੋਲ ਕਰਨ ਲਈ ਵਿਸਤਾਰ ਕਰੋ"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"ਏਕੀਕ੍ਰਿਤ ਕੰਟਰੋਲ \'ਤੇ ਜਾਣ ਲਈ ਸਮੇਟੋ"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"ਆਲੇ-ਦੁਆਲੇ ਦੇ ਸ਼ੋਰ ਨੂੰ ਮਿਊਟ ਕਰੋ"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"ਆਲੇ-ਦੁਆਲੇ ਦੇ ਸ਼ੋਰ ਨੂੰ ਅਣਮਿਊਟ ਕਰੋ"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"ਟੂਲ"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"ਲਾਈਵ ਸੁਰਖੀਆਂ"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"ਨੋਟ-ਕਥਨ"</string>
@@ -466,7 +457,7 @@
     <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_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="8231201163894922821">"ਸੈੱਟ ਨਹੀਂ ਹੈ"</string>
     <string name="zen_mode_no_manual_invocation" msgid="1769975741344633672">"ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਪ੍ਰਬੰਧਨ ਕਰੋ"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"ਘੱਟ ਤਰਜੀਹ ਵਾਲੇ ਸੂਚਨਾ ਪ੍ਰਤੀਕਾਂ ਨੂੰ ਦਿਖਾਓ"</string>
     <string name="other" msgid="429768510980739978">"ਹੋਰ"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ਟਾਇਲ ਹਟਾਓ"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"ਪਿਛਲੀ ਸਥਿਤੀ \'ਤੇ ਟਾਇਲ ਸ਼ਾਮਲ ਕਰੋ"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ਟਾਇਲ ਨੂੰ ਲਿਜਾਓ"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"ਮਨਪਸੰਦ ਸਥਿਤੀ \'ਤੇ ਟਾਇਲ ਸ਼ਾਮਲ ਕਰੋ"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> \'ਤੇ ਲਿਜਾਓ"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> ਸਥਾਨ \'ਤੇ ਸ਼ਾਮਲ ਕਰੋ"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"ਮੌਜੂਦਾ ਥਾਂ ਅਵੈਧ ਹੈ।"</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"ਵਰਤੋਂਕਾਰ ਚੁਣੋ"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"ਇੰਟਰਨੈੱਟ ਨਹੀਂ।"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"<xliff:g id="ID_1">%s</xliff:g> ਸੈਟਿੰਗਾਂ ਖੋਲ੍ਹੋ।"</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"ਤਤਕਾਲ ਸੈਟਿੰਗਾਂ ਦੇ ਕ੍ਰਮ ਦਾ ਸੰਪਾਦਨ ਕਰੋ।"</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"ਪਾਵਰ ਮੀਨੂ"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_2">%2$d</xliff:g> ਦਾ <xliff:g id="ID_1">%1$d</xliff:g> ਪੰਨਾ"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">" ਲਾਕ  ਸਕ੍ਰੀਨ"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"ਡੀਵਾਈਸ ਵਿੱਚ ਦਾਖਲ ਹੋਵੋ"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"ਖੋਲ੍ਹਣ ਲਈ ਫਿੰਗਰਪ੍ਰਿੰਟ ਵਰਤੋ"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"ਪ੍ਰਮਾਣੀਕਰਨ ਲੋੜੀਂਦਾ ਹੈ। ਪ੍ਰਮਾਣਿਤ ਕਰਨ ਲਈ ਫਿੰਗਰਪ੍ਰਿੰਟ ਸੈਂਸਰ ਨੂੰ ਸਪਰਸ਼ ਕਰੋ।"</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"ਜਾਰੀ ਫ਼ੋਨ ਕਾਲ"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"ਮੋਬਾਈਲ ਡਾਟਾ"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"ਕਨੈਕਟ ਹੈ"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"ਕੁਝ ਸਮੇਂ ਲਈ ਕਨੈਕਟ ਹੈ"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 82e3f67..b6dc2f61 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Stałe powiadomienie o sesji rejestrowania zawartości ekranu"</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>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"Nagrywaj ten ekran"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"Nagrywaj ekran %s"</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>
@@ -417,20 +415,13 @@
     <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="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Wybrano"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Otoczenie"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Po lewej"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Po prawej"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Rozwiń, aby oddzielić elementy sterujące po lewej i po prawej stronie"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Zwiń do ujednoliconego sterowania"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Wycisz otoczenie"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Wyłącz wyciszenie otoczenia"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Narzędzia"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Napisy na żywo"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Notatka"</string>
@@ -886,8 +877,8 @@
     <string name="group_system_go_back" msgid="2730322046244918816">"Przejdź wstecz"</string>
     <string name="group_system_access_home_screen" msgid="4130366993484706483">"Wyświetl ekran główny"</string>
     <string name="group_system_overview_open_apps" msgid="5659958952937994104">"Wyświetl ostatnie aplikacje"</string>
-    <string name="group_system_cycle_forward" msgid="5478663965957647805">"Przełącz się do przodu między ostatnimi aplikacjami"</string>
-    <string name="group_system_cycle_back" msgid="8194102916946802902">"Przełącz się wstecz między ostatnimi aplikacjami"</string>
+    <string name="group_system_cycle_forward" msgid="5478663965957647805">"Przełącz się między ostatnio używanymi aplikacjami (do przodu)"</string>
+    <string name="group_system_cycle_back" msgid="8194102916946802902">"Przełącz się między ostatnio używanymi aplikacjami (do tyłu)"</string>
     <string name="group_system_access_all_apps_search" msgid="1553588630154197469">"Otwórz listę aplikacji"</string>
     <string name="group_system_access_system_settings" msgid="8731721963449070017">"Otwórz ustawienia"</string>
     <string name="group_system_access_google_assistant" msgid="7210074957915968110">"Otwórz asystenta"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"Pokazuj ikony powiadomień o niskim priorytecie"</string>
     <string name="other" msgid="429768510980739978">"Inne"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"usunąć kartę"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"dodaj kafelek do ostatniej pozycji"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Przenieś kartę"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Dodaj kafelek do wybranej pozycji"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Przenieś do pozycji <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Dodaj w pozycji <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Nieprawidłowa pozycja."</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"wybrać użytkownika"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"Brak internetu"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"Otwórz ustawienia: <xliff:g id="ID_1">%s</xliff:g>."</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"Edytuj kolejność Szybkich ustawień."</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Menu zasilania"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Strona <xliff:g id="ID_1">%1$d</xliff:g> z <xliff:g id="ID_2">%2$d</xliff:g>"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"Ekran blokady"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"otwórz urządzenie"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"By otworzyć, użyj odcisku palca"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Wymagane uwierzytelnienie. Dotknij czytnika liniii papilarnych, by uwierzytelnić."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Aktywne połączenie"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobilna transmisja danych"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Połączono"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Tymczasowe połączenie"</string>
@@ -1473,7 +1462,7 @@
     <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Tak, zresetuj"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Anuluj"</string>
     <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Naciśnij klawisz"</string>
-    <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Kombinacja klawiszy jest już używana. Użyj innego klawisza."</string>
+    <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Ta kombinacja klawiszy jest już używana. Użyj innego klawisza."</string>
     <string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"Nie można ustawić skrótu."</string>
     <string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"Nawiguj za pomocą klawiatury"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 4ebc7ae..e7bcaa2 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -111,10 +111,8 @@
     <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="7415261783188749730">"Gravar a tela?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Gravar um app"</string>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"Gravar esta tela"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"Gravar %s"</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>
@@ -417,20 +415,13 @@
     <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="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Selecionado"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Som ambiente"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Lado esquerdo"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Lado direito"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Abrir para controles separados da esquerda e da direita"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Fechar para controle unificado"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Silenciar som ambiente"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Ativar som ambiente"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Ferramentas"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Legenda instantânea"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Observação"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"Mostrar ícones de notificações de baixa prioridade"</string>
     <string name="other" msgid="429768510980739978">"Outros"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"remover o bloco"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"adicionar o bloco à última posição"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mover bloco"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Adicionar o bloco à posição desejada"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mover para <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Adicionar à posição <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Posição inválida."</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"escolher o usuário"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"Sem Internet"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"Abrir configurações de <xliff:g id="ID_1">%s</xliff:g>."</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"Editar a ordem das Configurações rápidas."</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Menu liga/desliga"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Página <xliff:g id="ID_1">%1$d</xliff:g> de <xliff:g id="ID_2">%2$d</xliff:g>"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"Tela de bloqueio"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"acessar o dispositivo"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Use a impressão digital para abrir"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Autenticação obrigatória. Toque no sensor de impressão digital para autenticar."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Chamada em andamento"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Dados móveis"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Conectado"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Temporariamente conectado"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 2b49139..a9de961 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -111,10 +111,8 @@
     <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="7415261783188749730">"Gravar o ecrã?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Gravar uma app"</string>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"Gravar este ecrã"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"Gravar %s"</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>
@@ -1291,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"entrar no dispositivo"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Utilize a impressão digital para abrir"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Autenticação necessária. Toque no sensor de impressões digitais para autenticar."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Chamada telefónica em curso"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Dados móveis"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Ligado"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Ligado temporariamente"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 4ebc7ae..e7bcaa2 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -111,10 +111,8 @@
     <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="7415261783188749730">"Gravar a tela?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Gravar um app"</string>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"Gravar esta tela"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"Gravar %s"</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>
@@ -417,20 +415,13 @@
     <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="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Selecionado"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Som ambiente"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Lado esquerdo"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Lado direito"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Abrir para controles separados da esquerda e da direita"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Fechar para controle unificado"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Silenciar som ambiente"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Ativar som ambiente"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Ferramentas"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Legenda instantânea"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Observação"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"Mostrar ícones de notificações de baixa prioridade"</string>
     <string name="other" msgid="429768510980739978">"Outros"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"remover o bloco"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"adicionar o bloco à última posição"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mover bloco"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Adicionar o bloco à posição desejada"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mover para <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Adicionar à posição <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Posição inválida."</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"escolher o usuário"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"Sem Internet"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"Abrir configurações de <xliff:g id="ID_1">%s</xliff:g>."</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"Editar a ordem das Configurações rápidas."</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Menu liga/desliga"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Página <xliff:g id="ID_1">%1$d</xliff:g> de <xliff:g id="ID_2">%2$d</xliff:g>"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"Tela de bloqueio"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"acessar o dispositivo"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Use a impressão digital para abrir"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Autenticação obrigatória. Toque no sensor de impressão digital para autenticar."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Chamada em andamento"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Dados móveis"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Conectado"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Temporariamente conectado"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index 6f49a80..7fb1169 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -111,10 +111,8 @@
     <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="7415261783188749730">"Înregistrezi ecranul?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Înregistrează o aplicație"</string>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"Înregistrează acest ecran"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"Înregistrează %s"</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>
@@ -417,20 +415,13 @@
     <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="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Selectat"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Împrejurimi"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Stânga"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Dreapta"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Extinde comenzile separate la stânga și la dreapta"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Restrânge la comanda unificată"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Dezactivează sunetul ambiental"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Activează sunetul ambiental"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Instrumente"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Subtitrări live"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Notă"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"Afișează pictogramele de notificare cu prioritate redusă"</string>
     <string name="other" msgid="429768510980739978">"Altele"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"elimină cardul"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"adaugă cardul în ultima poziție"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Mută cardul"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Adaugă cardul în poziția dorită"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Mută pe poziția <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Adaugă pe poziția <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Poziție nevalidă."</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"alege utilizatorul"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"Fără conexiune la internet"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"Deschide setările <xliff:g id="ID_1">%s</xliff:g>."</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"Editează ordinea Setărilor rapide."</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Meniul de pornire"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Pagina <xliff:g id="ID_1">%1$d</xliff:g> din <xliff:g id="ID_2">%2$d</xliff:g>"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"Ecran de blocare"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"Accesează dispozitivul"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Folosește amprenta ca să deschizi"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Autentificare obligatorie. Atinge senzorul de amprentă pentru a te autentifica."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Apel telefonic în desfășurare"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Date mobile"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Conectat"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Conectat temporar"</string>
@@ -1470,7 +1459,7 @@
     <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Setările tastaturii"</string>
     <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Setează o comandă rapidă"</string>
     <string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Elimină"</string>
-    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Da, resetează"</string>
+    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Resetează"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Anulează"</string>
     <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Apasă tasta"</string>
     <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Combinația de taste este deja folosită. Încearcă altă tastă."</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index c534c73..448c245 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Текущее уведомление для записи видео с экрана"</string>
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Начать запись экрана?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Записывать одно приложение"</string>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"Записывать этот экран"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"Записывать экран \"%s\""</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>
@@ -417,20 +415,13 @@
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Не удалось обновить набор настроек."</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Набор настроек"</string>
     <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Выбрано"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Окружающие звуки"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Левый"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Правый"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Разделить на левый и правый элемент управления"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Объединить в один элемент управления"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Заглушить окружающие звуки"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Не заглушать окружающие звуки"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Инструменты"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Автоматические субтитры"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Заметка"</string>
@@ -864,11 +855,11 @@
     <string name="keyboard_shortcut_group_system_recents" msgid="8628108256824616927">"Недавние"</string>
     <string name="keyboard_shortcut_group_system_back" msgid="1055709713218453863">"Назад"</string>
     <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"Уведомления"</string>
-    <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"Быстрые клавиши"</string>
+    <string name="keyboard_shortcut_group_system_shortcuts_helper" msgid="4856808328618265589">"Сочетания клавиш"</string>
     <string name="keyboard_shortcut_group_system_switch_input" msgid="952555530383268166">"Переключение раскладки"</string>
     <string name="keyboard_shortcut_join" msgid="3578314570034512676">"или"</string>
     <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Удалить поисковый запрос"</string>
-    <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Быстрые клавиши"</string>
+    <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Сочетания клавиш"</string>
     <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Поиск сочетаний клавиш"</string>
     <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Нет сочетаний клавиш."</string>
     <string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"Система"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"Показывать значки уведомлений с низким приоритетом"</string>
     <string name="other" msgid="429768510980739978">"Другое"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"удалить панель"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"добавить параметр в конец"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Переместить панель"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Выбрать, куда добавить параметр"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Переместить на позицию <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Добавить на позицию <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Недопустимое расположение."</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"выбрать пользователя"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"Нет подключения к Интернету."</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"Открыть настройки <xliff:g id="ID_1">%s</xliff:g>."</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"Изменить порядок быстрых настроек."</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Меню кнопки питания"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Страница <xliff:g id="ID_1">%1$d</xliff:g> из <xliff:g id="ID_2">%2$d</xliff:g>"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"Заблокированный экран"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"указать устройство"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Используйте отпечаток пальца для входа."</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Требуется аутентификация. Приложите палец к сканеру отпечатков."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Текущий вызов"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Мобильный интернет"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Подключено"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Временное подключение"</string>
@@ -1422,7 +1411,7 @@
     <string name="connected_display_icon_desc" msgid="6373560639989971997">"Экран подключен"</string>
     <string name="privacy_dialog_title" msgid="7839968133469098311">"Микрофон и камера"</string>
     <string name="privacy_dialog_summary" msgid="2458769652125995409">"Недавнее использование приложениями"</string>
-    <string name="privacy_dialog_more_button" msgid="7610604080293562345">"Посмотреть недавний доступ"</string>
+    <string name="privacy_dialog_more_button" msgid="7610604080293562345">"История доступа"</string>
     <string name="privacy_dialog_done_button" msgid="4504330708531434263">"Готово"</string>
     <string name="privacy_dialog_expand_action" msgid="9129262348628331377">"Развернуть и показать параметры"</string>
     <string name="privacy_dialog_collapse_action" msgid="277419962019466347">"Свернуть"</string>
@@ -1444,17 +1433,17 @@
     <string name="shortcut_helper_category_multitasking" msgid="7413381961404090136">"Многозадачность"</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>
+    <string name="shortcut_helper_category_app_shortcuts" msgid="8010249408308587117">"Приложения"</string>
     <string name="shortcut_helper_category_current_app_shortcuts" msgid="4017840565974573628">"Это приложение"</string>
     <string name="shortcut_helper_category_a11y" msgid="6314444792641773464">"Специальные возможности"</string>
-    <string name="shortcut_helper_title" msgid="8567500639300970049">"Быстрые клавиши"</string>
-    <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Как настроить быстрые клавиши"</string>
+    <string name="shortcut_helper_title" msgid="8567500639300970049">"Сочетания клавиш"</string>
+    <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"Настройки сочетаний клавиш"</string>
     <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Удалить сочетание клавиш?"</string>
     <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Сбросить настройки?"</string>
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Нажмите клавишу, чтобы назначить сочетание клавиш."</string>
     <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Настроенное сочетание будет безвозвратно удалено."</string>
-    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Все пользовательские ярлыки будут безвозвратно удалены."</string>
-    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Найти быстрые клавиши"</string>
+    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Все настроенные сочетания клавиш будут безвозвратно удалены."</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_meta_key" msgid="3989315044342124818">"Значок клавиши Meta для выполнения действия"</string>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index 78331a0..d5c90b5 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"තිර පටිගත කිරීමේ සැසියක් සඳහා කෙරෙන දැනුම් දීම"</string>
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"ඔබේ තිරය පටිගත කරන්න ද?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"එක් යෙදුමක් පටිගත කරන්න"</string>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"මෙම තිරය පටිගත කරන්න"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"%s පටිගත කරන්න"</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>
@@ -417,20 +415,13 @@
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"පෙර සැකසීම යාවත්කාලීන කළ නොහැකි විය"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"පෙරසැකසුම"</string>
     <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"තෝරන ලදි"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"වටපිටාව"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"වම"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"දකුණ"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"වමට සහ දකුණට වෙන් වූ පාලන වෙත පුළුල් කරන්න"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"ඒකාබද්ධ පාලනයට හකුළන්න"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"අවට පරිසරය නිහඬ කරන්න"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"අවට නිහඬ නොකරන්න"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"මෙවලම්"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"සජීවී සිරස්තල"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"සටහන"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"අඩු ප්‍රමුඛතා දැනුම්දීම් අයිකන පෙන්වන්න"</string>
     <string name="other" msgid="429768510980739978">"වෙනත්"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ටයිල් ඉවත් කරන්න"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"ටයිල් එක අවසාන ස්ථානයට එක් කරන්න"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ටයිල් ගෙන යන්න"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"අපේක්ෂිත ස්ථානයට ටයිල් එක එක් කරන්න"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> වෙත ගෙන යන්න"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> ස්ථානයට එක් කරන්න"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"ස්ථානය අවලංගුයි."</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"පරිශීලක තෝරන්න"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"අන්තර්ජාලය නැත"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"<xliff:g id="ID_1">%s</xliff:g> සැකසීම් විවෘත කරන්න."</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"ඉක්මන් සැකසීම්වල අනුපිළිවෙල සංස්කරණය කරන්න."</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"බල මෙනුව"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_2">%2$d</xliff:g> න් <xliff:g id="ID_1">%1$d</xliff:g>"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"අගුලු තිරය"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"උපාංගය ඇතුළු කරන්න"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"විවෘත කිරීමට ඇඟිලි සලකුණ භාවිත කරන්න"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"සත්‍යාපනය අවශ්‍යයි. සත්‍යාපනය කිරීමට ඇඟිලි සලකුණු සංවේදකය ස්පර්ශ කරන්න."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"ක්‍රියාත්මක වන දුරකථන ඇමතුම"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"ජංගම දත්ත"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"සම්බන්ධයි"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"තාවකාලිකව සම්බන්ධ කළා"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index cf8972e..af84fd1 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -111,10 +111,8 @@
     <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="7415261783188749730">"Chcete nahrávať obrazovku?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Nahrávať jednu aplikáciu"</string>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"Nahrať túto obrazovku"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"Nahrať obrazovku %s"</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>
@@ -417,20 +415,13 @@
     <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="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Vybrané"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Okolie"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Vľavo"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Vpravo"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Rozbaliť na samostatné ovládanie ľavej a pravej strany"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Zbaliť na jednotné ovládanie"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Vypnúť zvuk okolia"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Zapnúť zvuk okolia"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Nástroje"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Živý prepis"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Poznámka"</string>
@@ -886,8 +877,8 @@
     <string name="group_system_go_back" msgid="2730322046244918816">"Prechod späť"</string>
     <string name="group_system_access_home_screen" msgid="4130366993484706483">"Prechod na plochu"</string>
     <string name="group_system_overview_open_apps" msgid="5659958952937994104">"Zobrazenie nedávnych aplikácií"</string>
-    <string name="group_system_cycle_forward" msgid="5478663965957647805">"Cyklické prechádzanie dopredu po nedávnych aplikáciách"</string>
-    <string name="group_system_cycle_back" msgid="8194102916946802902">"Cyklické prechádzanie dozadu po nedávnych aplikáciách"</string>
+    <string name="group_system_cycle_forward" msgid="5478663965957647805">"Cyklické prechádzanie dopredu nedávnymi aplikáciámi"</string>
+    <string name="group_system_cycle_back" msgid="8194102916946802902">"Cyklické prechádzanie dozadu nedávnymi aplikáciami"</string>
     <string name="group_system_access_all_apps_search" msgid="1553588630154197469">"Otvorenie zoznamu aplikácií"</string>
     <string name="group_system_access_system_settings" msgid="8731721963449070017">"Otvorenie nastavení"</string>
     <string name="group_system_access_google_assistant" msgid="7210074957915968110">"Otvoriť asistenta"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"Zobraziť ikony upozornení s nízkou prioritou"</string>
     <string name="other" msgid="429768510980739978">"Ďalšie"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"odstrániť kartu"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"pridáte kartu na poslednú pozíciu"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Presunúť kartu"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Pridať kartu na požadovanú pozíciu"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Presunúť na <xliff:g id="POSITION">%1$d</xliff:g>. pozíciu"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Pridať na <xliff:g id="POSITION">%1$d</xliff:g>. pozíciu"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Pozícia je neplatná."</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"vybrať používateľa"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"Žiadny internet"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"Otvoriť nastavenia <xliff:g id="ID_1">%s</xliff:g>"</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"Upraviť poradie rýchlych nastavení"</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Ponuka vypínača"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Strana <xliff:g id="ID_1">%1$d</xliff:g> z <xliff:g id="ID_2">%2$d</xliff:g>"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"Uzamknutá obrazovka"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"vstúpte do zariadenia"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Otvorte odtlačkom prsta"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Vyžaduje sa overenie. Dotknite sa senzora odtlačkov prstov."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Prebiehajúci telefonický hovor"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobilné dáta"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Pripojené"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Dočasne pripojené"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index d0063fd..1242774 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Nenehno obveščanje o seji snemanja zaslona"</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>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"Snemanje tega zaslona"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"Snemanje zaslona %s"</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>
@@ -417,20 +415,13 @@
     <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="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Izbrano"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Okolica"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Levo"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Desno"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Razširitev na ločene kontrolnike za levo in desno stran"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Strnitev v enotni kontrolnik"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Izklop okoliškega zvoka"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Vklop okoliškega zvoka"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Orodja"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Samodejni podnapisi"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Opomba"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"Pokaži ikone obvestil z nizko stopnjo prednosti"</string>
     <string name="other" msgid="429768510980739978">"Drugo"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"odstranitev ploščice"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"dodajanje ploščice na zadnji položaj"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Premik ploščice"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Dodaj ploščico na želeno mesto"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Premik na položaj <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Dodajanje na položaj <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Položaj je neveljaven."</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"izbiro uporabnika"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"Brez internetne povezave"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"Odpri nastavitve za <xliff:g id="ID_1">%s</xliff:g>."</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"Urejanje vrstnega reda hitrih nastavitev."</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Meni za vklop/izklop"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_1">%1$d</xliff:g>. stran od <xliff:g id="ID_2">%2$d</xliff:g>"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"Zaklenjen zaslon"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"vstop v napravo"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Odprite s prstnim odtisom"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Zahtevano je preverjanje pristnosti. Za preverjanje pristnosti se dotaknite tipala prstnih odtisov."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Poteka klic"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Prenos podatkov v mobilnem omrežju"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Povezano"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Začasno vzpostavljena povezava"</string>
@@ -1468,9 +1457,9 @@
     <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"poševnica naprej"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Ročica za vlečenje"</string>
     <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Nastavitve tipkovnice"</string>
-    <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Nastavite bližnjico"</string>
+    <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Nast. bliž."</string>
     <string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Odstrani"</string>
-    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Da, ponastavi"</string>
+    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Da"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Prekliči"</string>
     <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Pritisnite tipko"</string>
     <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Kombinacija tipk je že v uporabi. Poskusite z drugo tipko."</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index 803a5e5..c3d1353 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -111,10 +111,8 @@
     <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="7415261783188749730">"Të regjistrohet ekrani?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Regjistro një aplikacion"</string>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"Regjistro këtë ekran"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"Regjistro %s"</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>
@@ -417,20 +415,13 @@
     <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="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Zgjedhur"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Ambienti rrethues"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Majtas"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Djathtas"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Zgjero te kontrollet e veçuara majtas dhe djathtas"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Palos te kontrolli i unifikuar"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Vendos në heshtje ambientin rrethues"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Anulo vendosjen në heshtje të ambientit rrethues"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Veglat"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Titrat në çast"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Shënim"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"Shfaq ikonat e njoftimeve me përparësi të ulët"</string>
     <string name="other" msgid="429768510980739978">"Të tjera"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"hiq pllakëzën"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"shtuar pllakëzën në pozicionin e fundit"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Zhvendos pllakëzën"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Shto pllakëzën në pozicionin e dëshiruar"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Zhvendos te <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Shto te pozicioni <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Pozicion i pavlefshëm."</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"zgjidh përdoruesin"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"Nuk ka internet"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"Hap cilësimet e <xliff:g id="ID_1">%s</xliff:g>."</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"Modifiko renditjen e \"Cilësimeve të shpejta\"."</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Menyja e energjisë"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Faqja <xliff:g id="ID_1">%1$d</xliff:g> nga <xliff:g id="ID_2">%2$d</xliff:g>"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"Ekrani i kyçjes"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"për të hyrë në pajisje"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Përdor gjurmën e gishtit për ta hapur"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Kërkohet vërtetimi. Prek sensorin e gjurmës së gishtit për t\'u vërtetuar."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Telefonatë në vazhdim"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Të dhënat celulare"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Lidhur"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Lidhur përkohësisht"</string>
@@ -1470,7 +1459,7 @@
     <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Cilësimet e tastierës"</string>
     <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Cakto shkurtoren"</string>
     <string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Hiq"</string>
-    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Po, rivendosi"</string>
+    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Po"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Anulo"</string>
     <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Shtyp tastin"</string>
     <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Kombinimi i tasteve është tashmë në përdorim. Provo një tast tjetër."</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 1787072..344b012 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Обавештење о сесији снимања екрана је активно"</string>
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Желите да снимите екран?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Сними једну апликацију"</string>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"Сними овај екран"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"Сними %s"</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>
@@ -417,20 +415,13 @@
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Ажурирање задатих подешавања није успело"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Унапред одређена подешавања"</string>
     <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Изабрано"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Окружење"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Лево"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Десно"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Прошири на контроле раздвојене на леву и десну страну"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Скупи у јединствену контролу"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Искључи звук окружења"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Укључи звук окружења"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Алатке"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Титл уживо"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Белешка"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"Прикажи иконе обавештења ниског приоритета"</string>
     <string name="other" msgid="429768510980739978">"Друго"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"уклонили плочицу"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"додали плочицу на последњу позицију"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Преместите плочицу"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Додајте плочицу на жељену позицију"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Преместите на <xliff:g id="POSITION">%1$d</xliff:g>. позицију"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Додајте на <xliff:g id="POSITION">%1$d</xliff:g>. позицију"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Позиција је неважећа."</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"одабрали корисника"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"Нема интернета"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"Отвори подешавања за <xliff:g id="ID_1">%s</xliff:g>."</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"Измените редослед Брзих подешавања."</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Мени дугмета за укључивање"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_1">%1$d</xliff:g>. страна од <xliff:g id="ID_2">%2$d</xliff:g>"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"Закључан екран"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"унесите уређај"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Отворите помоћу отиска прста"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Потребна је потврда идентитета. Додирните сензор за отисак прста да бисте потврдили идентитет."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Актуелни телефонски позив"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Мобилни подаци"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Повезано"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Привремено повезано"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 6d9f721..4d862d9 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Avisering om att skärminspelning pågår"</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>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"Spela in den här skärmen"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"Spela in %s"</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>
@@ -417,20 +415,13 @@
     <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="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Markerad"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Omgivningsläge"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Vänster"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Höger"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Utöka till kontroller till vänster och höger"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Komprimera till enhetlig kontroll"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Stäng av omgivningsljudet"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Slå på omgivningsljudet"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Verktyg"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Live Caption"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Anteckning"</string>
@@ -1298,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"ange enhet"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Öppna med fingeravtryck"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Autentisering krävs. Identifiera dig genom att trycka på fingeravtryckssensorn."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Pågående samtal"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobildata"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Ansluten"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Tillfälligt ansluten"</string>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 6fb8f08..7317654 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Arifa inayoendelea ya kipindi cha kurekodi skrini"</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>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"Rekodi skrini hii"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"Rekodi %s"</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>
@@ -417,20 +415,13 @@
     <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="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Umechagua"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Mazingira"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Kushoto"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Kulia"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Panua iwe vidhibiti vilivyotenganishwa kushoto na kulia"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Kunja iwe kidhibiti cha pamoja"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Zima sauti ya mazingira"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Rejesha sauti ya mazingira"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Zana"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Manukuu Papo Hapo"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Dokezo"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"Onyesha aikoni za arifa zisizo muhimu"</string>
     <string name="other" msgid="429768510980739978">"Nyingine"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ondoa kigae"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"weka kigae kwenye nafasi ya mwisho"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Hamisha kigae"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Weka kigae kwenye nafasi unayopenda"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Hamishia kwenye <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Ongeza kwenye nafasi ya <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Nafasi si sahihi."</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"chagua mtumiaji"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"Hakuna intaneti"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"Fungua mipangilio ya <xliff:g id="ID_1">%s</xliff:g>."</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"Badilisha mpangilio wa Mipangilio ya Haraka."</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Menyu ya kuzima/kuwasha"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Ukurasa wa <xliff:g id="ID_1">%1$d</xliff:g> kati ya <xliff:g id="ID_2">%2$d</xliff:g>"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"Skrini iliyofungwa"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"weka kifaa"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Tumia alama ya kidole kufungua"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Uthibitishaji unahitajika. Gusa kitambua alama ya kidole ili uthibitishe."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Simu inayoendelea"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Data ya mtandao wa simu"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Imeunganishwa"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Imeunganishwa kwa muda"</string>
@@ -1468,9 +1457,9 @@
     <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"mkwaju wa mbele"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Aikoni ya buruta"</string>
     <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Mipangilio ya Kibodi"</string>
-    <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Weka njia ya mkato"</string>
+    <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Weka mkato"</string>
     <string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Ondoa"</string>
-    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Ndiyo, rejesha"</string>
+    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Ndiyo"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"Acha"</string>
     <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Bonyeza kitufe"</string>
     <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Tayari unatumia mchanganyiko wa vitufe. Jaribu kitufe kingine."</string>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index ead1f83..5a7defa 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"திரை ரெக்கார்டிங் அமர்விற்கான தொடர் அறிவிப்பு"</string>
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"உங்கள் திரையை ரெக்கார்டு செய்யவா?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"ஓர் ஆப்ஸை ரெக்கார்டு செய்தல்"</string>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"இந்தத் திரையை ரெக்கார்டு செய்"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"ரெக்கார்டு %s"</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>
@@ -417,20 +415,13 @@
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"முன்னமைவைப் புதுப்பிக்க முடியவில்லை"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"முன்னமைவு"</string>
     <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"தேர்ந்தெடுக்கப்பட்டது"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"சுற்றுப்புறங்கள்"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"இடது"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"வலது"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"இடது மற்றும் வலதுபுறம் உள்ள கட்டுப்பாடுகளை விரிவாக்கும்"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"ஒருங்கிணைந்த கட்டுப்பாட்டுக்குச் சுருக்கும்"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"சுற்றுப்புறங்களின் ஒலியை அடக்கும்"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"சுற்றுப்புறங்களின் ஒலியை இயக்கும்"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"கருவிகள்"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"உடனடி வசனம்"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"குறிப்பு"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"குறைந்த முன்னுரிமை உள்ள அறிவிப்பு ஐகான்களைக் காட்டு"</string>
     <string name="other" msgid="429768510980739978">"மற்றவை"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"கட்டத்தை அகற்றும்"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"கடைசி இடத்தில் கட்டத்தைச் சேர்க்கலாம்"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"கட்டத்தை நகர்த்து"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"விரும்பிய இடத்தில் கட்டத்தைச் சேர்க்கலாம்"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g>க்கு நகர்த்தும்"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g>ல் சேர்க்கும்"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"நிலை தவறானது."</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"பயனரைத் தேர்வுசெய்யவும்"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"இணைய இணைப்பு இல்லை"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"<xliff:g id="ID_1">%s</xliff:g> அமைப்புகளைத் திற."</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"விரைவு அமைப்புகளின் வரிசையை மாற்றலாம்."</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"பவர் மெனு"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"பக்கம் <xliff:g id="ID_1">%1$d</xliff:g> / <xliff:g id="ID_2">%2$d</xliff:g>"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"லாக் ஸ்கிரீன்"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"சாதனத்தைத் திற"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"கைரேகையைப் பயன்படுத்தி திறந்திடுங்கள்"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"அங்கீகாரம் தேவை. கைரேகை சென்சாரைத் தொட்டு அங்கீகரியுங்கள்."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"செயலில் உள்ள மொபைல் அழைப்பு"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"மொபைல் டேட்டா"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"இணைக்கப்பட்டது"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"தற்காலிகமாக இணைக்கப்பட்டுள்ளது"</string>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 73a4250..1be6ce6 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"స్క్రీన్ రికార్డ్ సెషన్ కోసం ఆన్‌గోయింగ్ నోటిఫికేషన్"</string>
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"మీ స్క్రీన్‌ను రికార్డ్ చేయాలా?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"ఒక యాప్‌ను రికార్డ్ చేయండి"</string>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"ఈ స్క్రీన్‌ను రికార్డ్ చేయండి"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"%s రికార్డ్ చేయండి"</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>
@@ -417,20 +415,13 @@
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"ప్రీసెట్‌ను అప్‌డేట్ చేయడం సాధ్యపడలేదు"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"ప్రీసెట్"</string>
     <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"ఎంచుకోబడింది"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"పరిసరాలు"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"ఎడమ వైపునకు"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"కుడి వైపునకు"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"ఎడమ, కుడి అని వేరు చేయబడిన కంట్రోల్స్‌కు విస్తరించండి"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"యూనిఫైడ్ కంట్రోల్‌కు కుదించండి"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"పరిసరాలను మ్యూట్ చేయండి"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"పరిసరాలను అన్‌మ్యూట్ చేయండి"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"టూల్స్"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"లైవ్ క్యాప్షన్"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"గమనిక"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"తక్కువ ప్రాధాన్యత నోటిఫికేషన్ చిహ్నాలను చూపించు"</string>
     <string name="other" msgid="429768510980739978">"ఇతరం"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"టైల్‌ను తీసివేయండి"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"చివరి పొజిషన్‌కు టైల్‌ను జోడించండి"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"టైల్‌ను తరలించండి"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"కావలసిన పొజిషన్‌కు టైల్‌ను జోడించండి"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g>కు తరలించండి"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> స్థానానికి జోడించండి"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"ప్రస్తుత పొజిషన్ చెల్లదు."</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"యూజర్‌ను ఎంపిక చేయండి"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"ఇంటర్నెట్ లేదు"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"<xliff:g id="ID_1">%s</xliff:g> సెట్టింగ్‌లను తెరవండి."</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"క్విక్ సెట్టింగ్‌ల క్రమాన్ని ఎడిట్ చేయండి."</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"పవర్ మెనూ"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_2">%2$d</xliff:g>లో <xliff:g id="ID_1">%1$d</xliff:g>వ పేజీ"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"లాక్ స్క్రీన్"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"పరికరాన్ని ఎంటర్ చేయండి"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"తెరవడానికి వేలిముద్రను ఉపయోగించండి"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"ప్రామాణీకరణ అవసరం. ప్రామాణీకరించడానికి వేలిముద్ర సెన్సార్‌ను తాకండి."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"ఫోన్ కాల్ జరుగుతోంది"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"మొబైల్ డేటా"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"కనెక్ట్ చేయబడింది"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"తాత్కాలికంగా కనెక్ట్ చేయబడింది"</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index c46a9d7..3770add 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"การแจ้งเตือนต่อเนื่องสำหรับเซสชันการบันทึกหน้าจอ"</string>
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"บันทึกหน้าจอไหม"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"บันทึกแอปเดียว"</string>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"บันทึกหน้าจอนี้"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"บันทึก %s"</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>
@@ -417,20 +415,13 @@
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"ไม่สามารถอัปเดตค่าที่กำหนดล่วงหน้า"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"ค่าที่กำหนดล่วงหน้า"</string>
     <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"เลือกแล้ว"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"เสียงแวดล้อม"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"ซ้าย"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"ขวา"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"ขยายเป็นการควบคุมที่แยกด้านซ้ายและขวา"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"ยุบเป็นการควบคุมแบบรวม"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"ปิดเสียงแวดล้อม"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"เปิดเสียงแวดล้อม"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"เครื่องมือ"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"คำบรรยายสด"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"จดบันทึก"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"แสดงไอคอนการแจ้งเตือนลำดับความสำคัญต่ำ"</string>
     <string name="other" msgid="429768510980739978">"อื่นๆ"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"นำชิ้นส่วนออก"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"เพิ่มการ์ดไปยังตำแหน่งสุดท้าย"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ย้ายชิ้นส่วน"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"เพิ่มการ์ดไปยังตำแหน่งที่ต้องการ"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"ย้ายไปที่ <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"เพิ่มไปยังตำแหน่ง <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"ตำแหน่งไม่ถูกต้อง"</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"เลือกผู้ใช้"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"ไม่มีอินเทอร์เน็ต"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"เปิดการตั้งค่า <xliff:g id="ID_1">%s</xliff:g>"</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"แก้ไขลำดับของการตั้งค่าด่วน"</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"เมนูเปิด/ปิด"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"หน้า <xliff:g id="ID_1">%1$d</xliff:g> จาก <xliff:g id="ID_2">%2$d</xliff:g>"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"หน้าจอล็อก"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"เข้าถึงอุปกรณ์"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"ใช้ลายนิ้วมือเพื่อเปิด"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"ต้องมีการตรวจสอบสิทธิ์ แตะเซ็นเซอร์ลายนิ้วมือเพื่อตรวจสอบสิทธิ์"</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"กำลังโทรอยู่"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"อินเทอร์เน็ตมือถือ"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"เชื่อมต่อแล้ว"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"เชื่อมต่อแล้วชั่วคราว"</string>
@@ -1453,7 +1442,7 @@
     <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"รีเซ็ตกลับเป็นค่าเริ่มต้นไหม"</string>
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"กดแป้นเพื่อกำหนดแป้นพิมพ์ลัด"</string>
     <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"การดำเนินการนี้จะลบแป้นพิมพ์ลัดที่กำหนดเองอย่างถาวร"</string>
-    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"การดำเนินการนี้จะลบทางลัดที่กำหนดเองทั้งหมดอย่างถาวร"</string>
+    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"การดำเนินการนี้จะลบแป้นพิมพ์ลัดที่กำหนดเองทั้งหมดอย่างถาวร"</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>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 6953458..d28915e 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -111,10 +111,8 @@
     <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="7415261783188749730">"I-record ang iyong screen?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Mag-record ng isang app"</string>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"I-record ang screen na ito"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"I-record ang %s"</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>
@@ -417,20 +415,13 @@
     <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="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Napili"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Paligid"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Kaliwa"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Kanan"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"I-expand sa kaliwa at kanang magkahiwalay na mga kontrol"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"I-collapse sa pinag-isang kontrol"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"I-mute ang paligid"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"I-unmute ang paligid"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Mga Tool"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Instant Caption"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Tala"</string>
@@ -1298,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"ilagay ang device"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Gamitin ang fingerprint para buksan"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Kailangan ng pag-authenticate. Pindutin ang sensor para sa fingerprint para mag-authenticate."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Kasalukuyang may tawag sa telepono"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobile data"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Nakakonekta"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Pansamantalang nakakonekta"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 441cec1..29a5763 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Ekran kaydı oturumu için devam eden bildirim"</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>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"Bu ekranı kaydet"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"%s ekranını kaydet"</string>
     <string name="screenrecord_permission_dialog_warning_entire_screen" msgid="1321758636709366068">"Ekranın tamamını kaydederken 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>
@@ -417,20 +415,13 @@
     <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="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Seçili"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Çevredeki sesler"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Sol"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Sağ"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Sol ve sağ kontrolleri ayırarak genişlet"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Kontrolleri birleştirerek daralt"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Çevredeki sesleri kapat"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Çevredeki sesleri aç"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Araçlar"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Canlı Altyazı"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Not"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"Düşük öncelikli bildirim simgelerini göster"</string>
     <string name="other" msgid="429768510980739978">"Diğer"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"Kutuyu kaldırmak için"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"kutuyu son konuma ekleyin"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Kutuyu taşı"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"kutuyu istediğiniz konuma ekleyin"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> konumuna taşı"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"<xliff:g id="POSITION">%1$d</xliff:g> konumuna ekle"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Konum geçersiz."</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"kullanıcı seç"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"İnternet yok"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"<xliff:g id="ID_1">%s</xliff:g> ayarlarını aç."</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"Hızlı Ayarlar\'ın sırasını düzenleyin."</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Güç menüsü"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Sayfa <xliff:g id="ID_1">%1$d</xliff:g> / <xliff:g id="ID_2">%2$d</xliff:g>"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"Kilit ekranı"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"cihaz girin"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Açmak için parmak izi kullanın"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Kimlik doğrulaması gerekiyor. Kimlik doğrulaması için parmak izi sensörüne dokunun."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Devam eden telefon görüşmesi"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobil veri"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Bağlı"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Geçici olarak bağlandı"</string>
@@ -1468,9 +1457,9 @@
     <string name="shortcut_helper_key_combinations_forward_slash" msgid="1238652537199346970">"eğik çizgi"</string>
     <string name="shortcut_helper_content_description_drag_handle" msgid="5092426406009848110">"Sürükleme tutamacı"</string>
     <string name="shortcut_helper_keyboard_settings_buttons_label" msgid="6720967595915985259">"Klavye Ayarları"</string>
-    <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Kısayol ayarla"</string>
+    <string name="shortcut_helper_customize_dialog_set_shortcut_button_label" msgid="4754492225010429382">"Ayarla"</string>
     <string name="shortcut_helper_customize_dialog_remove_button_label" msgid="6546386970440176552">"Kaldır"</string>
-    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Evet, sıfırlansın"</string>
+    <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"Sıfırla"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"İptal"</string>
     <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"Tuşa basın"</string>
     <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"Tuş kombinasyonu zaten kullanılıyor. Başka bir tuş deneyin."</string>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index af70c6f..ea839cc 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Сповіщення про сеанс запису екрана"</string>
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"Записати відео з екрана?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"Записувати один додаток"</string>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"Записати цей екран"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"Записати екран \"%s\""</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>
@@ -417,20 +415,13 @@
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Не вдалось оновити набір налаштувань"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Набір налаштувань"</string>
     <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Вибрано"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Звуки оточення"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Ліворуч"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Праворуч"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Розгорнути в окремі елементи керування ліворуч і праворуч"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Згорнути в єдиний елемент керування"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Вимкнути звуки оточення"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Увімкнути звуки оточення"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Інструменти"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Живі субтитри"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Нотатка"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"Показувати значки сповіщень із низьким пріоритетом"</string>
     <string name="other" msgid="429768510980739978">"Інше"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"вилучити опцію"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"додати панель на останню позицію"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Перемістити опцію"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"додати панель на потрібну позицію"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Перемістити на позицію <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Додати на позицію <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Позиція недійсна."</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"вибрати користувача"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"Немає Інтернету"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"Відкрити налаштування <xliff:g id="ID_1">%s</xliff:g>."</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"Змінити порядок швидких налаштувань."</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Меню кнопки живлення"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Сторінка <xliff:g id="ID_1">%1$d</xliff:g> з <xliff:g id="ID_2">%2$d</xliff:g>"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"Заблокований екран"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"відкрити пристрій"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Щоб відкрити, використайте відбиток пальця"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Пройдіть автентифікацію. Для цього торкніться сканера відбитків пальців."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Активний телефонний виклик"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Мобільний трафік"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Підключено"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Тимчасово з’єднано"</string>
@@ -1454,7 +1443,7 @@
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Натисніть клавішу, щоб призначити комбінацію клавіш"</string>
     <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Вашу власну комбінацію клавіш буде видалено назавжди."</string>
     <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Усі ваші власні комбінації клавіш буде видалено назавжди."</string>
-    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Комбінації клавіш для пошуку"</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_meta_key" msgid="3989315044342124818">"Значок клавіші дії або метаклавіші"</string>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index 28aa1bb..63fa93d 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"اسکرین ریکارڈ سیشن کیلئے جاری اطلاع"</string>
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"آپ کی اسکرین ریکارڈ کریں؟"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"ایک ایپ ریکارڈ کریں"</string>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"یہ اسکرین ریکارڈ کریں"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"‏‫‎%s ریکارڈ کریں"</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>
@@ -417,20 +415,13 @@
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"پہلے سے ترتیب شدہ کو اپ ڈیٹ نہیں کیا جا سکا"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"پہلے سے ترتیب شدہ"</string>
     <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"منتخب کردہ"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"اطراف"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"دائیں"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"بائیں"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"بائیں اور دائیں علیحدہ کردہ کنٹرولز کو پھیلائیں"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"یونیفائیڈ کنٹرول کیلئے سکیڑیں"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"اطراف کو خاموش کریں"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"اطراف کی آواز چالو کریں"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"ٹولز"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"لائیو کیپشن"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"نوٹ"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"کم ترجیحی اطلاع کے آئیکنز دکھائیں"</string>
     <string name="other" msgid="429768510980739978">"دیگر"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"ٹائل ہٹائیں"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"ٹائل کو آخری پوزیشن پر شامل کریں"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"ٹائل منتقل کریں"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"ٹائل کو مطلوبہ پوزیشن پر شامل کریں"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"<xliff:g id="POSITION">%1$d</xliff:g> میں منتقل کریں"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"پوزیشن <xliff:g id="POSITION">%1$d</xliff:g> میں شامل کریں"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"پوزیشن غلط ہے۔"</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"صارف منتخب کریں"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"انٹرنیٹ نہیں ہے"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"<xliff:g id="ID_1">%s</xliff:g> ترتیبات کھولیں۔"</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"فوری ترتیبات کی ترتیب میں ترمیم کریں۔"</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"پاور مینیو"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"صفحہ <xliff:g id="ID_1">%1$d</xliff:g> از <xliff:g id="ID_2">%2$d</xliff:g>"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"مقفل اسکرین"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"آلہ درج کریں"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"کھولنے کے لیے فنگر پرنٹ کا استعمال کریں"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"توثیق مطلوب ہے۔ توثیق کرنے کے لیے فنگر پرنٹ سینسر کو ٹچ کریں۔"</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"جاری فون کال"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"موبائل ڈیٹا"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"منسلک ہے"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"عارضی طور پر منسلک ہے"</string>
@@ -1452,8 +1441,8 @@
     <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"شارٹ کٹ ہٹائیں؟"</string>
     <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"ڈیفالٹ پر واپس ری سیٹ کریں؟"</string>
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"شارٹ کٹ تفویض کرنے کے لیے کلید کو دبائیں"</string>
-    <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"یہ آپ کا حسب ضرورت شارٹ کٹ مستقل طور پر حذف کر دے گا۔"</string>
-    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"یہ آپ کے تمام حسب ضرورت شارٹ کٹس کو مستقل طور پر حذف کر دے گا۔"</string>
+    <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"اس سے آپ کا حسب ضرورت شارٹ کٹ مستقل طور پر حذف ہو جائے گا۔"</string>
+    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"اس سے آپ کے تمام حسب ضرورت شارٹ کٹس مستقل طور پر حذف ہو جائیں گے۔"</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>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 394a6e4..a20f42d 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Ekrandan yozib olish seansi uchun joriy bildirishnoma"</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>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"Bu ekranni yozib olish"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"Yozib olish: %s"</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 ilovada 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>
@@ -417,20 +415,13 @@
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"Andoza yangilanmadi"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Andoza"</string>
     <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Tanlangan"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Atrof-muhit"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Chap"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Oʻng"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Chap va oʻngga ajratilgan boshqaruv elementlariga yoyish"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Yagona boshqaruvga yigʻish"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Atrof-muhitni ovozsiz qilish"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Atrof-muhitni sukutdan chiqarish"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Vositalar"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Jonli izoh"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Qayd"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"Muhim boʻlmagan bildirishnoma ikonkalarini koʻrsatish"</string>
     <string name="other" msgid="429768510980739978">"Boshqa"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"katakchani olib tashlash"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"kartochkani oxirgi oʻringa qoʻshish"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Katakchani boshqa joyga olish"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Kartochkani kerakli oʻringa qoʻshish"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Bu joyga olish: <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Bu joyga kiritish: <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Pozitsiya yaroqsiz."</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"foydalanuvchini tanlash"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"Internetga ulanmagansiz"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"<xliff:g id="ID_1">%s</xliff:g> sozlamalarini ochish."</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"Tezkor sozlamalar tartibini tahrirlash"</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Quvvat menyusi"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"<xliff:g id="ID_1">%1$d</xliff:g>-sahifa, jami: <xliff:g id="ID_2">%2$d</xliff:g> ta sahifa"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"Ekran qulfi"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"qurilmani ochish"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Ochish uchun barmoq izidan foydalaning"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Haqiqiylikni tekshirish talab etiladi. Autentifikatsiya uchun barmoq izi skaneriga tegining."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Joriy telefon chaqiruvi"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Mobil internet"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Ulangan"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Vaqtincha ulangan"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 81ac5e7..2a8fa4c 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -111,10 +111,8 @@
     <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="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>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"Ghi màn hình này"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"Ghi %s"</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>
@@ -417,20 +415,13 @@
     <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="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Đã chọn"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Âm lượng xung quanh"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Trái"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Phải"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Mở rộng thành các nút điều khiển tách biệt bên trái và bên phải"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Thu gọn thành nút điều khiển hợp nhất"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Tắt tiếng xung quanh"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Bật tiếng xung quanh"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Công cụ"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Phụ đề trực tiếp"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Ghi chú"</string>
@@ -857,7 +848,7 @@
     <string name="keyboard_key_move_end" msgid="99190401463834854">"Cuối"</string>
     <string name="keyboard_key_insert" msgid="4621692715704410493">"Insert"</string>
     <string name="keyboard_key_num_lock" msgid="7209960042043090548">"Num Lock"</string>
-    <string name="keyboard_key_numpad_template" msgid="7316338238459991821">"Bàn phím số <xliff:g id="NAME">%1$s</xliff:g>"</string>
+    <string name="keyboard_key_numpad_template" msgid="7316338238459991821">"Số <xliff:g id="NAME">%1$s</xliff:g> trên bàn phím số"</string>
     <string name="notif_inline_reply_remove_attachment_description" msgid="7954075334095405429">"Xóa tệp đính kèm"</string>
     <string name="keyboard_shortcut_group_system" msgid="1583416273777875970">"Hệ thống"</string>
     <string name="keyboard_shortcut_group_system_home" msgid="7465138628692109907">"Màn hình chính"</string>
@@ -869,7 +860,7 @@
     <string name="keyboard_shortcut_join" msgid="3578314570034512676">"hoặc"</string>
     <string name="keyboard_shortcut_clear_text" msgid="6631051796030377857">"Xoá cụm từ tìm kiếm"</string>
     <string name="keyboard_shortcut_search_list_title" msgid="4271769465397671138">"Phím tắt"</string>
-    <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Tìm lối tắt"</string>
+    <string name="keyboard_shortcut_search_list_hint" msgid="5982623262974326746">"Tìm phím tắt"</string>
     <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"Không tìm thấy lối tắt"</string>
     <string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"Hệ thống"</string>
     <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"Đầu vào"</string>
@@ -886,8 +877,8 @@
     <string name="group_system_go_back" msgid="2730322046244918816">"Quay lại"</string>
     <string name="group_system_access_home_screen" msgid="4130366993484706483">"Chuyển đến màn hình chính"</string>
     <string name="group_system_overview_open_apps" msgid="5659958952937994104">"Xem các ứng dụng gần đây"</string>
-    <string name="group_system_cycle_forward" msgid="5478663965957647805">"Di chuyển tiến trong danh sách các ứng dụng gần đây"</string>
-    <string name="group_system_cycle_back" msgid="8194102916946802902">"Di chuyển lùi trong danh sách các ứng dụng gần đây"</string>
+    <string name="group_system_cycle_forward" msgid="5478663965957647805">"Di chuyển tiến qua các ứng dụng gần đây"</string>
+    <string name="group_system_cycle_back" msgid="8194102916946802902">"Di chuyển lùi qua các ứng dụng gần đây"</string>
     <string name="group_system_access_all_apps_search" msgid="1553588630154197469">"Mở danh sách ứng dụng"</string>
     <string name="group_system_access_system_settings" msgid="8731721963449070017">"Mở phần cài đặt"</string>
     <string name="group_system_access_google_assistant" msgid="7210074957915968110">"Mở Trợ lý"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"Hiển thị biểu tượng thông báo có mức ưu tiên thấp"</string>
     <string name="other" msgid="429768510980739978">"Khác"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"xóa ô"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"thêm ô vào vị trí cuối cùng"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Di chuyển ô"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Thêm ô vào vị trí mong muốn"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Di chuyển tới <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Thêm vào vị trí <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Vị trí không hợp lệ."</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"chọn người dùng"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"Không có Internet"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"Mở cài đặt <xliff:g id="ID_1">%s</xliff:g>."</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"Chỉnh sửa thứ tự của trình đơn Cài đặt nhanh."</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Trình đơn nguồn"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Trang <xliff:g id="ID_1">%1$d</xliff:g> / <xliff:g id="ID_2">%2$d</xliff:g>"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"Màn hình khóa"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"truy cập thiết bị"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Dùng vân tay để mở"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Bạn cần phải xác thực. Hãy chạm vào cảm biến vân tay để xác thực."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Đang gọi điện thoại"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Dữ liệu di động"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Đã kết nối"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Tạm thời có kết nối"</string>
@@ -1449,12 +1438,12 @@
     <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_customize_mode_title" msgid="1467657117101096033">"Tuỳ chỉnh phím tắt"</string>
-    <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Xoá lối tắt?"</string>
+    <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"Xoá phím tắt?"</string>
     <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"Đặt lại về phím tắt mặc định?"</string>
-    <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Nhấn phím để chỉ định lối tắt"</string>
-    <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Thao tác này sẽ xoá vĩnh viễn lối tắt tuỳ chỉnh của bạn."</string>
+    <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"Nhấn phím để chỉ định phím tắt"</string>
+    <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"Thao tác này sẽ xoá vĩnh viễn phím tắt tuỳ chỉnh của bạn."</string>
     <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"Thao tác này sẽ xoá vĩnh viễn mọi phím tắt tuỳ chỉnh của bạn."</string>
-    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Tìm lối tắt"</string>
+    <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Tìm phím tắt"</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_meta_key" msgid="3989315044342124818">"Biểu tượng phím Meta (phím hành động)"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 1649b3a..8f0b2f1 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"持续显示屏幕录制会话通知"</string>
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"要录制屏幕吗?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"录制单个应用"</string>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"录制此屏幕"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"录制“%s”屏幕"</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>
@@ -417,20 +415,13 @@
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"无法更新预设"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"预设"</string>
     <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"已选择"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"周围声音"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"左侧"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"右侧"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"展开为左侧和右侧的单独控件"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"收起为统一控件"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"将周围声音静音"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"取消周围声音静音"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"工具"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"实时字幕"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"记事"</string>
@@ -490,7 +481,7 @@
     <string name="keyguard_retry" msgid="886802522584053523">"向上滑动即可重试"</string>
     <string name="accesssibility_keyguard_retry" msgid="8880238862712870676">"向上滑动即可再次尝试人脸解锁"</string>
     <string name="require_unlock_for_nfc" msgid="1305686454823018831">"需要解锁才能使用 NFC"</string>
-    <string name="do_disclosure_generic" msgid="4896482821974707167">"此设备归贵单位所有"</string>
+    <string name="do_disclosure_generic" msgid="4896482821974707167">"此设备归贵组织所有"</string>
     <string name="do_disclosure_with_name" msgid="2091641464065004091">"此设备归<xliff:g id="ORGANIZATION_NAME">%s</xliff:g>所有"</string>
     <string name="do_financed_disclosure_with_name" msgid="6723004643314467864">"这是<xliff:g id="ORGANIZATION_NAME">%s</xliff:g>提供的设备"</string>
     <string name="phone_hint" msgid="6682125338461375925">"滑动图标即可拨打电话"</string>
@@ -624,7 +615,7 @@
     <string name="quick_settings_financed_disclosure_named_management" msgid="2307703784594859524">"此设备由<xliff:g id="ORGANIZATION_NAME">%s</xliff:g>提供"</string>
     <string name="quick_settings_disclosure_management_named_vpn" msgid="4137564460025113168">"此设备归贵组织所有,已通过“<xliff:g id="VPN_APP">%1$s</xliff:g>”连接到互联网"</string>
     <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_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>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"显示低优先级的通知图标"</string>
     <string name="other" msgid="429768510980739978">"其他"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"移除功能块"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"将功能块添加到最后一个位置"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"移动功能块"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"将功能块添加到所需位置"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"移至 <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"添加到位置 <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"位置无效。"</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"选择用户"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"未连接到互联网"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"打开<xliff:g id="ID_1">%s</xliff:g>设置。"</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"修改快捷设置的顺序。"</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"电源菜单"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"第 <xliff:g id="ID_1">%1$d</xliff:g> 页,共 <xliff:g id="ID_2">%2$d</xliff:g> 页"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"锁定屏幕"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"进入设备"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"使用指纹即可打开"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"需要进行身份验证。请轻触指纹传感器以验证身份。"</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"正在进行通话"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"移动数据网络"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"已连接"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"已暂时连接"</string>
@@ -1439,7 +1428,7 @@
     <string name="privacy_dialog_active_app_usage_2" msgid="2770926061339921767">"“<xliff:g id="APP_NAME">%1$s</xliff:g>”正在使用(<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"“<xliff:g id="APP_NAME">%1$s</xliff:g>”最近使用过(<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</string>
     <string name="shortcut_helper_category_system" msgid="462110876978937359">"系统"</string>
-    <string name="shortcut_helper_category_system_controls" msgid="3153344561395751020">"系统控件"</string>
+    <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_split_screen" msgid="1159669813444812244">"分屏"</string>
@@ -1450,10 +1439,10 @@
     <string name="shortcut_helper_title" msgid="8567500639300970049">"键盘快捷键"</string>
     <string name="shortcut_helper_customize_mode_title" msgid="1467657117101096033">"自定义键盘快捷键"</string>
     <string name="shortcut_customize_mode_remove_shortcut_dialog_title" msgid="7106420484940737208">"要移除快捷键吗?"</string>
-    <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"要重置为默认快捷方式吗?"</string>
+    <string name="shortcut_customize_mode_reset_shortcut_dialog_title" msgid="8131184731313717780">"要重置为默认快捷键吗?"</string>
     <string name="shortcut_customize_mode_add_shortcut_description" msgid="6866025005347407696">"按下按键即可指定快捷键"</string>
     <string name="shortcut_customize_mode_remove_shortcut_description" msgid="6851287900585057128">"此操作会永久删除您的自定义快捷键。"</string>
-    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"此操作会永久删除您的所有自定义快捷方式。"</string>
+    <string name="shortcut_customize_mode_reset_shortcut_description" msgid="2081849715634358684">"此操作会永久删除您的所有自定义快捷键。"</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>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 29e9712..afae659 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"持續顯示錄影畫面工作階段通知"</string>
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"要錄影螢幕畫面嗎?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"錄影一個應用程式"</string>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"錄影此畫面"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"錄影 %s"</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>
@@ -417,20 +415,13 @@
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"無法更新預設"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"預設"</string>
     <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"揀咗"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"環境聲音"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"左"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"右"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"打開就可以分開左右控制"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"收埋就可以統一控制"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"環境聲音靜音"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"取消環境聲音靜音"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"工具"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"即時字幕"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"筆記"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"顯示低優先順序通知圖示"</string>
     <string name="other" msgid="429768510980739978">"其他"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"移除圖塊"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"加圖塊去上一個位置"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"移動圖塊"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"加圖塊去目標位置"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"移去 <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"加去位置 <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"位置冇效。"</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"揀使用者"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"沒有互聯網連線"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"開啟<xliff:g id="ID_1">%s</xliff:g>設定頁面。"</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"編輯「快速設定」嘅次序"</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"電源選單"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"第 <xliff:g id="ID_1">%1$d</xliff:g> 頁 (共 <xliff:g id="ID_2">%2$d</xliff:g> 頁)"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"螢幕鎖定"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"進入裝置"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"使用指紋即可開啟"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"需要驗證。掂一下指紋感應器就可以驗證。"</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"通話中"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"流動數據"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"已連線"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"已暫時連線"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 1c80985..f52e906 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -111,10 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"持續顯示螢幕畫面錄製工作階段通知"</string>
     <string name="screenrecord_permission_dialog_title" msgid="7415261783188749730">"要錄製畫面嗎?"</string>
     <string name="screenrecord_permission_dialog_option_text_single_app" msgid="1996450687814647583">"錄製單一應用程式"</string>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"錄製這個畫面"</string>
+    <string name="screenrecord_permission_dialog_option_text_entire_screen_for_display" msgid="4169494703993148253">"錄製「%s」畫面"</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>
@@ -417,20 +415,13 @@
     <string name="hearing_devices_presets_error" msgid="350363093458408536">"無法更新預設設定"</string>
     <string name="hearing_devices_preset_label" msgid="7878267405046232358">"預設"</string>
     <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"已選取"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"環境"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"左"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"右"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"展開為左右獨立控制選項"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"收合為統合控制選項"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"將環境靜音"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"取消環境靜音"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"工具"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"即時字幕"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"筆記"</string>
@@ -976,11 +967,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"顯示低優先順序通知圖示"</string>
     <string name="other" msgid="429768510980739978">"其他"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"移除圖塊"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"將設定方塊新增到最後一個位置"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"移動圖塊"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"將設定方塊新增到所需位置"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"移至 <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"新增到位置 <xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"位置無效。"</string>
@@ -996,8 +985,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"選擇使用者"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"沒有網際網路連線"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"開啟「<xliff:g id="ID_1">%s</xliff:g>」設定。"</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"編輯「快速設定」的順序。"</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"電源鍵選單"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"第 <xliff:g id="ID_1">%1$d</xliff:g> 頁,共 <xliff:g id="ID_2">%2$d</xliff:g> 頁"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"螢幕鎖定"</string>
@@ -1301,7 +1289,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"進入裝置"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"使用指紋即可開啟"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"需要驗證。輕觸指紋感應器即可進行驗證。"</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"通話中"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"行動數據"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"已連線"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"已暫時建立連線"</string>
@@ -1473,7 +1462,7 @@
     <string name="shortcut_helper_customize_dialog_reset_button_label" msgid="7645535254306312685">"是,請重設"</string>
     <string name="shortcut_helper_customize_dialog_cancel_button_label" msgid="5595546460431741178">"取消"</string>
     <string name="shortcut_helper_add_shortcut_dialog_placeholder" msgid="9154297849458741995">"按下按鍵"</string>
-    <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"這個按鍵組合已在使用中,請改用其他按鍵。"</string>
+    <string name="shortcut_customizer_key_combination_in_use_error_message" msgid="7693234470526626327">"按鍵組合重複,請改用其他按鍵。"</string>
     <string name="shortcut_customizer_generic_error_message" msgid="3128454624049722741">"無法設定捷徑。"</string>
     <string name="shortcut_helper_plus_symbol" msgid="4534843157353732011">"+"</string>
     <string name="launch_keyboard_tutorial_notification_title" msgid="8849933155160522519">"使用鍵盤操作"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 6a8483d..a63167e 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -111,8 +111,8 @@
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"Isaziso esiqhubekayo seseshini yokurekhoda isikrini"</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>
-    <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen (4882406311415082016) -->
-    <skip />
+    <string name="screenrecord_permission_dialog_option_text_entire_screen" msgid="4882406311415082016">"Rekhoda lesi sikrini"</string>
+    <!-- String.format failed for translation -->
     <!-- no translation found for screenrecord_permission_dialog_option_text_entire_screen_for_display (4169494703993148253) -->
     <skip />
     <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>
@@ -417,20 +417,13 @@
     <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="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Okukhethiwe"</string>
-    <!-- no translation found for hearing_devices_ambient_label (629440938614895797) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_left (3586965448230412600) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_control_right (6192137602448918383) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_expand_controls (2131816068187709200) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_collapse_controls (2261097656446201581) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_mute (1836882837647429416) -->
-    <skip />
-    <!-- no translation found for hearing_devices_ambient_unmute (2187938085943876814) -->
-    <skip />
+    <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Izindawo ezizungezile"</string>
+    <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Kwesokunxele"</string>
+    <string name="hearing_devices_ambient_control_right" msgid="6192137602448918383">"Kwesokudla"</string>
+    <string name="hearing_devices_ambient_expand_controls" msgid="2131816068187709200">"Nwebela ezilawulini ezihlukanisiwe zakwesokunxele nakwesokudla"</string>
+    <string name="hearing_devices_ambient_collapse_controls" msgid="2261097656446201581">"Goqa ezilawulini ezihlanganisiwe"</string>
+    <string name="hearing_devices_ambient_mute" msgid="1836882837647429416">"Thulisa izindawo ezizungezile"</string>
+    <string name="hearing_devices_ambient_unmute" msgid="2187938085943876814">"Susa ukuthula ezindaweni ezizungezile"</string>
     <string name="hearing_devices_tools_label" msgid="1929081464316074476">"Amathuluzi"</string>
     <string name="quick_settings_hearing_devices_live_caption_title" msgid="1054814050932225451">"Okushuthwe Bukhoma"</string>
     <string name="quick_settings_notes_label" msgid="1028004078001002623">"Inothi"</string>
@@ -976,11 +969,9 @@
     <string name="tuner_low_priority" msgid="8412666814123009820">"Bonisa izithonjana zesaziso zokubaluleka okuncane"</string>
     <string name="other" msgid="429768510980739978">"Okunye"</string>
     <string name="accessibility_qs_edit_remove_tile_action" msgid="775511891457193480">"susa ithayela"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_add_action (8311378984458545661) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_add_action" msgid="8311378984458545661">"faka ithayela endaweni yokugcina"</string>
     <string name="accessibility_qs_edit_tile_start_move" msgid="2009373939914517817">"Hambisa ithayela"</string>
-    <!-- no translation found for accessibility_qs_edit_tile_start_add (8141710006899065161) -->
-    <skip />
+    <string name="accessibility_qs_edit_tile_start_add" msgid="8141710006899065161">"Faka ithayela endaweni oyifunayo"</string>
     <string name="accessibility_qs_edit_tile_move_to_position" msgid="5198161544045930556">"Hambisa ku-<xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibility_qs_edit_tile_add_to_position" msgid="9029163095148274690">"Engeza kusikhundla se-<xliff:g id="POSITION">%1$d</xliff:g>"</string>
     <string name="accessibilit_qs_edit_tile_add_move_invalid_position" msgid="2858467994472624487">"Indawo ayivumelekile."</string>
@@ -996,8 +987,7 @@
     <string name="accessibility_quick_settings_choose_user_action" msgid="4554388498186576087">"khetha umsebenzisi"</string>
     <string name="data_connection_no_internet" msgid="691058178914184544">"Ayikho i-inthanethi"</string>
     <string name="accessibility_quick_settings_open_settings" msgid="536838345505030893">"Vula izilungiselelo ze-<xliff:g id="ID_1">%s</xliff:g>."</string>
-    <!-- no translation found for accessibility_quick_settings_edit (6544873823850165) -->
-    <skip />
+    <string name="accessibility_quick_settings_edit" msgid="6544873823850165">"Hlela i-oda Lamasethingi Asheshayo."</string>
     <string name="accessibility_quick_settings_power_menu" msgid="6820426108301758412">"Imenyu yamandla"</string>
     <string name="accessibility_quick_settings_page" msgid="7506322631645550961">"Ikhasi <xliff:g id="ID_1">%1$d</xliff:g> kwangu-<xliff:g id="ID_2">%2$d</xliff:g>"</string>
     <string name="tuner_lock_screen" msgid="2267383813241144544">"Khiya isikrini"</string>
@@ -1301,7 +1291,8 @@
     <string name="accessibility_enter_hint" msgid="2617864063504824834">"faka idivayisi"</string>
     <string name="keyguard_try_fingerprint" msgid="2825130772993061165">"Sebenzisa izigxivizo zeminwe ukuvula"</string>
     <string name="accessibility_fingerprint_bouncer" msgid="7189102492498735519">"Ukufakazela ubuqiniso budingekile. Thinta inzwa yezigxivizo zeminwe ukuze uqinisekise."</string>
-    <string name="ongoing_phone_call_content_description" msgid="5332334388483099947">"Ikholi yefoni eqhubekayo"</string>
+    <!-- no translation found for ongoing_call_content_description (6394763878322348560) -->
+    <skip />
     <string name="mobile_data_settings_title" msgid="3955246641380064901">"Idatha yeselula"</string>
     <string name="mobile_data_connection_active" msgid="944490013299018227">"Ixhunyiwe"</string>
     <string name="mobile_data_temp_connection_active" msgid="4590222725908806824">"Ixhume okwesikhashana"</string>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 6994a55..2ffa3d1 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1278,6 +1278,7 @@
     <dimen name="qs_center_guideline_padding">10dp</dimen>
     <dimen name="qs_media_action_spacing">4dp</dimen>
     <dimen name="qs_media_action_margin">12dp</dimen>
+    <dimen name="qs_media_action_play_pause_width">72dp</dimen>
     <dimen name="qs_seamless_height">24dp</dimen>
     <dimen name="qs_seamless_icon_size">12dp</dimen>
     <dimen name="qs_media_disabled_seekbar_height">1dp</dimen>
@@ -1683,10 +1684,14 @@
     <dimen name="accessibility_floating_menu_margin">16dp</dimen>
     <dimen name="accessibility_floating_menu_small_padding">6dp</dimen>
     <dimen name="accessibility_floating_menu_small_width_height">36dp</dimen>
+    <dimen name="accessibility_floating_menu_small_badge_width_height">12dp</dimen>
+    <dimen name="accessibility_floating_menu_badge_position">0.67</dimen>
+    <dimen name="accessibility_floating_menu_left_badge_x_position">-0.67</dimen>
     <dimen name="accessibility_floating_menu_small_single_radius">25dp</dimen>
     <dimen name="accessibility_floating_menu_small_multiple_radius">20dp</dimen>
     <dimen name="accessibility_floating_menu_large_padding">8dp</dimen>
     <dimen name="accessibility_floating_menu_large_width_height">56dp</dimen>
+    <dimen name="accessibility_floating_menu_large_badge_width_height">18dp</dimen>
     <dimen name="accessibility_floating_menu_large_single_radius">35dp</dimen>
     <dimen name="accessibility_floating_menu_large_multiple_radius">35dp</dimen>
     <dimen name="accessibility_floating_menu_ime_shifting_space">48dp</dimen>
@@ -1767,6 +1772,7 @@
 
     <!-- Ongoing activity chip -->
     <dimen name="ongoing_activity_chip_max_text_width">74dp</dimen>
+    <dimen name="ongoing_activity_chip_margin_start">5dp</dimen>
     <!-- The activity chip side padding, used with the default phone icon. -->
     <dimen name="ongoing_activity_chip_side_padding">12dp</dimen>
     <!-- The activity chip side padding, used with an icon that has embedded padding (e.g. if the icon comes from the notification's smallIcon field). If the icon has padding, the chip itself can have less padding. -->
@@ -2100,6 +2106,8 @@
 
     <dimen name="volume_dialog_background_corner_radius">30dp</dimen>
     <dimen name="volume_dialog_background_vertical_margin">-10dp</dimen>
+    <!-- top margin covers half the ringer button + components spacing -->
+    <dimen name="volume_dialog_background_top_margin">-28dp</dimen>
 
     <dimen name="volume_dialog_components_spacing">8dp</dimen>
     <dimen name="volume_dialog_floating_sliders_spacing">8dp</dimen>
@@ -2109,6 +2117,11 @@
     <dimen name="volume_dialog_button_size">40dp</dimen>
     <dimen name="volume_dialog_slider_width">52dp</dimen>
     <dimen name="volume_dialog_slider_height">254dp</dimen>
+    <!--
+        A primary goal of this margin is to vertically constraint slider height in the landscape
+        orientation when the vertical space is limited
+    -->
+    <dimen name="volume_dialog_slider_vertical_margin">124dp</dimen>
 
     <fraction name="volume_dialog_half_opened_bias">0.2</fraction>
 
@@ -2116,6 +2129,8 @@
 
     <dimen name="volume_dialog_background_square_corner_radius">12dp</dimen>
 
+    <dimen name="volume_dialog_ringer_drawer_margin">10dp</dimen>
+    <dimen name="volume_dialog_ringer_drawer_diff_end_margin">6dp</dimen>
     <dimen name="volume_dialog_ringer_drawer_button_size">@dimen/volume_dialog_button_size</dimen>
     <dimen name="volume_dialog_ringer_drawer_button_icon_radius">10dp</dimen>
     <dimen name="volume_dialog_ringer_selected_button_background_radius">20dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index cd37c22..d445ed9 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -3798,7 +3798,7 @@
     <!-- Title at the top of the keyboard shortcut helper UI when in customize mode. The helper
          is a component that shows the user which keyboard shortcuts they can use.
          [CHAR LIMIT=NONE] -->
-    <string name="shortcut_helper_customize_mode_title">Customize keyboard shortcuts</string>
+    <string name="shortcut_helper_customize_mode_title">Customize shortcuts</string>
     <!-- Title at the top of the keyboard shortcut helper remove shortcut dialog.
          The helper is a component that shows the user which keyboard shortcuts they can use. Also
          allows the user to add/remove custom shortcuts.[CHAR LIMIT=NONE] -->
@@ -3919,6 +3919,16 @@
          The helper is a component that shows the user which keyboard shortcuts they can use.
          [CHAR LIMIT=NONE] -->
     <string name="shortcut_helper_plus_symbol">+</string>
+    <!-- Accessibility label for the plus icon on a shortcut in shortcut helper that allows the user
+         to add a new custom shortcut.
+         The helper is a component that shows the user which keyboard shortcuts they can use.
+         [CHAR LIMIT=NONE] -->
+    <string name="shortcut_helper_add_shortcut_button_label">Add shortcut</string>
+    <!-- Accessibility label for the bin(trash) icon on a shortcut in shortcut helper that allows the
+         user to delete an existing custom shortcut.
+         The helper is a component that shows the user which keyboard shortcuts they can use.
+         [CHAR LIMIT=NONE] -->
+    <string name="shortcut_helper_delete_shortcut_button_label">Delete shortcut</string>
 
     <!-- Keyboard touchpad tutorial scheduler-->
     <!-- Notification title for launching keyboard tutorial [CHAR_LIMIT=100] -->
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 94698bc..08891aa 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -79,7 +79,7 @@
              the chip. -->
         <item name="android:layout_height">match_parent</item>
         <item name="android:layout_gravity">center_vertical|start</item>
-        <item name="android:layout_marginStart">5dp</item>
+        <item name="android:layout_marginStart">@dimen/ongoing_activity_chip_margin_start</item>
     </style>
 
     <style name="StatusBar.Chip.Text">
@@ -576,12 +576,12 @@
 
     <style name="SystemUI.Material3.Slider" parent="@style/Widget.Material3.Slider">
         <item name="labelStyle">@style/Widget.Material3.Slider.Label</item>
-        <item name="thumbColor">@androidprv:color/materialColorPrimary</item>
-        <item name="tickColorActive">@androidprv:color/materialColorSurfaceContainerHighest</item>
-        <item name="tickColorInactive">@androidprv:color/materialColorPrimary</item>
-        <item name="trackColorActive">@androidprv:color/materialColorPrimary</item>
-        <item name="trackColorInactive">@androidprv:color/materialColorSurfaceContainerHighest</item>
-        <item name="trackIconActiveColor">@androidprv:color/materialColorSurfaceContainerHighest</item>
+        <item name="thumbColor">@color/thumb_color</item>
+        <item name="tickColorActive">@color/on_active_track_color</item>
+        <item name="tickColorInactive">@color/on_inactive_track_color</item>
+        <item name="trackColorActive">@color/active_track_color</item>
+        <item name="trackColorInactive">@color/inactive_track_color</item>
+        <item name="trackIconActiveColor">@color/on_active_track_color</item>
     </style>
 
     <style name="Theme.SystemUI.DayNightDialog" parent="@android:style/Theme.DeviceDefault.Light.Dialog"/>
diff --git a/packages/SystemUI/res/xml/volume_dialog_constraint_set.xml b/packages/SystemUI/res/xml/volume_dialog_constraint_set.xml
index 9018e5b..a8f616c 100644
--- a/packages/SystemUI/res/xml/volume_dialog_constraint_set.xml
+++ b/packages/SystemUI/res/xml/volume_dialog_constraint_set.xml
@@ -6,10 +6,13 @@
     <Constraint
         android:id="@id/volume_dialog_main_slider_container"
         android:layout_width="@dimen/volume_dialog_slider_width"
-        android:layout_height="@dimen/volume_dialog_slider_height"
+        android:layout_height="0dp"
+        android:layout_marginTop="@dimen/volume_dialog_slider_vertical_margin"
         android:layout_marginEnd="@dimen/volume_dialog_components_spacing"
+        android:layout_marginBottom="@dimen/volume_dialog_slider_vertical_margin"
         app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintHeight_max="@dimen/volume_dialog_slider_height"
         app:layout_constraintTop_toTopOf="parent"
         app:layout_constraintVertical_bias="0.5" />
 </ConstraintSet>
\ No newline at end of file
diff --git a/packages/SystemUI/res/xml/volume_dialog_half_folded_constraint_set.xml b/packages/SystemUI/res/xml/volume_dialog_half_folded_constraint_set.xml
index 297c388..b4d8ae7 100644
--- a/packages/SystemUI/res/xml/volume_dialog_half_folded_constraint_set.xml
+++ b/packages/SystemUI/res/xml/volume_dialog_half_folded_constraint_set.xml
@@ -6,10 +6,13 @@
     <Constraint
         android:id="@id/volume_dialog_main_slider_container"
         android:layout_width="@dimen/volume_dialog_slider_width"
-        android:layout_height="@dimen/volume_dialog_slider_height"
+        android:layout_height="0dp"
+        android:layout_marginTop="@dimen/volume_dialog_slider_vertical_margin"
         android:layout_marginEnd="@dimen/volume_dialog_components_spacing"
+        android:layout_marginBottom="@dimen/volume_dialog_slider_vertical_margin"
         app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintHeight_max="@dimen/volume_dialog_slider_height"
         app:layout_constraintTop_toTopOf="parent"
         app:layout_constraintVertical_bias="@fraction/volume_dialog_half_opened_bias" />
 </ConstraintSet>
\ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
index 7d220b5..6e23a07 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
@@ -21,11 +21,9 @@
 import android.view.RemoteAnimationTarget;
 import android.view.SurfaceControl;
 import android.window.PictureInPictureSurfaceTransaction;
-import android.window.TaskSnapshot;
 import android.window.WindowAnimationState;
 
 import com.android.internal.os.IResultReceiver;
-import com.android.systemui.shared.recents.model.ThumbnailData;
 import com.android.wm.shell.recents.IRecentsAnimationController;
 
 public class RecentsAnimationControllerCompat {
@@ -40,18 +38,6 @@
         mAnimationController = animationController;
     }
 
-    public ThumbnailData screenshotTask(int taskId) {
-        try {
-            final TaskSnapshot snapshot = mAnimationController.screenshotTask(taskId);
-            if (snapshot != null) {
-                return ThumbnailData.fromSnapshot(snapshot);
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "Failed to screenshot task", e);
-        }
-        return new ThumbnailData();
-    }
-
     public void setInputConsumerEnabled(boolean enabled) {
         try {
             mAnimationController.setInputConsumerEnabled(enabled);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
index d3c02e6..b159a70 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
@@ -29,7 +29,6 @@
 import com.android.systemui.bouncer.ui.helper.BouncerHapticPlayer;
 import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
 import com.android.systemui.res.R;
 import com.android.systemui.statusbar.policy.DevicePostureController;
 import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
@@ -93,10 +92,8 @@
         mPasswordEntry.setUserActivityListener(this::onUserInput);
         mView.onDevicePostureChanged(mPostureController.getDevicePosture());
         mPostureController.addCallback(mPostureCallback);
-        if (mFeatureFlags.isEnabled(Flags.AUTO_PIN_CONFIRMATION)) {
-            mPasswordEntry.setUsePinShapes(true);
-            updateAutoConfirmationState();
-        }
+        mPasswordEntry.setUsePinShapes(true);
+        updateAutoConfirmationState();
     }
 
     protected void onUserInput() {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java
index 4246061..a8de433 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java
@@ -140,7 +140,7 @@
     @Override
     protected void onViewDetached() {
         // TODO(b/117344873) Remove below work around after this issue be fixed.
-        if (mDisplayId == mDisplayTracker.getDefaultDisplayId()) {
+        if (mDisplayId == mDisplayTracker.getDefaultDisplayId() && mLiveData != null) {
             mLiveData.removeObserver(mObserver);
         }
         mConfigurationController.removeCallback(mConfigurationListener);
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 41b9d33..5f0acfa 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityFloatingMenuController.java
@@ -19,6 +19,7 @@
 import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
 
+import android.annotation.Nullable;
 import android.content.Context;
 import android.hardware.display.DisplayManager;
 import android.os.Handler;
@@ -35,6 +36,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.settingslib.bluetooth.HearingAidDeviceManager;
 import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
 import com.android.systemui.accessibility.AccessibilityButtonModeObserver.AccessibilityButtonMode;
 import com.android.systemui.accessibility.AccessibilityButtonTargetsObserver;
@@ -61,6 +63,7 @@
     private final ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
     private final DisplayManager mDisplayManager;
     private final AccessibilityManager mAccessibilityManager;
+    private final HearingAidDeviceManager mHearingAidDeviceManager;
 
     private final SecureSettings mSecureSettings;
     private final DisplayTracker mDisplayTracker;
@@ -107,6 +110,7 @@
             AccessibilityManager accessibilityManager,
             AccessibilityButtonTargetsObserver accessibilityButtonTargetsObserver,
             AccessibilityButtonModeObserver accessibilityButtonModeObserver,
+            @Nullable HearingAidDeviceManager hearingAidDeviceManager,
             KeyguardUpdateMonitor keyguardUpdateMonitor,
             SecureSettings secureSettings,
             DisplayTracker displayTracker,
@@ -119,6 +123,7 @@
         mAccessibilityManager = accessibilityManager;
         mAccessibilityButtonTargetsObserver = accessibilityButtonTargetsObserver;
         mAccessibilityButtonModeObserver = accessibilityButtonModeObserver;
+        mHearingAidDeviceManager = hearingAidDeviceManager;
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mSecureSettings = secureSettings;
         mDisplayTracker = displayTracker;
@@ -201,7 +206,7 @@
                     TYPE_NAVIGATION_BAR_PANEL, /* options= */ null);
             mFloatingMenu = new MenuViewLayerController(windowContext, mWindowManager,
                     mViewCaptureAwareWindowManager, mAccessibilityManager, mSecureSettings,
-                    mNavigationModeController);
+                    mNavigationModeController, mHearingAidDeviceManager);
         }
 
         mFloatingMenu.show();
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapter.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapter.java
index 5160309..277c81e 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/AccessibilityTargetAdapter.java
@@ -16,12 +16,17 @@
 
 package com.android.systemui.accessibility.floatingmenu;
 
+import static com.android.internal.accessibility.AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME;
+
+import android.content.ComponentName;
+import android.content.Context;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
 
 import androidx.annotation.IntDef;
 import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
 import androidx.core.view.ViewCompat;
 import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
 import androidx.recyclerview.widget.RecyclerView;
@@ -29,7 +34,10 @@
 
 import com.android.internal.accessibility.common.ShortcutConstants.AccessibilityFragmentType;
 import com.android.internal.accessibility.dialog.AccessibilityTarget;
+import com.android.settingslib.bluetooth.HearingAidDeviceManager;
+import com.android.settingslib.bluetooth.HearingAidDeviceManager.ConnectionStatus;
 import com.android.systemui.accessibility.floatingmenu.AccessibilityTargetAdapter.ViewHolder;
+import com.android.systemui.accessibility.hearingaid.HearingDeviceStatusDrawableInfo;
 import com.android.systemui.res.R;
 
 import java.lang.annotation.Retention;
@@ -40,10 +48,16 @@
  * An adapter which shows the set of accessibility targets that can be performed.
  */
 public class AccessibilityTargetAdapter extends Adapter<ViewHolder> {
+    @VisibleForTesting static final int PAYLOAD_HEARING_STATUS_DRAWABLE = 1;
+
     private int mIconWidthHeight;
+    private int mBadgeWidthHeight;
     private int mItemPadding;
     private final List<AccessibilityTarget> mTargets;
 
+    private int mHearingDeviceStatus;
+    private boolean mBadgeOnLeftSide = false;
+
     @IntDef({
             ItemType.FIRST_ITEM,
             ItemType.REGULAR_ITEM,
@@ -56,7 +70,7 @@
         int LAST_ITEM = 2;
     }
 
-    public AccessibilityTargetAdapter(List<AccessibilityTarget> targets) {
+    public AccessibilityTargetAdapter(@NonNull List<AccessibilityTarget> targets) {
         mTargets = targets;
     }
 
@@ -82,7 +96,9 @@
     public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
         final AccessibilityTarget target = mTargets.get(position);
         holder.mIconView.setBackground(target.getIcon());
-        holder.updateIconWidthHeight(mIconWidthHeight);
+        holder.mRightBadgeView.setBackground(null);
+        holder.mLeftBadgeView.setBackground(null);
+        holder.updateIconSize(mIconWidthHeight);
         holder.updateItemPadding(mItemPadding, getItemCount());
         holder.itemView.setOnClickListener((v) -> target.onSelected());
         holder.itemView.setStateDescription(target.getStateDescription());
@@ -95,6 +111,32 @@
         ViewCompat.replaceAccessibilityAction(holder.itemView,
                 AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_CLICK,
                 clickHint, /* command= */ null);
+
+        if (com.android.settingslib.flags.Flags.hearingDeviceSetConnectionStatusReport()) {
+            if (ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.equals(
+                    ComponentName.unflattenFromString(target.getId()))) {
+                updateHearingDeviceStatusDrawable(holder, mHearingDeviceStatus);
+            }
+        }
+    }
+
+    @Override
+    public void onBindViewHolder(@NonNull ViewHolder holder, int position,
+            @NonNull List<Object> payloads) {
+        if (payloads.isEmpty()) {
+            onBindViewHolder(holder, position);
+            return;
+        }
+
+        if (com.android.settingslib.flags.Flags.hearingDeviceSetConnectionStatusReport()) {
+            payloads.forEach(payload -> {
+                if (payload instanceof Integer cmd) {
+                    if (cmd == PAYLOAD_HEARING_STATUS_DRAWABLE) {
+                        updateHearingDeviceStatusDrawable(holder, mHearingDeviceStatus);
+                    }
+                }
+            });
+        }
     }
 
     @ItemType
@@ -122,19 +164,76 @@
         mIconWidthHeight = iconWidthHeight;
     }
 
+    public void setBadgeWidthHeight(int badgeWidthHeight) {
+        mBadgeWidthHeight = badgeWidthHeight;
+    }
+
     public void setItemPadding(int itemPadding) {
         mItemPadding = itemPadding;
     }
 
+    public void setBadgeOnLeftSide(boolean leftSide) {
+        mBadgeOnLeftSide = leftSide;
+    }
+
+    /**
+     * Notifies to update the hearing device status drawable at the given target index.
+     *
+     * @param status the connection status for hearing devices.
+     *               {@link HearingAidDeviceManager.ConnectionStatus}
+     * @param targetIndex The index of the hearing aid device in the target list, or -1 if not
+     *                    exist.
+     */
+    public void onHearingDeviceStatusChanged(@HearingAidDeviceManager.ConnectionStatus int status,
+            int targetIndex) {
+        mHearingDeviceStatus = status;
+
+        if (targetIndex >= 0) {
+            notifyItemChanged(targetIndex, PAYLOAD_HEARING_STATUS_DRAWABLE);
+        }
+    }
+
+    private void updateHearingDeviceStatusDrawable(ViewHolder holder,
+            @ConnectionStatus int status) {
+        final Context context = holder.itemView.getContext();
+        HearingDeviceStatusDrawableInfo.StatusDrawableInfo statusDrawableInfo =
+                HearingDeviceStatusDrawableInfo.get(status);
+        final int baseDrawableId = statusDrawableInfo.baseDrawableId();
+        final int stateDescriptionId = statusDrawableInfo.stateDescriptionId();
+        final int indicatorDrawableId = statusDrawableInfo.indicatorDrawableId();
+
+        holder.mIconView.setBackground(
+                (baseDrawableId != 0) ? context.getDrawable(baseDrawableId) : null);
+        holder.mRightBadgeView.setBackground(
+                (indicatorDrawableId != 0) ? context.getDrawable(indicatorDrawableId) : null);
+        holder.mLeftBadgeView.setBackground(
+                (indicatorDrawableId != 0) ? context.getDrawable(indicatorDrawableId) : null);
+        holder.itemView.setStateDescription(
+                (stateDescriptionId != 0) ? context.getString(stateDescriptionId) : null);
+        holder.updateBadgeSize(mBadgeWidthHeight);
+
+        if (mBadgeOnLeftSide) {
+            holder.mRightBadgeView.setVisibility(View.INVISIBLE);
+            holder.mLeftBadgeView.setVisibility(View.VISIBLE);
+        } else {
+            holder.mRightBadgeView.setVisibility(View.VISIBLE);
+            holder.mLeftBadgeView.setVisibility(View.INVISIBLE);
+        }
+    }
+
     static class ViewHolder extends RecyclerView.ViewHolder {
         final View mIconView;
+        final View mRightBadgeView;
+        final View mLeftBadgeView;
 
         ViewHolder(View itemView) {
             super(itemView);
             mIconView = itemView.findViewById(R.id.icon_view);
+            mRightBadgeView = itemView.findViewById(R.id.right_badge_view);
+            mLeftBadgeView = itemView.findViewById(R.id.left_badge_view);
         }
 
-        void updateIconWidthHeight(int newValue) {
+        void updateIconSize(int newValue) {
             final ViewGroup.LayoutParams layoutParams = mIconView.getLayoutParams();
             if (layoutParams.width == newValue) {
                 return;
@@ -144,6 +243,24 @@
             mIconView.setLayoutParams(layoutParams);
         }
 
+        void updateBadgeSize(int newValue) {
+            final ViewGroup.LayoutParams rightLayoutParams = mRightBadgeView.getLayoutParams();
+            if (rightLayoutParams.width == newValue) {
+                return;
+            }
+            rightLayoutParams.width = newValue;
+            rightLayoutParams.height = newValue;
+            final ViewGroup.LayoutParams leftLayoutParams = mLeftBadgeView.getLayoutParams();
+            if (leftLayoutParams.width == newValue) {
+                return;
+            }
+            leftLayoutParams.width = newValue;
+            leftLayoutParams.height = newValue;
+
+            mRightBadgeView.setLayoutParams(rightLayoutParams);
+            mLeftBadgeView.setLayoutParams(leftLayoutParams);
+        }
+
         void updateItemPadding(int padding, int size) {
             itemView.setPaddingRelative(padding, padding, padding, 0);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java
index ffb5f3d..121b51f 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuInfoRepository.java
@@ -30,6 +30,7 @@
 
 import android.annotation.FloatRange;
 import android.annotation.IntDef;
+import android.annotation.Nullable;
 import android.content.ComponentCallbacks;
 import android.content.Context;
 import android.content.pm.ActivityInfo;
@@ -49,6 +50,8 @@
 
 import com.android.internal.accessibility.dialog.AccessibilityTarget;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.settingslib.bluetooth.HearingAidDeviceManager;
+import com.android.settingslib.utils.ThreadUtils;
 import com.android.systemui.Prefs;
 import com.android.systemui.util.settings.SecureSettings;
 
@@ -80,8 +83,11 @@
     private final AccessibilityManager mAccessibilityManager;
     private final AccessibilityManager.AccessibilityServicesStateChangeListener
             mA11yServicesStateChangeListener = manager -> onTargetFeaturesChanged();
+    private final HearingAidDeviceManager mHearingAidDeviceManager;
+    private final HearingAidDeviceManager.ConnectionStatusListener
+            mHearingDeviceStatusListener = this::onDevicesConnectionStatusChanged;
     private final Handler mHandler = new Handler(Looper.getMainLooper());
-    private final OnSettingsContentsChanged mSettingsContentsCallback;
+    private final OnContentsChanged mSettingsContentsCallback;
     private final SecureSettings mSecureSettings;
     private Position mPercentagePosition;
 
@@ -148,12 +154,14 @@
     };
 
     MenuInfoRepository(Context context, AccessibilityManager accessibilityManager,
-            OnSettingsContentsChanged settingsContentsChanged, SecureSettings secureSettings) {
+            OnContentsChanged settingsContentsChanged, SecureSettings secureSettings,
+            @Nullable HearingAidDeviceManager hearingAidDeviceManager) {
         mContext = context;
         mAccessibilityManager = accessibilityManager;
         mConfiguration = new Configuration(context.getResources().getConfiguration());
         mSettingsContentsCallback = settingsContentsChanged;
         mSecureSettings = secureSettings;
+        mHearingAidDeviceManager = hearingAidDeviceManager;
 
         mPercentagePosition = getStartPosition();
     }
@@ -185,6 +193,14 @@
         callback.onReady(getTargets(mContext, SOFTWARE));
     }
 
+    void loadHearingDeviceStatus(OnInfoReady<Integer> callback) {
+        if (mHearingAidDeviceManager != null) {
+            callback.onReady(mHearingAidDeviceManager.getDevicesConnectionStatus());
+        } else {
+            callback.onReady(HearingAidDeviceManager.ConnectionStatus.NO_DEVICE_BONDED);
+        }
+    }
+
     void loadMenuSizeType(OnInfoReady<Integer> callback) {
         callback.onReady(getMenuSizeTypeFromSettings());
     }
@@ -222,8 +238,8 @@
     }
 
     private void onTargetFeaturesChanged() {
-        mSettingsContentsCallback.onTargetFeaturesChanged(
-                getTargets(mContext, SOFTWARE));
+        List<AccessibilityTarget> targets = getTargets(mContext, SOFTWARE);
+        mSettingsContentsCallback.onTargetFeaturesChanged(targets);
     }
 
     private Position getStartPosition() {
@@ -269,6 +285,24 @@
             mAccessibilityManager.addAccessibilityServicesStateChangeListener(
                     mA11yServicesStateChangeListener);
         }
+
+        if (com.android.settingslib.flags.Flags.hearingDeviceSetConnectionStatusReport()) {
+            registerConnectionStatusListener();
+        }
+    }
+
+    private void registerConnectionStatusListener() {
+        if (mHearingAidDeviceManager != null) {
+            mHearingAidDeviceManager.registerConnectionStatusListener(
+                    mHearingDeviceStatusListener, ThreadUtils.getBackgroundExecutor());
+        }
+    }
+
+    private void unregisterConnectionStatusListener() {
+        if (mHearingAidDeviceManager != null) {
+            mHearingAidDeviceManager.unregisterConnectionStatusListener(
+                    mHearingDeviceStatusListener);
+        }
     }
 
     void unregisterObserversAndCallbacks() {
@@ -281,14 +315,18 @@
             mAccessibilityManager.removeAccessibilityServicesStateChangeListener(
                     mA11yServicesStateChangeListener);
         }
+
+        unregisterConnectionStatusListener();
     }
 
-    interface OnSettingsContentsChanged {
+    interface OnContentsChanged {
         void onTargetFeaturesChanged(List<AccessibilityTarget> newTargetFeatures);
 
         void onSizeTypeChanged(int newSizeType);
 
         void onFadeEffectInfoChanged(MenuFadeEffectInfo fadeEffectInfo);
+
+        void onDevicesConnectionStatusChanged(@HearingAidDeviceManager.ConnectionStatus int status);
     }
 
     interface OnInfoReady<T> {
@@ -311,4 +349,9 @@
                 ACCESSIBILITY_FLOATING_MENU_OPACITY, DEFAULT_OPACITY_VALUE,
                 UserHandle.USER_CURRENT);
     }
+
+    private void onDevicesConnectionStatusChanged(
+            @HearingAidDeviceManager.ConnectionStatus int status) {
+        mSettingsContentsCallback.onDevicesConnectionStatusChanged(status);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java
index 9d9e7df..3f49010 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuView.java
@@ -37,6 +37,7 @@
 
 import com.android.internal.accessibility.dialog.AccessibilityTarget;
 import com.android.modules.expresslog.Counter;
+import com.android.settingslib.bluetooth.HearingAidDeviceManager;
 import com.android.systemui.Flags;
 import com.android.systemui.util.settings.SecureSettings;
 
@@ -65,8 +66,11 @@
     private final Observer<Integer> mSizeTypeObserver = this::onSizeTypeChanged;
     private final Observer<List<AccessibilityTarget>> mTargetFeaturesObserver =
             this::onTargetFeaturesChanged;
+    private final Observer<Integer> mHearingDeviceStatusObserver =
+            this::updateHearingDeviceStatus;
+    private final Observer<Integer> mHearingDeviceTargetIndexObserver =
+            this::updateHearingDeviceTargetIndex;
     private final MenuViewAppearance mMenuViewAppearance;
-
     private boolean mIsMoveToTucked;
 
     private final MenuAnimationController mMenuAnimationController;
@@ -86,6 +90,7 @@
         mTargetFeaturesView = new RecyclerView(context);
         mTargetFeaturesView.setAdapter(mAdapter);
         mTargetFeaturesView.setLayoutManager(new LinearLayoutManager(context));
+        mTargetFeaturesView.setClipChildren(false);
         setLayoutParams(new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
         // Avoid drawing out of bounds of the parent view
         setClipToOutline(true);
@@ -153,6 +158,14 @@
     private void onItemSizeChanged() {
         mAdapter.setItemPadding(mMenuViewAppearance.getMenuPadding());
         mAdapter.setIconWidthHeight(mMenuViewAppearance.getMenuIconSize());
+        mAdapter.setBadgeWidthHeight(mMenuViewAppearance.getBadgeIconSize());
+        mAdapter.notifyDataSetChanged();
+    }
+
+    @SuppressLint("NotifyDataSetChanged")
+    void onSideChanged() {
+        // Badge should be on different side of Menu view's side.
+        mAdapter.setBadgeOnLeftSide(!mMenuViewAppearance.isMenuOnLeftSide());
         mAdapter.notifyDataSetChanged();
     }
 
@@ -201,6 +214,7 @@
         mMenuViewAppearance.setPercentagePosition(percentagePosition);
 
         onPositionChanged();
+        onSideChanged();
     }
 
     void onPositionChanged() {
@@ -242,6 +256,8 @@
 
         mAdapter.setItemPadding(mMenuViewAppearance.getMenuPadding());
         mAdapter.setIconWidthHeight(mMenuViewAppearance.getMenuIconSize());
+        mAdapter.setBadgeWidthHeight(mMenuViewAppearance.getBadgeIconSize());
+
         mAdapter.notifyDataSetChanged();
 
         onSizeChanged();
@@ -309,6 +325,7 @@
         mMenuViewAppearance.setPercentagePosition(percentagePosition);
 
         onEdgeChangedIfNeeded();
+        onSideChanged();
     }
 
     boolean isMoveToTucked() {
@@ -357,6 +374,11 @@
         mMenuViewModel.getTargetFeaturesData().observeForever(mTargetFeaturesObserver);
         mMenuViewModel.getSizeTypeData().observeForever(mSizeTypeObserver);
         mMenuViewModel.getMoveToTuckedData().observeForever(mMoveToTuckedObserver);
+        if (com.android.settingslib.flags.Flags.hearingDeviceSetConnectionStatusReport()) {
+            mMenuViewModel.loadHearingDeviceStatus().observeForever(mHearingDeviceStatusObserver);
+            mMenuViewModel.getHearingDeviceTargetIndexData().observeForever(
+                    mHearingDeviceTargetIndexObserver);
+        }
         setVisibility(VISIBLE);
         mMenuViewModel.registerObserversAndCallbacks();
         getViewTreeObserver().addOnComputeInternalInsetsListener(this);
@@ -371,6 +393,9 @@
         mMenuViewModel.getTargetFeaturesData().removeObserver(mTargetFeaturesObserver);
         mMenuViewModel.getSizeTypeData().removeObserver(mSizeTypeObserver);
         mMenuViewModel.getMoveToTuckedData().removeObserver(mMoveToTuckedObserver);
+        mMenuViewModel.getHearingDeviceStatusData().removeObserver(mHearingDeviceStatusObserver);
+        mMenuViewModel.getHearingDeviceTargetIndexData().removeObserver(
+                mHearingDeviceTargetIndexObserver);
         mMenuViewModel.unregisterObserversAndCallbacks();
         getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
         getViewTreeObserver().removeOnDrawListener(mSystemGestureExcludeUpdater);
@@ -421,6 +446,24 @@
         parentView.setSystemGestureExclusionRects(Collections.singletonList(mBoundsInParent));
     }
 
+    private void updateHearingDeviceStatus(@HearingAidDeviceManager.ConnectionStatus int status) {
+        final int haStatus = mMenuViewModel.getHearingDeviceStatusData().getValue();
+        final int haPosition = mMenuViewModel.getHearingDeviceTargetIndexData().getValue();
+        if (haPosition >= 0) {
+            mContext.getMainExecutor().execute(
+                    () -> mAdapter.onHearingDeviceStatusChanged(haStatus, haPosition));
+        }
+    }
+
+    private void updateHearingDeviceTargetIndex(int position) {
+        final int haStatus = mMenuViewModel.getHearingDeviceStatusData().getValue();
+        final int haPosition = mMenuViewModel.getHearingDeviceTargetIndexData().getValue();
+        if (haPosition >= 0) {
+            mContext.getMainExecutor().execute(
+                    () -> mAdapter.onHearingDeviceStatusChanged(haStatus, haPosition));
+        }
+    }
+
     /**
      * Interface definition for the {@link AccessibilityTarget} list changes.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearance.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearance.java
index 760e1c3..a700cbe 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearance.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewAppearance.java
@@ -57,6 +57,8 @@
     private int mLargePadding;
     private int mSmallIconSize;
     private int mLargeIconSize;
+    private int mSmallBadgeSize;
+    private int mLargeBadgeSize;
     private int mSmallSingleRadius;
     private int mSmallMultipleRadius;
     private int mLargeSingleRadius;
@@ -97,6 +99,12 @@
                 mRes.getDimensionPixelSize(R.dimen.accessibility_floating_menu_small_width_height);
         mLargeIconSize =
                 mRes.getDimensionPixelSize(R.dimen.accessibility_floating_menu_large_width_height);
+        mSmallBadgeSize =
+                mRes.getDimensionPixelSize(
+                        R.dimen.accessibility_floating_menu_small_badge_width_height);
+        mLargeBadgeSize =
+                mRes.getDimensionPixelSize(
+                        R.dimen.accessibility_floating_menu_large_badge_width_height);
         mSmallSingleRadius =
                 mRes.getDimensionPixelSize(R.dimen.accessibility_floating_menu_small_single_radius);
         mSmallMultipleRadius = mRes.getDimensionPixelSize(
@@ -211,6 +219,10 @@
         return mSizeType == SMALL ? mSmallIconSize : mLargeIconSize;
     }
 
+    int getBadgeIconSize() {
+        return mSizeType == SMALL ? mSmallBadgeSize : mLargeBadgeSize;
+    }
+
     private int getMenuMargin() {
         return mMargin;
     }
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 cb96e78..184518a 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewLayerController.java
@@ -24,6 +24,7 @@
 import android.view.accessibility.AccessibilityManager;
 
 import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
+import com.android.settingslib.bluetooth.HearingAidDeviceManager;
 import com.android.systemui.navigationbar.NavigationModeController;
 import com.android.systemui.util.settings.SecureSettings;
 
@@ -39,11 +40,12 @@
     MenuViewLayerController(Context context, WindowManager windowManager,
             ViewCaptureAwareWindowManager viewCaptureAwareWindowManager,
             AccessibilityManager accessibilityManager, SecureSettings secureSettings,
-            NavigationModeController navigationModeController) {
+            NavigationModeController navigationModeController,
+            HearingAidDeviceManager hearingAidDeviceManager) {
         mWindowManager = viewCaptureAwareWindowManager;
 
         MenuViewModel menuViewModel = new MenuViewModel(
-                context, accessibilityManager, secureSettings);
+                context, accessibilityManager, secureSettings, hearingAidDeviceManager);
         MenuViewAppearance menuViewAppearance = new MenuViewAppearance(context, windowManager);
 
         mMenuViewLayer = new MenuViewLayer(context, windowManager, accessibilityManager,
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewModel.java b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewModel.java
index f924784..8b7d6a1 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewModel.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/floatingmenu/MenuViewModel.java
@@ -16,13 +16,20 @@
 
 package com.android.systemui.accessibility.floatingmenu;
 
+import static com.android.internal.accessibility.AccessibilityShortcutController.ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME;
+
+import static java.util.Collections.emptyList;
+
+import android.content.ComponentName;
 import android.content.Context;
 import android.view.accessibility.AccessibilityManager;
 
 import androidx.lifecycle.LiveData;
 import androidx.lifecycle.MutableLiveData;
+import androidx.lifecycle.Transformations;
 
 import com.android.internal.accessibility.dialog.AccessibilityTarget;
+import com.android.settingslib.bluetooth.HearingAidDeviceManager;
 import com.android.systemui.util.settings.SecureSettings;
 
 import java.util.List;
@@ -31,9 +38,9 @@
  * The view model provides the menu information from the repository{@link MenuInfoRepository} for
  * the menu view{@link MenuView}.
  */
-class MenuViewModel implements MenuInfoRepository.OnSettingsContentsChanged {
+class MenuViewModel implements MenuInfoRepository.OnContentsChanged {
     private final MutableLiveData<List<AccessibilityTarget>> mTargetFeaturesData =
-            new MutableLiveData<>();
+            new MutableLiveData<>(emptyList());
     private final MutableLiveData<Integer> mSizeTypeData = new MutableLiveData<>();
     private final MutableLiveData<MenuFadeEffectInfo> mFadeEffectInfoData =
             new MutableLiveData<>();
@@ -41,12 +48,18 @@
     private final MutableLiveData<Boolean> mDockTooltipData = new MutableLiveData<>();
     private final MutableLiveData<Boolean> mMigrationTooltipData = new MutableLiveData<>();
     private final MutableLiveData<Position> mPercentagePositionData = new MutableLiveData<>();
+    private final MutableLiveData<Integer> mHearingDeviceStatusData = new MutableLiveData<>(
+            HearingAidDeviceManager.ConnectionStatus.NO_DEVICE_BONDED);
+    private final LiveData<Integer> mHearingDeviceTargetIndex = Transformations.map(
+            mTargetFeaturesData, this::getHearingDeviceTargetIndex);
+
     private final MenuInfoRepository mInfoRepository;
 
     MenuViewModel(Context context, AccessibilityManager accessibilityManager,
-            SecureSettings secureSettings) {
+            SecureSettings secureSettings, HearingAidDeviceManager hearingAidDeviceManager) {
         mInfoRepository = new MenuInfoRepository(context,
-                accessibilityManager, /* settingsContentsChanged= */ this, secureSettings);
+                accessibilityManager, /* settingsContentsChanged= */ this, secureSettings,
+                hearingAidDeviceManager);
     }
 
     @Override
@@ -64,6 +77,12 @@
         mFadeEffectInfoData.setValue(fadeEffectInfo);
     }
 
+    @Override
+    public void onDevicesConnectionStatusChanged(
+            @HearingAidDeviceManager.ConnectionStatus int status) {
+        mHearingDeviceStatusData.postValue(status);
+    }
+
     void updateMenuMoveToTucked(boolean isMoveToTucked) {
         mInfoRepository.updateMoveToTucked(isMoveToTucked);
     }
@@ -115,6 +134,19 @@
         return mTargetFeaturesData;
     }
 
+    LiveData<Integer> loadHearingDeviceStatus() {
+        mInfoRepository.loadHearingDeviceStatus(mHearingDeviceStatusData::setValue);
+        return mHearingDeviceStatusData;
+    }
+
+    LiveData<Integer> getHearingDeviceStatusData() {
+        return mHearingDeviceStatusData;
+    }
+
+    LiveData<Integer> getHearingDeviceTargetIndexData() {
+        return mHearingDeviceTargetIndex;
+    }
+
     void registerObserversAndCallbacks() {
         mInfoRepository.registerObserversAndCallbacks();
     }
@@ -122,4 +154,16 @@
     void unregisterObserversAndCallbacks() {
         mInfoRepository.unregisterObserversAndCallbacks();
     }
+
+    private int getHearingDeviceTargetIndex(List<AccessibilityTarget> targetList) {
+        final int listSize = targetList.size();
+        for (int index = 0; index < listSize; index++) {
+            AccessibilityTarget target = targetList.get(index);
+            if (ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.equals(
+                    ComponentName.unflattenFromString(target.getId()))) {
+                return index;
+            }
+        }
+        return -1;
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDeviceStatusDrawableInfo.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDeviceStatusDrawableInfo.java
new file mode 100644
index 0000000..ff9bfec
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDeviceStatusDrawableInfo.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 com.android.systemui.accessibility.hearingaid;
+
+import com.android.settingslib.bluetooth.HearingAidDeviceManager.ConnectionStatus;
+
+/**
+ * A utility class to get the hearing device status drawable and its description for the
+ * given connection status. Hearing device status drawable combine with base and indicator
+ * drawable.
+ */
+public final class HearingDeviceStatusDrawableInfo {
+
+    private static final StatusDrawableInfo DRAWABLE_DEFAULT_INFO = new StatusDrawableInfo(
+            com.android.internal.R.drawable.ic_accessibility_hearing_aid,
+            0,
+            0);
+    private static final StatusDrawableInfo DRAWABLE_DISCONNECTED_INFO = new StatusDrawableInfo(
+            com.android.internal.R.drawable.ic_accessibility_hearing_aid_disconnected,
+            0,
+            com.android.internal.R.string.hearing_device_status_disconnected);
+    private static final StatusDrawableInfo DRAWABLE_CONNECTED_INFO = new StatusDrawableInfo(
+            com.android.internal.R.drawable.ic_accessibility_hearing_aid,
+            com.android.internal.R.drawable.ic_accessibility_hearing_aid_blue_dot,
+            com.android.internal.R.string.hearing_device_status_connected);
+    private static final StatusDrawableInfo DRAWABLE_ACTIVE_INFO = new StatusDrawableInfo(
+            com.android.internal.R.drawable.ic_accessibility_hearing_aid,
+            com.android.internal.R.drawable.ic_accessibility_hearing_aid_green_dot,
+            com.android.internal.R.string.hearing_device_status_active);
+
+    private HearingDeviceStatusDrawableInfo() {}
+
+    /**
+     * Returns the corresponding {@link StatusDrawableInfo} for the given {@link ConnectionStatus}.
+     */
+    public static StatusDrawableInfo get(@ConnectionStatus int status) {
+        return switch (status) {
+            case ConnectionStatus.DISCONNECTED -> DRAWABLE_DISCONNECTED_INFO;
+            case ConnectionStatus.CONNECTED -> DRAWABLE_CONNECTED_INFO;
+            case ConnectionStatus.ACTIVE -> DRAWABLE_ACTIVE_INFO;
+            // TODO: b/357882387 - Handle to show connecting or disconnecting status drawable
+            case ConnectionStatus.CONNECTING_OR_DISCONNECTING, ConnectionStatus.NO_DEVICE_BONDED ->
+                    DRAWABLE_DEFAULT_INFO;
+            default -> DRAWABLE_DEFAULT_INFO;
+        };
+    }
+
+    /**
+     * A data class that holds the base drawable, indicator drawable and state description to
+     * represent hearing device connection status.
+     *
+     * @param baseDrawableId the base drawable id for the hearing device status
+     * @param indicatorDrawableId the indicator drawable id for the hearing device status
+     * @param stateDescriptionId the description for the hearing device status
+     */
+    public record StatusDrawableInfo(int baseDrawableId, int indicatorDrawableId,
+                                     int stateDescriptionId) {
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/helper/BouncerSceneLayout.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/helper/BouncerSceneLayout.kt
deleted file mode 100644
index 554dd69..0000000
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/helper/BouncerSceneLayout.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.systemui.bouncer.ui.helper
-
-import androidx.annotation.VisibleForTesting
-
-/** Enumerates all known adaptive layout configurations. */
-enum class BouncerSceneLayout {
-    /** The default UI with the bouncer laid out normally. */
-    STANDARD_BOUNCER,
-    /** The bouncer is displayed vertically stacked with the user switcher. */
-    BELOW_USER_SWITCHER,
-    /** The bouncer is displayed side-by-side with the user switcher or an empty space. */
-    BESIDE_USER_SWITCHER,
-    /** The bouncer is split in two with both sides shown side-by-side. */
-    SPLIT_BOUNCER,
-}
-
-/** Enumerates the supported window size classes. */
-enum class SizeClass {
-    COMPACT,
-    MEDIUM,
-    EXPANDED,
-}
-
-/**
- * Internal version of `calculateLayout` in the System UI Compose library, extracted here to allow
- * for testing that's not dependent on Compose.
- */
-@VisibleForTesting
-fun calculateLayoutInternal(
-    width: SizeClass,
-    height: SizeClass,
-    isOneHandedModeSupported: Boolean,
-): BouncerSceneLayout {
-    return when (height) {
-        SizeClass.COMPACT -> BouncerSceneLayout.SPLIT_BOUNCER
-        SizeClass.MEDIUM ->
-            when (width) {
-                SizeClass.COMPACT -> BouncerSceneLayout.STANDARD_BOUNCER
-                SizeClass.MEDIUM -> BouncerSceneLayout.STANDARD_BOUNCER
-                SizeClass.EXPANDED -> BouncerSceneLayout.BESIDE_USER_SWITCHER
-            }
-        SizeClass.EXPANDED ->
-            when (width) {
-                SizeClass.COMPACT -> BouncerSceneLayout.STANDARD_BOUNCER
-                SizeClass.MEDIUM -> BouncerSceneLayout.BELOW_USER_SWITCHER
-                SizeClass.EXPANDED -> BouncerSceneLayout.BESIDE_USER_SWITCHER
-            }
-    }.takeIf { it != BouncerSceneLayout.BESIDE_USER_SWITCHER || isOneHandedModeSupported }
-        ?: BouncerSceneLayout.STANDARD_BOUNCER
-}
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java
index 7033e64..c0c4ec3 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java
@@ -19,6 +19,7 @@
 import static android.content.ClipDescription.CLASSIFICATION_COMPLETE;
 
 import static com.android.systemui.Flags.clipboardNoninteractiveOnLockscreen;
+import static com.android.systemui.Flags.clipboardOverlayMultiuser;
 import static com.android.systemui.Flags.overrideSuppressOverlayCondition;
 import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_ENTERED;
 import static com.android.systemui.clipboardoverlay.ClipboardOverlayEvent.CLIPBOARD_OVERLAY_UPDATED;
@@ -35,12 +36,18 @@
 import android.provider.Settings;
 import android.util.Log;
 
+import androidx.annotation.NonNull;
+
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.CoreStartable;
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.settings.UserTracker;
 import com.android.systemui.user.utils.UserScopedService;
 
+import java.util.concurrent.Executor;
+
 import javax.inject.Inject;
 import javax.inject.Provider;
 
@@ -61,42 +68,71 @@
     private final Context mContext;
     private final Provider<ClipboardOverlayController> mOverlayProvider;
     private final ClipboardToast mClipboardToast;
-    private final ClipboardManager mClipboardManager;
-    private final KeyguardManager mKeyguardManager;
+    private final UserScopedService<ClipboardManager> mClipboardManagerProvider;
+    private final UserScopedService<KeyguardManager> mKeyguardManagerProvider;
     private final UiEventLogger mUiEventLogger;
     private final ClipboardOverlaySuppressionController mClipboardOverlaySuppressionController;
     private ClipboardOverlay mClipboardOverlay;
+    private ClipboardManager mClipboardManagerForUser;
+    private KeyguardManager mKeyguardManagerForUser;
+
+    private final UserTracker mUserTracker;
+    private final Executor mMainExecutor;
+
+    private final UserTracker.Callback mCallback = new UserTracker.Callback() {
+        @Override
+        public void onUserChanged(int newUser, @NonNull Context userContext) {
+            UserTracker.Callback.super.onUserChanged(newUser, userContext);
+            mClipboardManagerForUser.removePrimaryClipChangedListener(ClipboardListener.this);
+            setUser(mUserTracker.getUserHandle());
+            mClipboardManagerForUser.addPrimaryClipChangedListener(ClipboardListener.this);
+        }
+    };
 
     @Inject
     public ClipboardListener(Context context,
             Provider<ClipboardOverlayController> clipboardOverlayControllerProvider,
             ClipboardToast clipboardToast,
+            UserTracker userTracker,
             UserScopedService<ClipboardManager> clipboardManager,
-            KeyguardManager keyguardManager,
+            UserScopedService<KeyguardManager> keyguardManager,
             UiEventLogger uiEventLogger,
+            @Main Executor mainExecutor,
             ClipboardOverlaySuppressionController clipboardOverlaySuppressionController) {
         mContext = context;
         mOverlayProvider = clipboardOverlayControllerProvider;
         mClipboardToast = clipboardToast;
-        mClipboardManager = clipboardManager.forUser(UserHandle.CURRENT);
-        mKeyguardManager = keyguardManager;
+        mClipboardManagerProvider = clipboardManager;
+        mKeyguardManagerProvider = keyguardManager;
         mUiEventLogger = uiEventLogger;
         mClipboardOverlaySuppressionController = clipboardOverlaySuppressionController;
+
+        mMainExecutor = mainExecutor;
+        mUserTracker = userTracker;
+        setUser(mUserTracker.getUserHandle());
+    }
+
+    private void setUser(UserHandle user) {
+        mClipboardManagerForUser = mClipboardManagerProvider.forUser(user);
+        mKeyguardManagerForUser = mKeyguardManagerProvider.forUser(user);
     }
 
     @Override
     public void start() {
-        mClipboardManager.addPrimaryClipChangedListener(this);
+        if (clipboardOverlayMultiuser()) {
+            mUserTracker.addCallback(mCallback, mMainExecutor);
+        }
+        mClipboardManagerForUser.addPrimaryClipChangedListener(this);
     }
 
     @Override
     public void onPrimaryClipChanged() {
-        if (!mClipboardManager.hasPrimaryClip()) {
+        if (!mClipboardManagerForUser.hasPrimaryClip()) {
             return;
         }
 
-        String clipSource = mClipboardManager.getPrimaryClipSource();
-        ClipData clipData = mClipboardManager.getPrimaryClip();
+        String clipSource = mClipboardManagerForUser.getPrimaryClipSource();
+        ClipData clipData = mClipboardManagerForUser.getPrimaryClip();
 
         if (overrideSuppressOverlayCondition()) {
             if (mClipboardOverlaySuppressionController.shouldSuppressOverlay(clipData, clipSource,
@@ -112,7 +148,7 @@
         }
 
         // user should not access intents before setup or while device is locked
-        if ((clipboardNoninteractiveOnLockscreen() && mKeyguardManager.isDeviceLocked())
+        if ((clipboardNoninteractiveOnLockscreen() && mKeyguardManagerForUser.isDeviceLocked())
                 || !isUserSetupComplete()
                 || clipData == null // shouldn't happen, but just in case
                 || clipData.getItemCount() == 0) {
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 307a07f..6c10eea 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlayModule.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/dagger/ClipboardOverlayModule.java
@@ -18,6 +18,7 @@
 
 import static android.view.WindowManager.LayoutParams.TYPE_SCREENSHOT;
 
+import static com.android.systemui.Flags.clipboardOverlayMultiuser;
 import static com.android.systemui.Flags.enableViewCaptureTracing;
 import static com.android.systemui.util.ConvenienceExtensionsKt.toKotlinLazy;
 
@@ -34,6 +35,7 @@
 import com.android.systemui.clipboardoverlay.ClipboardOverlayView;
 import com.android.systemui.res.R;
 import com.android.systemui.settings.DisplayTracker;
+import com.android.systemui.settings.UserTracker;
 
 import dagger.Lazy;
 import dagger.Module;
@@ -54,18 +56,28 @@
     @Provides
     @OverlayWindowContext
     static Context provideWindowContext(DisplayManager displayManager,
-            DisplayTracker displayTracker, Context context) {
+            DisplayTracker displayTracker, Context context, UserTracker userTracker) {
         Display display = displayManager.getDisplay(displayTracker.getDefaultDisplayId());
-        return context.createWindowContext(display, TYPE_SCREENSHOT, null);
+        if (clipboardOverlayMultiuser()) {
+            return userTracker.getUserContext().createWindowContext(display, TYPE_SCREENSHOT, null);
+        } else {
+            return context.createWindowContext(display, TYPE_SCREENSHOT, null);
+        }
     }
 
     /**
      *
      */
     @Provides
-    static ClipboardOverlayView provideClipboardOverlayView(@OverlayWindowContext Context context) {
-        return (ClipboardOverlayView) LayoutInflater.from(context).inflate(
-                R.layout.clipboard_overlay, null);
+    static ClipboardOverlayView provideClipboardOverlayView(
+            @OverlayWindowContext Context overlayContext, Context context) {
+        if (clipboardOverlayMultiuser()) {
+            return (ClipboardOverlayView) LayoutInflater.from(context).inflate(
+                    R.layout.clipboard_overlay, null);
+        } else {
+            return (ClipboardOverlayView) LayoutInflater.from(overlayContext).inflate(
+                    R.layout.clipboard_overlay, null);
+        }
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/common/shared/model/ContentDescription.kt b/packages/SystemUI/src/com/android/systemui/common/shared/model/ContentDescription.kt
index 08e8293..d628aca 100644
--- a/packages/SystemUI/src/com/android/systemui/common/shared/model/ContentDescription.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/shared/model/ContentDescription.kt
@@ -24,13 +24,9 @@
  * be a [reference][ContentDescription.Resource] to a resource.
  */
 sealed class ContentDescription {
-    data class Loaded(
-        val description: String?,
-    ) : ContentDescription()
+    data class Loaded(val description: String?) : ContentDescription()
 
-    data class Resource(
-        @StringRes val res: Int,
-    ) : ContentDescription()
+    data class Resource(@StringRes val res: Int) : ContentDescription()
 
     companion object {
         /**
@@ -39,6 +35,7 @@
          * Prefer [com.android.systemui.common.ui.binder.ContentDescriptionViewBinder.bind] over
          * this method. This should only be used for testing or concatenation purposes.
          */
+        @JvmStatic
         fun ContentDescription?.loadContentDescription(context: Context): String? {
             return when (this) {
                 null -> null
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
index 014c0db..35c0149 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
@@ -430,6 +430,12 @@
 
     @Provides
     @Singleton
+    static UserScopedService<KeyguardManager> provideKeyguardManagerUserScoped(Context context) {
+        return new UserScopedServiceImpl<>(context, KeyguardManager.class);
+    }
+
+    @Provides
+    @Singleton
     static LatencyTracker provideLatencyTracker(Context context) {
         return LatencyTracker.getInstance(context);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SettingsLibraryModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SettingsLibraryModule.java
index 14626e1..e72dfad 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SettingsLibraryModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SettingsLibraryModule.java
@@ -22,6 +22,7 @@
 import android.os.Handler;
 import android.os.UserHandle;
 
+import com.android.settingslib.bluetooth.HearingAidDeviceManager;
 import com.android.settingslib.bluetooth.LocalBluetoothManager;
 import com.android.systemui.dagger.qualifiers.Background;
 
@@ -41,4 +42,16 @@
             @Background Handler bgHandler) {
         return LocalBluetoothManager.create(context, bgHandler, UserHandle.ALL);
     }
+
+    @SuppressLint("MissingPermission")
+    @SysUISingleton
+    @Provides
+    @Nullable
+    static HearingAidDeviceManager provideHearingAidDeviceManager(
+            @Nullable LocalBluetoothManager localBluetoothManager) {
+        if (localBluetoothManager == null) {
+            return null;
+        }
+        return localBluetoothManager.getCachedDeviceManager().getHearingAidDeviceManager();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepository.kt
index f310b30..3390640 100644
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayWindowPropertiesRepository.kt
@@ -18,6 +18,8 @@
 
 import android.annotation.SuppressLint
 import android.content.Context
+import android.os.Bundle
+import android.util.Log
 import android.view.Display
 import android.view.LayoutInflater
 import android.view.WindowManager
@@ -39,14 +41,13 @@
 interface DisplayWindowPropertiesRepository {
 
     /**
-     * Returns a [DisplayWindowProperties] instance for a given display id and window type.
-     *
-     * @throws IllegalArgumentException if no display with the given display id exists.
+     * Returns a [DisplayWindowProperties] instance for a given display id and window type, or null
+     * if no display with the given display id exists.
      */
     fun get(
         displayId: Int,
         @WindowManager.LayoutParams.WindowType windowType: Int,
-    ): DisplayWindowProperties
+    ): DisplayWindowProperties?
 }
 
 @SysUISingleton
@@ -72,12 +73,10 @@
     override fun get(
         displayId: Int,
         @WindowManager.LayoutParams.WindowType windowType: Int,
-    ): DisplayWindowProperties {
-        val display =
-            displayRepository.getDisplay(displayId)
-                ?: throw IllegalArgumentException("Display with id $displayId doesn't exist")
+    ): DisplayWindowProperties? {
+        val display = displayRepository.getDisplay(displayId) ?: return null
         return properties.get(displayId, windowType)
-            ?: create(display, windowType).also { properties.put(displayId, windowType, it) }
+            ?: create(display, windowType)?.also { properties.put(displayId, windowType, it) }
     }
 
     override fun start() {
@@ -88,7 +87,7 @@
         }
     }
 
-    private fun create(display: Display, windowType: Int): DisplayWindowProperties {
+    private fun create(display: Display, windowType: Int): DisplayWindowProperties? {
         val displayId = display.displayId
         return if (displayId == Display.DEFAULT_DISPLAY) {
             // For the default display, we can just reuse the global/application properties.
@@ -102,6 +101,14 @@
             )
         } else {
             val context = createWindowContext(display, windowType)
+            if (context.displayId != display.displayId) {
+                Log.e(
+                    TAG,
+                    "Returning null because the new context doesn't have the desired display id " +
+                        "${display.displayId}. Display was already removed.",
+                )
+                return null
+            }
             @SuppressLint("NonInjectedService") // Need to manually get the service
             val windowManager = context.getSystemService(WindowManager::class.java) as WindowManager
             val layoutInflater = LayoutInflater.from(context)
@@ -110,11 +117,15 @@
     }
 
     private fun createWindowContext(display: Display, windowType: Int): Context =
-        globalContext.createWindowContext(display, windowType, /* options= */ null).also {
+        globalContext.createWindowContext(display, windowType, /* options= */ Bundle.EMPTY).also {
             it.setTheme(R.style.Theme_SystemUI)
         }
 
     override fun dump(pw: PrintWriter, args: Array<out String>) {
         pw.write("perDisplayContexts: $properties")
     }
+
+    private companion object {
+        const val TAG = "DisplayWindowPropsRepo"
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayStore.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayStore.kt
index 711534f..564588c 100644
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayStore.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayStore.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.display.data.repository
 
+import android.util.Log
 import android.view.Display
 import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.CoreStartable
@@ -36,12 +37,10 @@
     val defaultDisplay: T
 
     /**
-     * Returns an instance for a specific display id.
-     *
-     * @throws IllegalArgumentException if [displayId] doesn't match the id of any existing
-     *   displays.
+     * Returns an instance for a specific display id, or null if [displayId] doesn't match the id of
+     * any existing displays.
      */
-    fun forDisplay(displayId: Int): T
+    fun forDisplay(displayId: Int): T?
 }
 
 abstract class PerDisplayStoreImpl<T>(
@@ -58,7 +57,7 @@
      * Note that the id of the default display is [Display.DEFAULT_DISPLAY].
      */
     override val defaultDisplay: T
-        get() = forDisplay(Display.DEFAULT_DISPLAY)
+        get() = forDisplay(Display.DEFAULT_DISPLAY)!!
 
     /**
      * Returns an instance for a specific display id.
@@ -66,16 +65,30 @@
      * @throws IllegalArgumentException if [displayId] doesn't match the id of any existing
      *   displays.
      */
-    override fun forDisplay(displayId: Int): T {
+    override fun forDisplay(displayId: Int): T? {
         if (displayRepository.getDisplay(displayId) == null) {
-            throw IllegalArgumentException("Display with id $displayId doesn't exist.")
+            Log.e(TAG, "<${instanceClass.simpleName}>: Display with id $displayId doesn't exist.")
+            return null
         }
-        return perDisplayInstances.computeIfAbsent(displayId) {
-            createInstanceForDisplay(displayId)
+        synchronized(perDisplayInstances) {
+            val existingInstance = perDisplayInstances[displayId]
+            if (existingInstance != null) {
+                return existingInstance
+            }
+            val newInstance = createInstanceForDisplay(displayId)
+            if (newInstance == null) {
+                Log.e(
+                    TAG,
+                    "<${instanceClass.simpleName}> returning null because createInstanceForDisplay($displayId) returned null.",
+                )
+            } else {
+                perDisplayInstances[displayId] = newInstance
+            }
+            return newInstance
         }
     }
 
-    protected abstract fun createInstanceForDisplay(displayId: Int): T
+    protected abstract fun createInstanceForDisplay(displayId: Int): T?
 
     override fun start() {
         val instanceType = instanceClass.simpleName
@@ -98,6 +111,10 @@
     override fun dump(pw: PrintWriter, args: Array<out String>) {
         pw.println(perDisplayInstances)
     }
+
+    private companion object {
+        const val TAG = "PerDisplayStore"
+    }
 }
 
 class SingleDisplayStore<T>(defaultInstance: T) : PerDisplayStore<T> {
diff --git a/packages/SystemUI/src/com/android/systemui/display/domain/interactor/DisplayWindowPropertiesInteractor.kt b/packages/SystemUI/src/com/android/systemui/display/domain/interactor/DisplayWindowPropertiesInteractor.kt
index 22e467b..99c9ca9 100644
--- a/packages/SystemUI/src/com/android/systemui/display/domain/interactor/DisplayWindowPropertiesInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/domain/interactor/DisplayWindowPropertiesInteractor.kt
@@ -33,7 +33,7 @@
      *
      * @throws IllegalArgumentException if no display with the given display id exists.
      */
-    fun getForStatusBar(displayId: Int): DisplayWindowProperties
+    fun getForStatusBar(displayId: Int): DisplayWindowProperties?
 }
 
 @SysUISingleton
@@ -42,7 +42,7 @@
 constructor(private val repo: DisplayWindowPropertiesRepository) :
     DisplayWindowPropertiesInteractor {
 
-    override fun getForStatusBar(displayId: Int): DisplayWindowProperties {
+    override fun getForStatusBar(displayId: Int): DisplayWindowProperties? {
         return repo.get(displayId, TYPE_STATUS_BAR)
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
index e02e3fb..10f060c 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
@@ -22,10 +22,10 @@
 import android.annotation.MainThread;
 import android.content.res.Configuration;
 import android.hardware.display.AmbientDisplayConfiguration;
-import android.os.Trace;
 import android.util.Log;
 import android.view.Display;
 
+import com.android.app.tracing.coroutines.TrackTracer;
 import com.android.internal.util.Preconditions;
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.doze.dagger.DozeScope;
@@ -314,7 +314,7 @@
         mState = newState;
 
         mDozeLog.traceState(newState);
-        Trace.traceCounter(Trace.TRACE_TAG_APP, "doze_machine_state", newState.ordinal());
+        TrackTracer.instantForGroup("keyguard", "doze_machine_state", newState.ordinal());
 
         updatePulseReason(newState, oldState, pulseReason);
         performTransitionOnComponents(oldState, newState);
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 2978595..9596a54 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
@@ -20,10 +20,12 @@
 import android.hardware.input.InputManager
 import android.hardware.input.KeyGestureEvent
 import androidx.datastore.core.DataStore
+import androidx.datastore.core.handlers.ReplaceFileCorruptionHandler
 import androidx.datastore.preferences.core.MutablePreferences
 import androidx.datastore.preferences.core.PreferenceDataStoreFactory
 import androidx.datastore.preferences.core.Preferences
 import androidx.datastore.preferences.core.edit
+import androidx.datastore.preferences.core.emptyPreferences
 import androidx.datastore.preferences.core.intPreferencesKey
 import androidx.datastore.preferences.core.longPreferencesKey
 import androidx.datastore.preferences.preferencesDataStoreFile
@@ -68,7 +70,7 @@
 
     suspend fun updateGestureEduModel(
         gestureType: GestureType,
-        transform: (GestureEduModel) -> GestureEduModel
+        transform: (GestureEduModel) -> GestureEduModel,
     )
 
     suspend fun updateEduDeviceConnectionTime(
@@ -149,6 +151,8 @@
                         String.format(DATASTORE_DIR, userId)
                     )
                 },
+                corruptionHandler =
+                    ReplaceFileCorruptionHandler(produceNewData = { emptyPreferences() }),
                 scope = newDsScope,
             )
         dataStoreScope = newDsScope
@@ -159,7 +163,7 @@
 
     private fun getGestureEduModel(
         gestureType: GestureType,
-        preferences: Preferences
+        preferences: Preferences,
     ): GestureEduModel {
         return GestureEduModel(
             signalCount = preferences[getSignalCountKey(gestureType)] ?: 0,
@@ -183,7 +187,7 @@
 
     override suspend fun updateGestureEduModel(
         gestureType: GestureType,
-        transform: (GestureEduModel) -> GestureEduModel
+        transform: (GestureEduModel) -> GestureEduModel,
     ) {
         datastore.filterNotNull().first().edit { preferences ->
             val currentModel = getGestureEduModel(gestureType, preferences)
@@ -193,17 +197,17 @@
             setInstant(
                 preferences,
                 updatedModel.lastShortcutTriggeredTime,
-                getLastShortcutTriggeredTimeKey(gestureType)
+                getLastShortcutTriggeredTimeKey(gestureType),
             )
             setInstant(
                 preferences,
                 updatedModel.usageSessionStartTime,
-                getUsageSessionStartTimeKey(gestureType)
+                getUsageSessionStartTimeKey(gestureType),
             )
             setInstant(
                 preferences,
                 updatedModel.lastEducationTime,
-                getLastEducationTimeKey(gestureType)
+                getLastEducationTimeKey(gestureType),
             )
         }
     }
@@ -220,12 +224,12 @@
             setInstant(
                 preferences,
                 updatedModel.keyboardFirstConnectionTime,
-                getKeyboardFirstConnectionTimeKey()
+                getKeyboardFirstConnectionTimeKey(),
             )
             setInstant(
                 preferences,
                 updatedModel.touchpadFirstConnectionTime,
-                getTouchpadFirstConnectionTimeKey()
+                getTouchpadFirstConnectionTimeKey(),
             )
         }
     }
@@ -235,7 +239,7 @@
             keyboardFirstConnectionTime =
                 preferences[getKeyboardFirstConnectionTimeKey()]?.let { Instant.ofEpochSecond(it) },
             touchpadFirstConnectionTime =
-                preferences[getTouchpadFirstConnectionTimeKey()]?.let { Instant.ofEpochSecond(it) }
+                preferences[getTouchpadFirstConnectionTimeKey()]?.let { Instant.ofEpochSecond(it) },
         )
     }
 
@@ -263,7 +267,7 @@
     private fun setInstant(
         preferences: MutablePreferences,
         instant: Instant?,
-        key: Preferences.Key<Long>
+        key: Preferences.Key<Long>,
     ) {
         if (instant != null) {
             // Use epochSecond because an instant is defined as a signed long (64bit number) of
diff --git a/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt b/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt
index 63ac783..129a6bb 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/FlagDependencies.kt
@@ -35,7 +35,6 @@
 import com.android.systemui.shade.shared.flag.DualShade
 import com.android.systemui.statusbar.notification.collection.SortBySectionTimeFlag
 import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyShadeFix
-import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
 import com.android.systemui.statusbar.notification.interruption.VisualInterruptionRefactor
 import com.android.systemui.statusbar.notification.shared.NotificationAvalancheSuppression
 import com.android.systemui.statusbar.notification.shared.NotificationMinimalism
@@ -57,7 +56,6 @@
         NotificationAvalancheSuppression.token dependsOn VisualInterruptionRefactor.token
         PriorityPeopleSection.token dependsOn SortBySectionTimeFlag.token
         NotificationMinimalism.token dependsOn NotificationThrottleHun.token
-        ModesEmptyShadeFix.token dependsOn FooterViewRefactor.token
         ModesEmptyShadeFix.token dependsOn modesUi
 
         // SceneContainer dependencies
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index c039e01..2c33c0b 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -76,21 +76,10 @@
     val LOCKSCREEN_CUSTOM_CLOCKS =
         resourceBooleanFlag(R.bool.config_enableLockScreenCustomClocks, "lockscreen_custom_clocks")
 
-    /**
-     * Migration from the legacy isDozing/dozeAmount paths to the new KeyguardTransitionRepository
-     * will occur in stages. This is one stage of many to come.
-     */
-    // TODO(b/255607168): Tracking Bug
-    @JvmField val DOZING_MIGRATION_1 = unreleasedFlag("dozing_migration_1")
-
     /** Flag to control the revamp of keyguard biometrics progress animation */
     // TODO(b/244313043): Tracking bug
     @JvmField val BIOMETRICS_ANIMATION_REVAMP = unreleasedFlag("biometrics_animation_revamp")
 
-    // flag for controlling auto pin confirmation and material u shapes in bouncer
-    @JvmField
-    val AUTO_PIN_CONFIRMATION = releasedFlag("auto_pin_confirmation", "auto_pin_confirmation")
-
     /** Enables code to show contextual loyalty cards in wallet entrypoints */
     // TODO(b/294110497): Tracking Bug
     @JvmField
@@ -100,10 +89,6 @@
     // TODO(b/242908637): Tracking Bug
     @JvmField val WALLPAPER_FULLSCREEN_PREVIEW = releasedFlag("wallpaper_fullscreen_preview")
 
-    /** Inflate and bind views upon emitting a blueprint value . */
-    // TODO(b/297365780): Tracking Bug
-    @JvmField val LAZY_INFLATE_KEYGUARD = releasedFlag("lazy_inflate_keyguard")
-
     /** Enables UI updates for AI wallpapers in the wallpaper picker. */
     // TODO(b/267722622): Tracking Bug
     @JvmField val WALLPAPER_PICKER_UI_FOR_AIWP = releasedFlag("wallpaper_picker_ui_for_aiwp")
diff --git a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/data/model/DeviceSchedulerInfo.kt b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/data/model/DeviceSchedulerInfo.kt
index c436ef0..9a065be 100644
--- a/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/data/model/DeviceSchedulerInfo.kt
+++ b/packages/SystemUI/src/com/android/systemui/inputdevice/tutorial/data/model/DeviceSchedulerInfo.kt
@@ -19,23 +19,26 @@
 import java.time.Instant
 
 data class DeviceSchedulerInfo(
-    var launchTime: Instant? = null,
+    var launchedTime: Instant? = null,
     var firstConnectionTime: Instant? = null,
-    var isNotified: Boolean = false,
+    var notifiedTime: Instant? = null,
 ) {
     constructor(
         launchTimeSec: Long?,
         firstConnectionTimeSec: Long?,
-        isNotified: Boolean = false,
+        notifyTimeSec: Long?,
     ) : this(
         launchTimeSec?.let { Instant.ofEpochSecond(it) },
         firstConnectionTimeSec?.let { Instant.ofEpochSecond(it) },
-        isNotified,
+        notifyTimeSec?.let { Instant.ofEpochSecond(it) },
     )
 
     val wasEverConnected: Boolean
         get() = firstConnectionTime != null
 
     val isLaunched: Boolean
-        get() = launchTime != null
+        get() = launchedTime != null
+
+    val isNotified: Boolean
+        get() = notifiedTime != 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 526e7db..8b0accd 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
@@ -19,7 +19,6 @@
 import android.content.Context
 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
@@ -49,35 +48,42 @@
         preferencesDataStore(name = dataStoreName, scope = backgroundScope)
 
     suspend fun setScheduledTutorialLaunchTime(device: DeviceType, time: Instant) {
-        applicationContext.dataStore.edit { pref -> pref[getLaunchKey(device)] = time.epochSecond }
+        updateData(key = getLaunchedKey(device), value = time.epochSecond)
     }
 
     suspend fun isScheduledTutorialLaunched(deviceType: DeviceType): Boolean =
         loadData()[deviceType]!!.isLaunched
 
     suspend fun getScheduledTutorialLaunchTime(deviceType: DeviceType): Instant? =
-        loadData()[deviceType]!!.launchTime
+        loadData()[deviceType]!!.launchedTime
 
     suspend fun setFirstConnectionTime(device: DeviceType, time: Instant) {
-        applicationContext.dataStore.edit { pref -> pref[getConnectKey(device)] = time.epochSecond }
+        updateData(key = getConnectedKey(device), value = time.epochSecond)
     }
 
-    suspend fun setNotified(device: DeviceType) {
-        applicationContext.dataStore.edit { pref -> pref[getNotificationKey(device)] = true }
-    }
-
-    suspend fun isNotified(deviceType: DeviceType): Boolean = loadData()[deviceType]!!.isNotified
-
     suspend fun wasEverConnected(deviceType: DeviceType): Boolean =
         loadData()[deviceType]!!.wasEverConnected
 
     suspend fun getFirstConnectionTime(deviceType: DeviceType): Instant? =
         loadData()[deviceType]!!.firstConnectionTime
 
+    suspend fun setNotifiedTime(device: DeviceType, time: Instant) {
+        updateData(key = getNotifiedKey(device), value = time.epochSecond)
+    }
+
+    suspend fun isNotified(deviceType: DeviceType): Boolean = loadData()[deviceType]!!.isNotified
+
+    suspend fun getNotifiedTime(deviceType: DeviceType): Instant? =
+        loadData()[deviceType]!!.notifiedTime
+
     private suspend fun loadData(): Map<DeviceType, DeviceSchedulerInfo> {
         return applicationContext.dataStore.data.map { pref -> getSchedulerInfo(pref) }.first()
     }
 
+    private suspend fun <T> updateData(key: Preferences.Key<T>, value: T) {
+        applicationContext.dataStore.edit { pref -> pref[key] = value }
+    }
+
     private fun getSchedulerInfo(pref: Preferences): Map<DeviceType, DeviceSchedulerInfo> {
         return mapOf(
             DeviceType.KEYBOARD to getDeviceSchedulerInfo(pref, DeviceType.KEYBOARD),
@@ -86,20 +92,20 @@
     }
 
     private fun getDeviceSchedulerInfo(pref: Preferences, device: DeviceType): DeviceSchedulerInfo {
-        val launchTime = pref[getLaunchKey(device)]
-        val connectionTime = pref[getConnectKey(device)]
-        val isNotified = pref[getNotificationKey(device)] == true
-        return DeviceSchedulerInfo(launchTime, connectionTime, isNotified)
+        val launchedTime = pref[getLaunchedKey(device)]
+        val connectedTime = pref[getConnectedKey(device)]
+        val notifiedTime = pref[getNotifiedKey(device)]
+        return DeviceSchedulerInfo(launchedTime, connectedTime, notifiedTime)
     }
 
-    private fun getLaunchKey(device: DeviceType) =
-        longPreferencesKey(device.name + LAUNCH_TIME_SUFFIX)
+    private fun getLaunchedKey(device: DeviceType) =
+        longPreferencesKey(device.name + LAUNCHED_TIME_SUFFIX)
 
-    private fun getConnectKey(device: DeviceType) =
-        longPreferencesKey(device.name + CONNECT_TIME_SUFFIX)
+    private fun getConnectedKey(device: DeviceType) =
+        longPreferencesKey(device.name + CONNECTED_TIME_SUFFIX)
 
-    private fun getNotificationKey(device: DeviceType) =
-        booleanPreferencesKey(device.name + NOTIFIED_SUFFIX)
+    private fun getNotifiedKey(device: DeviceType) =
+        longPreferencesKey(device.name + NOTIFIED_TIME_SUFFIX)
 
     suspend fun clear() {
         applicationContext.dataStore.edit { it.clear() }
@@ -107,9 +113,9 @@
 
     companion object {
         const val DATASTORE_NAME = "TutorialScheduler"
-        const val LAUNCH_TIME_SUFFIX = "_LAUNCH_TIME"
-        const val CONNECT_TIME_SUFFIX = "_CONNECT_TIME"
-        const val NOTIFIED_SUFFIX = "_NOTIFIED"
+        const val LAUNCHED_TIME_SUFFIX = "_LAUNCHED_TIME"
+        const val CONNECTED_TIME_SUFFIX = "_CONNECTED_TIME"
+        const val NOTIFIED_TIME_SUFFIX = "_NOTIFIED_TIME"
     }
 }
 
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 419eefe..9607053 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
@@ -111,9 +111,9 @@
             val tutorialType = resolveTutorialType(it)
 
             if (tutorialType == TutorialType.KEYBOARD || tutorialType == TutorialType.BOTH)
-                repo.setNotified(KEYBOARD)
+                repo.setNotifiedTime(KEYBOARD, Instant.now())
             if (tutorialType == TutorialType.TOUCHPAD || tutorialType == TutorialType.BOTH)
-                repo.setNotified(TOUCHPAD)
+                repo.setNotifiedTime(TOUCHPAD, Instant.now())
 
             logger.logTutorialLaunched(tutorialType)
             tutorialType
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ShortcutHelperCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ShortcutHelperCoreStartable.kt
new file mode 100644
index 0000000..c702ba9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ShortcutHelperCoreStartable.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.keyboard.shortcut
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.os.UserHandle
+import com.android.systemui.CoreStartable
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.keyboard.shortcut.data.repository.CustomInputGesturesRepository
+import com.android.systemui.keyboard.shortcut.data.repository.ShortcutHelperStateRepository
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.statusbar.CommandQueue
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+
+@SysUISingleton
+class ShortcutHelperCoreStartable
+@Inject
+constructor(
+    private val commandQueue: CommandQueue,
+    private val broadcastDispatcher: BroadcastDispatcher,
+    private val stateRepository: ShortcutHelperStateRepository,
+    private val activityStarter: ActivityStarter,
+    @Background private val backgroundScope: CoroutineScope,
+    private val customInputGesturesRepository: CustomInputGesturesRepository
+) : CoreStartable {
+    override fun start() {
+        registerBroadcastReceiver(
+            action = Intent.ACTION_SHOW_KEYBOARD_SHORTCUTS,
+            onReceive = { showShortcutHelper() },
+        )
+        registerBroadcastReceiver(
+            action = Intent.ACTION_DISMISS_KEYBOARD_SHORTCUTS,
+            onReceive = { stateRepository.hide() },
+        )
+        registerBroadcastReceiver(
+            action = Intent.ACTION_CLOSE_SYSTEM_DIALOGS,
+            onReceive = { stateRepository.hide() },
+        )
+        registerBroadcastReceiver(
+            action = Intent.ACTION_USER_SWITCHED,
+            onReceive = { customInputGesturesRepository.refreshCustomInputGestures() },
+        )
+        commandQueue.addCallback(
+            object : CommandQueue.Callbacks {
+                override fun dismissKeyboardShortcutsMenu() {
+                    stateRepository.hide()
+                }
+
+                override fun toggleKeyboardShortcutsMenu(deviceId: Int) {
+                    toggleShortcutHelper(deviceId)
+                }
+            }
+        )
+    }
+
+    private fun registerBroadcastReceiver(action: String, onReceive: () -> Unit) {
+        broadcastDispatcher.registerReceiver(
+            receiver =
+                object : BroadcastReceiver() {
+                    override fun onReceive(context: Context, intent: Intent) {
+                        onReceive()
+                    }
+                },
+            filter = IntentFilter(action),
+            flags = Context.RECEIVER_EXPORTED or Context.RECEIVER_VISIBLE_TO_INSTANT_APPS,
+            user = UserHandle.ALL,
+        )
+    }
+
+    private fun showShortcutHelper() {
+        dismissKeyguardThenPerformShortcutHelperAction { stateRepository.show() }
+    }
+
+    private fun toggleShortcutHelper(deviceId: Int? = null) {
+        dismissKeyguardThenPerformShortcutHelperAction { stateRepository.toggle(deviceId) }
+    }
+
+    private fun dismissKeyguardThenPerformShortcutHelperAction(action: suspend () -> Unit) {
+        activityStarter.dismissKeyguardThenExecute(
+            /* action= */ {
+                backgroundScope.launch { action() }
+                false
+            },
+            /* cancel= */ {},
+            /* afterKeyguardGone= */ true,
+        )
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ShortcutHelperModule.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ShortcutHelperModule.kt
index 1af7340..d8532c1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ShortcutHelperModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ShortcutHelperModule.kt
@@ -21,7 +21,6 @@
 import com.android.systemui.keyboard.shortcut.data.repository.CustomShortcutCategoriesRepository
 import com.android.systemui.keyboard.shortcut.data.repository.DefaultShortcutCategoriesRepository
 import com.android.systemui.keyboard.shortcut.data.repository.ShortcutCategoriesRepository
-import com.android.systemui.keyboard.shortcut.data.repository.ShortcutHelperStateRepository
 import com.android.systemui.keyboard.shortcut.data.source.AppCategoriesShortcutsSource
 import com.android.systemui.keyboard.shortcut.data.source.CurrentAppShortcutsSource
 import com.android.systemui.keyboard.shortcut.data.source.InputShortcutsSource
@@ -95,8 +94,8 @@
 
         @Provides
         @IntoMap
-        @ClassKey(ShortcutHelperStateRepository::class)
-        fun repo(implLazy: Lazy<ShortcutHelperStateRepository>): CoreStartable {
+        @ClassKey(ShortcutHelperCoreStartable::class)
+        fun repo(implLazy: Lazy<ShortcutHelperCoreStartable>): CoreStartable {
             return if (keyboardShortcutHelperRewrite()) {
                 implLazy.get()
             } else {
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomInputGesturesRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomInputGesturesRepository.kt
index 36cd400..e5c638c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomInputGesturesRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/CustomInputGesturesRepository.kt
@@ -25,6 +25,7 @@
 import android.hardware.input.InputManager.CUSTOM_INPUT_GESTURE_RESULT_SUCCESS
 import android.hardware.input.InputSettings
 import android.util.Log
+import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.keyboard.shared.model.ShortcutCustomizationRequestResult
 import com.android.systemui.keyboard.shared.model.ShortcutCustomizationRequestResult.ERROR_OTHER
@@ -37,6 +38,7 @@
 import javax.inject.Inject
 import kotlin.coroutines.CoroutineContext
 
+@SysUISingleton
 class CustomInputGesturesRepository
 @Inject
 constructor(private val userTracker: UserTracker,
@@ -56,7 +58,7 @@
     val customInputGestures =
         _customInputGesture.onStart { refreshCustomInputGestures() }
 
-    private fun refreshCustomInputGestures() {
+    fun refreshCustomInputGestures() {
         setCustomInputGestures(inputGestures = retrieveCustomInputGestures())
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestureMaps.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestureMaps.kt
index d7be5e6..e255bde 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestureMaps.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/InputGestureMaps.kt
@@ -27,14 +27,19 @@
 import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS
 import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT
 import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_LOCK_SCREEN
+import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_MINIMIZE_FREEFORM_WINDOW
+import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY
 import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION
 import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_NOTES
 import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER
 import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS
 import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER
+import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_LEFT_FREEFORM_WINDOW
+import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW
 import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT
 import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT
 import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT
+import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAXIMIZE_FREEFORM_WINDOW
 import android.hardware.input.KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL
 import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.AppCategories
 import com.android.systemui.keyboard.shortcut.shared.model.ShortcutCategoryType.MultiTasking
@@ -66,6 +71,11 @@
             KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION to MultiTasking,
             KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_LEFT to MultiTasking,
             KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT to MultiTasking,
+            KEY_GESTURE_TYPE_SNAP_LEFT_FREEFORM_WINDOW to MultiTasking,
+            KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW to MultiTasking,
+            KEY_GESTURE_TYPE_MINIMIZE_FREEFORM_WINDOW to MultiTasking,
+            KEY_GESTURE_TYPE_TOGGLE_MAXIMIZE_FREEFORM_WINDOW to MultiTasking,
+            KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY to MultiTasking,
 
             // App Category
             KEY_GESTURE_TYPE_LAUNCH_APPLICATION to AppCategories,
@@ -102,15 +112,23 @@
                 R.string.shortcutHelper_category_split_screen,
             KEY_GESTURE_TYPE_CHANGE_SPLITSCREEN_FOCUS_RIGHT to
                 R.string.shortcutHelper_category_split_screen,
+            KEY_GESTURE_TYPE_SNAP_LEFT_FREEFORM_WINDOW to
+                R.string.shortcutHelper_category_split_screen,
+            KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW to
+                R.string.shortcutHelper_category_split_screen,
+            KEY_GESTURE_TYPE_MINIMIZE_FREEFORM_WINDOW to
+                R.string.shortcutHelper_category_split_screen,
+            KEY_GESTURE_TYPE_TOGGLE_MAXIMIZE_FREEFORM_WINDOW to
+                R.string.shortcutHelper_category_split_screen,
+            KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY to R.string.shortcutHelper_category_split_screen,
 
             // App Category
-            KEY_GESTURE_TYPE_LAUNCH_APPLICATION to
-                R.string.keyboard_shortcut_group_applications,
+            KEY_GESTURE_TYPE_LAUNCH_APPLICATION to R.string.keyboard_shortcut_group_applications,
         )
 
     /**
-     * App Category shortcut labels are mapped dynamically based on intent
-     * see [InputGestureDataAdapter.fetchShortcutLabelByAppLaunchData]
+     * App Category shortcut labels are mapped dynamically based on intent see
+     * [InputGestureDataAdapter.fetchShortcutLabelByAppLaunchData]
      */
     val gestureToInternalKeyboardShortcutInfoLabelResIdMap =
         mapOf(
@@ -136,6 +154,16 @@
             KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT to R.string.system_multitasking_lhs,
             KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT to R.string.system_multitasking_rhs,
             KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION to R.string.system_multitasking_full_screen,
+            KEY_GESTURE_TYPE_SNAP_LEFT_FREEFORM_WINDOW to
+                R.string.system_desktop_mode_snap_left_window,
+            KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW to
+                R.string.system_desktop_mode_snap_right_window,
+            KEY_GESTURE_TYPE_MINIMIZE_FREEFORM_WINDOW to
+                R.string.system_desktop_mode_minimize_window,
+            KEY_GESTURE_TYPE_TOGGLE_MAXIMIZE_FREEFORM_WINDOW to
+                R.string.system_desktop_mode_toggle_maximize_window,
+            KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY to
+                R.string.system_multitasking_move_to_next_display,
         )
 
     val shortcutLabelToKeyGestureTypeMap: Map<String, Int>
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperStateRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperStateRepository.kt
index aa6b61b..42a13f5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperStateRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperStateRepository.kt
@@ -16,73 +16,44 @@
 
 package com.android.systemui.keyboard.shortcut.data.repository
 
-import android.content.BroadcastReceiver
-import android.content.Context
-import android.content.Intent
-import android.content.IntentFilter
 import android.hardware.input.InputManager
-import android.os.UserHandle
 import android.view.KeyCharacterMap.VIRTUAL_KEYBOARD
-import com.android.systemui.CoreStartable
-import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.keyboard.shortcut.shared.model.ShortcutHelperState
 import com.android.systemui.keyboard.shortcut.shared.model.ShortcutHelperState.Active
 import com.android.systemui.keyboard.shortcut.shared.model.ShortcutHelperState.Inactive
 import com.android.systemui.shared.hardware.findInputDevice
-import com.android.systemui.statusbar.CommandQueue
-import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.MutableStateFlow
-import com.android.app.tracing.coroutines.launchTraced as launch
+import kotlinx.coroutines.flow.asStateFlow
 import kotlinx.coroutines.withContext
+import javax.inject.Inject
 
 @SysUISingleton
 class ShortcutHelperStateRepository
 @Inject
 constructor(
-    private val commandQueue: CommandQueue,
-    private val broadcastDispatcher: BroadcastDispatcher,
     private val inputManager: InputManager,
-    @Background private val backgroundScope: CoroutineScope,
     @Background private val backgroundDispatcher: CoroutineDispatcher,
-) : CoreStartable {
+) {
+    private val _state = MutableStateFlow<ShortcutHelperState>(Inactive)
+    val state = _state.asStateFlow()
 
-    val state = MutableStateFlow<ShortcutHelperState>(Inactive)
+    suspend fun toggle(deviceId: Int? = null) {
+        if (_state.value is Inactive) {
+            show(deviceId)
+        } else {
+            hide()
+        }
+    }
 
-    override fun start() {
-        registerBroadcastReceiver(
-            action = Intent.ACTION_SHOW_KEYBOARD_SHORTCUTS,
-            onReceive = {
-                backgroundScope.launch { state.value = Active(findPhysicalKeyboardId()) }
-            }
-        )
-        registerBroadcastReceiver(
-            action = Intent.ACTION_DISMISS_KEYBOARD_SHORTCUTS,
-            onReceive = { state.value = Inactive }
-        )
-        registerBroadcastReceiver(
-            action = Intent.ACTION_CLOSE_SYSTEM_DIALOGS,
-            onReceive = { state.value = Inactive }
-        )
-        commandQueue.addCallback(
-            object : CommandQueue.Callbacks {
-                override fun dismissKeyboardShortcutsMenu() {
-                    state.value = Inactive
-                }
+    suspend fun show(deviceId: Int? = null) {
+        _state.value = Active(deviceId ?: findPhysicalKeyboardId())
+    }
 
-                override fun toggleKeyboardShortcutsMenu(deviceId: Int) {
-                    state.value =
-                        if (state.value is Inactive) {
-                            Active(deviceId)
-                        } else {
-                            Inactive
-                        }
-                }
-            }
-        )
+    fun hide() {
+        _state.value = Inactive
     }
 
     private suspend fun findPhysicalKeyboardId() =
@@ -92,21 +63,4 @@
             return@withContext firstEnabledPhysicalKeyboard?.id ?: VIRTUAL_KEYBOARD
         }
 
-    fun hide() {
-        state.value = Inactive
-    }
-
-    private fun registerBroadcastReceiver(action: String, onReceive: () -> Unit) {
-        broadcastDispatcher.registerReceiver(
-            receiver =
-                object : BroadcastReceiver() {
-                    override fun onReceive(context: Context, intent: Intent) {
-                        onReceive()
-                    }
-                },
-            filter = IntentFilter(action),
-            flags = Context.RECEIVER_EXPORTED or Context.RECEIVER_VISIBLE_TO_INSTANT_APPS,
-            user = UserHandle.ALL,
-        )
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperStateInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperStateInteractor.kt
index cea3b64..5505189 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperStateInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/domain/interactor/ShortcutHelperStateInteractor.kt
@@ -23,10 +23,9 @@
 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.flow.Flow
-import kotlinx.coroutines.flow.asStateFlow
+import javax.inject.Inject
 import com.android.app.tracing.coroutines.launchTraced as launch
 
 @SysUISingleton
@@ -39,7 +38,7 @@
     private val repository: ShortcutHelperStateRepository
 ) {
 
-    val state: Flow<ShortcutHelperState> = repository.state.asStateFlow()
+    val state: Flow<ShortcutHelperState> = repository.state
 
     fun onViewClosed() {
         repository.hide()
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutCustomizationDialogStarter.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutCustomizationDialogStarter.kt
index 274fa59..a16b4a6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutCustomizationDialogStarter.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/ShortcutCustomizationDialogStarter.kt
@@ -53,11 +53,11 @@
 
     override suspend fun onActivated(): Nothing {
         viewModel.shortcutCustomizationUiState.collect { uiState ->
-            when(uiState){
+            when (uiState) {
                 is AddShortcutDialog,
                 is DeleteShortcutDialog,
                 is ResetShortcutDialog -> {
-                    if (dialog == null){
+                    if (dialog == null) {
                         dialog = createDialog().also { it.show() }
                     }
                 }
@@ -85,7 +85,9 @@
             ShortcutCustomizationDialog(
                 uiState = uiState,
                 modifier = Modifier.width(364.dp).wrapContentHeight().padding(vertical = 24.dp),
-                onKeyPress = { viewModel.onKeyPressed(it) },
+                onShortcutKeyCombinationSelected = {
+                    viewModel.onShortcutKeyCombinationSelected(it)
+                },
                 onCancel = { dialog.dismiss() },
                 onConfirmSetShortcut = { coroutineScope.launch { viewModel.onSetShortcut() } },
                 onConfirmDeleteShortcut = {
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutCustomizer.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutCustomizer.kt
index ac6708a..d9e55f8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutCustomizer.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutCustomizer.kt
@@ -46,10 +46,15 @@
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.focus.focusProperties
 import androidx.compose.ui.focus.focusRequester
 import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.input.key.Key
 import androidx.compose.ui.input.key.KeyEvent
-import androidx.compose.ui.input.key.onKeyEvent
+import androidx.compose.ui.input.key.KeyEventType
+import androidx.compose.ui.input.key.key
+import androidx.compose.ui.input.key.onPreviewKeyEvent
+import androidx.compose.ui.input.key.type
 import androidx.compose.ui.res.painterResource
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.text.font.FontWeight
@@ -64,7 +69,7 @@
 fun ShortcutCustomizationDialog(
     uiState: ShortcutCustomizationUiState,
     modifier: Modifier = Modifier,
-    onKeyPress: (KeyEvent) -> Boolean,
+    onShortcutKeyCombinationSelected: (KeyEvent) -> Boolean,
     onCancel: () -> Unit,
     onConfirmSetShortcut: () -> Unit,
     onConfirmDeleteShortcut: () -> Unit,
@@ -72,7 +77,13 @@
 ) {
     when (uiState) {
         is ShortcutCustomizationUiState.AddShortcutDialog -> {
-            AddShortcutDialog(modifier, uiState, onKeyPress, onCancel, onConfirmSetShortcut)
+            AddShortcutDialog(
+                modifier,
+                uiState,
+                onShortcutKeyCombinationSelected,
+                onCancel,
+                onConfirmSetShortcut,
+            )
         }
         is ShortcutCustomizationUiState.DeleteShortcutDialog -> {
             DeleteShortcutDialog(modifier, onCancel, onConfirmDeleteShortcut)
@@ -90,29 +101,27 @@
 private fun AddShortcutDialog(
     modifier: Modifier,
     uiState: ShortcutCustomizationUiState.AddShortcutDialog,
-    onKeyPress: (KeyEvent) -> Boolean,
+    onShortcutKeyCombinationSelected: (KeyEvent) -> Boolean,
     onCancel: () -> Unit,
-    onConfirmSetShortcut: () -> Unit
-){
+    onConfirmSetShortcut: () -> Unit,
+) {
     Column(modifier = modifier) {
         Title(uiState.shortcutLabel)
         Description(
-            text =
-            stringResource(
-                id = R.string.shortcut_customize_mode_add_shortcut_description
-            )
+            text = stringResource(id = R.string.shortcut_customize_mode_add_shortcut_description)
         )
         PromptShortcutModifier(
             modifier =
-            Modifier.padding(top = 24.dp, start = 116.5.dp, end = 116.5.dp)
-                .width(131.dp)
-                .height(48.dp),
+                Modifier.padding(top = 24.dp, start = 116.5.dp, end = 116.5.dp)
+                    .width(131.dp)
+                    .height(48.dp),
             defaultModifierKey = uiState.defaultCustomShortcutModifierKey,
         )
         SelectedKeyCombinationContainer(
             shouldShowError = uiState.errorMessage.isNotEmpty(),
-            onKeyPress = onKeyPress,
+            onShortcutKeyCombinationSelected = onShortcutKeyCombinationSelected,
             pressedKeys = uiState.pressedKeys,
+            onConfirmSetShortcut = onConfirmSetShortcut,
         )
         ErrorMessageContainer(uiState.errorMessage)
         DialogButtons(
@@ -120,9 +129,7 @@
             isConfirmButtonEnabled = uiState.pressedKeys.isNotEmpty(),
             onConfirm = onConfirmSetShortcut,
             confirmButtonText =
-            stringResource(
-                R.string.shortcut_helper_customize_dialog_set_shortcut_button_label
-            ),
+                stringResource(R.string.shortcut_helper_customize_dialog_set_shortcut_button_label),
         )
     }
 }
@@ -131,20 +138,15 @@
 private fun DeleteShortcutDialog(
     modifier: Modifier,
     onCancel: () -> Unit,
-    onConfirmDeleteShortcut: () -> Unit
-){
+    onConfirmDeleteShortcut: () -> Unit,
+) {
     ConfirmationDialog(
         modifier = modifier,
-        title =
-        stringResource(
-            id = R.string.shortcut_customize_mode_remove_shortcut_dialog_title
-        ),
+        title = stringResource(id = R.string.shortcut_customize_mode_remove_shortcut_dialog_title),
         description =
-        stringResource(
-            id = R.string.shortcut_customize_mode_remove_shortcut_description
-        ),
+            stringResource(id = R.string.shortcut_customize_mode_remove_shortcut_description),
         confirmButtonText =
-        stringResource(R.string.shortcut_helper_customize_dialog_remove_button_label),
+            stringResource(R.string.shortcut_helper_customize_dialog_remove_button_label),
         onCancel = onCancel,
         onConfirm = onConfirmDeleteShortcut,
     )
@@ -154,20 +156,15 @@
 private fun ResetShortcutDialog(
     modifier: Modifier,
     onCancel: () -> Unit,
-    onConfirmResetShortcut: () -> Unit
-){
+    onConfirmResetShortcut: () -> Unit,
+) {
     ConfirmationDialog(
         modifier = modifier,
-        title =
-        stringResource(
-            id = R.string.shortcut_customize_mode_reset_shortcut_dialog_title
-        ),
+        title = stringResource(id = R.string.shortcut_customize_mode_reset_shortcut_dialog_title),
         description =
-        stringResource(
-            id = R.string.shortcut_customize_mode_reset_shortcut_description
-        ),
+            stringResource(id = R.string.shortcut_customize_mode_reset_shortcut_description),
         confirmButtonText =
-        stringResource(R.string.shortcut_helper_customize_dialog_reset_button_label),
+            stringResource(R.string.shortcut_helper_customize_dialog_reset_button_label),
         onCancel = onCancel,
         onConfirm = onConfirmResetShortcut,
     )
@@ -200,6 +197,9 @@
     onConfirm: () -> Unit,
     confirmButtonText: String,
 ) {
+    val focusRequester = remember { FocusRequester() }
+    LaunchedEffect(Unit) { focusRequester.requestFocus() }
+
     Row(
         modifier =
             Modifier.padding(top = 24.dp, start = 24.dp, end = 24.dp)
@@ -217,6 +217,10 @@
         )
         Spacer(modifier = Modifier.width(8.dp))
         ShortcutHelperButton(
+            modifier =
+                Modifier.focusRequester(focusRequester).focusProperties {
+                    canFocus = true
+                }, // enable focus on touch/click mode
             onClick = onConfirm,
             color = MaterialTheme.colorScheme.primary,
             width = 116.dp,
@@ -247,8 +251,9 @@
 @Composable
 private fun SelectedKeyCombinationContainer(
     shouldShowError: Boolean,
-    onKeyPress: (KeyEvent) -> Boolean,
+    onShortcutKeyCombinationSelected: (KeyEvent) -> Boolean,
     pressedKeys: List<ShortcutKey>,
+    onConfirmSetShortcut: () -> Unit,
 ) {
     val interactionSource = remember { MutableInteractionSource() }
     val isFocused by interactionSource.collectIsFocusedAsState()
@@ -268,7 +273,18 @@
             Modifier.padding(all = 16.dp)
                 .sizeIn(minWidth = 332.dp, minHeight = 56.dp)
                 .border(width = 2.dp, color = outlineColor, shape = RoundedCornerShape(50.dp))
-                .onKeyEvent { onKeyPress(it) }
+                .onPreviewKeyEvent { keyEvent ->
+                    val keyEventProcessed = onShortcutKeyCombinationSelected(keyEvent)
+                    if (
+                        !keyEventProcessed &&
+                            keyEvent.key == Key.Enter &&
+                            keyEvent.type == KeyEventType.KeyUp
+                    ) {
+                        onConfirmSetShortcut()
+                        true
+                    } else keyEventProcessed
+                }
+                .focusProperties { canFocus = true } // enables keyboard focus when in touch mode
                 .focusRequester(focusRequester),
         interactionSource = interactionSource,
     ) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt
index aea583d..ba31d08 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/ShortcutHelper.kt
@@ -729,6 +729,7 @@
         contentColor = MaterialTheme.colorScheme.primary,
         contentPaddingVertical = 0.dp,
         contentPaddingHorizontal = 0.dp,
+        contentDescription = stringResource(R.string.shortcut_helper_add_shortcut_button_label),
     )
 }
 
@@ -749,6 +750,7 @@
         contentColor = MaterialTheme.colorScheme.primary,
         contentPaddingVertical = 0.dp,
         contentPaddingHorizontal = 0.dp,
+        contentDescription = stringResource(R.string.shortcut_helper_delete_shortcut_button_label),
     )
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/Surfaces.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/Surfaces.kt
index 55c0fe2..9a380f4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/Surfaces.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/composable/Surfaces.kt
@@ -230,6 +230,7 @@
     contentPaddingVertical: Dp = 10.dp,
     enabled: Boolean = true,
     border: BorderStroke? = null,
+    contentDescription: String? = null,
 ) {
     ShortcutHelperButtonSurface(
         onClick = onClick,
@@ -254,8 +255,7 @@
                 Icon(
                     tint = contentColor,
                     imageVector = iconSource.imageVector,
-                    contentDescription =
-                        null, // TODO this probably should not be null for accessibility.
+                    contentDescription = contentDescription,
                     modifier = Modifier.size(20.dp).wrapContentSize(Alignment.Center),
                 )
             }
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModel.kt
index 373eb25..915a66c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/shortcut/ui/viewmodel/ShortcutCustomizationViewModel.kt
@@ -46,6 +46,7 @@
     private val context: Context,
     private val shortcutCustomizationInteractor: ShortcutCustomizationInteractor,
 ) {
+    private var keyDownEventCache: KeyEvent? = null
     private val _shortcutCustomizationUiState =
         MutableStateFlow<ShortcutCustomizationUiState>(ShortcutCustomizationUiState.Inactive)
 
@@ -94,9 +95,16 @@
         shortcutCustomizationInteractor.updateUserSelectedKeyCombination(null)
     }
 
-    fun onKeyPressed(keyEvent: KeyEvent): Boolean {
-        if ((keyEvent.isMetaPressed && keyEvent.type == KeyEventType.KeyDown)) {
-            updatePressedKeys(keyEvent)
+    fun onShortcutKeyCombinationSelected(keyEvent: KeyEvent): Boolean {
+        if (isModifier(keyEvent)) {
+            return false
+        }
+        if (keyEvent.isMetaPressed && keyEvent.type == KeyEventType.KeyDown) {
+            keyDownEventCache = keyEvent
+            return true
+        } else if (keyEvent.type == KeyEventType.KeyUp && keyEvent.key == keyDownEventCache?.key) {
+            updatePressedKeys(keyDownEventCache!!)
+            clearKeyDownEventCache()
             return true
         }
         return false
@@ -157,16 +165,21 @@
         return (uiState as? AddShortcutDialog)?.copy(errorMessage = errorMessage) ?: uiState
     }
 
+    private fun isModifier(keyEvent: KeyEvent) = SUPPORTED_MODIFIERS.contains(keyEvent.key)
+
     private fun updatePressedKeys(keyEvent: KeyEvent) {
-        val isModifier = SUPPORTED_MODIFIERS.contains(keyEvent.key)
         val keyCombination =
             KeyCombination(
                 modifiers = keyEvent.nativeKeyEvent.modifiers,
-                keyCode = if (!isModifier) keyEvent.key.nativeKeyCode else null,
+                keyCode = if (!isModifier(keyEvent)) keyEvent.key.nativeKeyCode else null,
             )
         shortcutCustomizationInteractor.updateUserSelectedKeyCombination(keyCombination)
     }
 
+    private fun clearKeyDownEventCache() {
+        keyDownEventCache = null
+    }
+
     @AssistedFactory
     interface Factory {
         fun create(): ShortcutCustomizationViewModel
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 0f5f073..d61165c 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
@@ -129,7 +129,7 @@
                     val iconDrawable =
                         userContext.packageManager.getApplicationIcon(type.packageName)
                     IconSource(painter = DrawablePainter(drawable = iconDrawable))
-                } catch (e: NameNotFoundException) {
+                } catch (_: NameNotFoundException) {
                     Log.w(
                         "ShortcutHelperViewModel",
                         "Package not found when retrieving icon for ${type.packageName}",
@@ -234,6 +234,7 @@
 
     fun onViewClosed() {
         stateInteractor.onViewClosed()
+        resetSearchQuery()
     }
 
     fun onViewOpened() {
@@ -243,4 +244,8 @@
     fun onSearchQueryChanged(query: String) {
         searchQuery.value = query
     }
+
+    private fun resetSearchQuery(){
+        searchQuery.value = ""
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index d40fe46..5913839 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -538,27 +538,30 @@
 
         @Override // Binder interface
         public void onFinishedGoingToSleep(
-                @PowerManager.GoToSleepReason int pmSleepReason, boolean cameraGestureTriggered) {
+                @PowerManager.GoToSleepReason int pmSleepReason, boolean
+                powerButtonLaunchGestureTriggered) {
             trace("onFinishedGoingToSleep pmSleepReason=" + pmSleepReason
-                    + " cameraGestureTriggered=" + cameraGestureTriggered);
+                    + " powerButtonLaunchTriggered=" + powerButtonLaunchGestureTriggered);
             checkPermission();
             mKeyguardViewMediator.onFinishedGoingToSleep(
                     WindowManagerPolicyConstants.translateSleepReasonToOffReason(pmSleepReason),
-                    cameraGestureTriggered);
-            mPowerInteractor.onFinishedGoingToSleep(cameraGestureTriggered);
+                    powerButtonLaunchGestureTriggered);
+            mPowerInteractor.onFinishedGoingToSleep(powerButtonLaunchGestureTriggered);
             mKeyguardLifecyclesDispatcher.dispatch(
                     KeyguardLifecyclesDispatcher.FINISHED_GOING_TO_SLEEP);
         }
 
         @Override // Binder interface
         public void onStartedWakingUp(
-                @PowerManager.WakeReason int pmWakeReason, boolean cameraGestureTriggered) {
+                @PowerManager.WakeReason int pmWakeReason,
+                boolean powerButtonLaunchGestureTriggered) {
             trace("onStartedWakingUp pmWakeReason=" + pmWakeReason
-                    + " cameraGestureTriggered=" + cameraGestureTriggered);
+                    + " powerButtonLaunchGestureTriggered=" + powerButtonLaunchGestureTriggered);
             Trace.beginSection("KeyguardService.mBinder#onStartedWakingUp");
             checkPermission();
-            mKeyguardViewMediator.onStartedWakingUp(pmWakeReason, cameraGestureTriggered);
-            mPowerInteractor.onStartedWakingUp(pmWakeReason, cameraGestureTriggered);
+            mKeyguardViewMediator.onStartedWakingUp(pmWakeReason,
+                    powerButtonLaunchGestureTriggered);
+            mPowerInteractor.onStartedWakingUp(pmWakeReason, powerButtonLaunchGestureTriggered);
             mKeyguardLifecyclesDispatcher.dispatch(
                     KeyguardLifecyclesDispatcher.STARTED_WAKING_UP, pmWakeReason);
             Trace.endSection();
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 63ac509..6473628 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -109,6 +109,7 @@
 import androidx.annotation.VisibleForTesting;
 
 import com.android.app.animation.Interpolators;
+import com.android.app.tracing.coroutines.TrackTracer;
 import com.android.internal.foldables.FoldGracePeriodProvider;
 import com.android.internal.jank.InteractionJankMonitor;
 import com.android.internal.jank.InteractionJankMonitor.Configuration;
@@ -813,7 +814,7 @@
             if (targetUserId != mSelectedUserInteractor.getSelectedUserId()) {
                 return;
             }
-            if (DEBUG) Log.d(TAG, "keyguardDone");
+            Log.d(TAG, "keyguardDone");
             tryKeyguardDone();
         }
 
@@ -832,7 +833,7 @@
         @Override
         public void keyguardDonePending(int targetUserId) {
             Trace.beginSection("KeyguardViewMediator.mViewMediatorCallback#keyguardDonePending");
-            if (DEBUG) Log.d(TAG, "keyguardDonePending");
+            Log.d(TAG, "keyguardDonePending");
             if (targetUserId != mSelectedUserInteractor.getSelectedUserId()) {
                 Trace.endSection();
                 return;
@@ -2735,10 +2736,8 @@
     }
 
     private void tryKeyguardDone() {
-        if (DEBUG) {
-            Log.d(TAG, "tryKeyguardDone: pending - " + mKeyguardDonePending + ", animRan - "
-                    + mHideAnimationRun + " animRunning - " + mHideAnimationRunning);
-        }
+        Log.d(TAG, "tryKeyguardDone: pending - " + mKeyguardDonePending + ", animRan - "
+                + mHideAnimationRun + " animRunning - " + mHideAnimationRunning);
         if (!mKeyguardDonePending && mHideAnimationRun && !mHideAnimationRunning) {
             handleKeyguardDone();
         } else if (mSurfaceBehindRemoteAnimationRunning) {
@@ -3040,7 +3039,7 @@
     }
 
     private final Runnable mHideAnimationFinishedRunnable = () -> {
-        Log.e(TAG, "mHideAnimationFinishedRunnable#run");
+        Log.d(TAG, "mHideAnimationFinishedRunnable#run");
         mHideAnimationRunning = false;
         tryKeyguardDone();
     };
@@ -3983,7 +3982,7 @@
 
     public void setPendingLock(boolean hasPendingLock) {
         mPendingLock = hasPendingLock;
-        Trace.traceCounter(Trace.TRACE_TAG_APP, "pendingLock", mPendingLock ? 1 : 0);
+        TrackTracer.instantForGroup("keyguard", "pendingLock", mPendingLock ? 1 : 0);
     }
 
     private boolean isViewRootReady() {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ScreenLifecycle.java b/packages/SystemUI/src/com/android/systemui/keyguard/ScreenLifecycle.java
index 633628f..c318200 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ScreenLifecycle.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ScreenLifecycle.java
@@ -16,8 +16,7 @@
 
 package com.android.systemui.keyguard;
 
-import android.os.Trace;
-
+import com.android.app.tracing.coroutines.TrackTracer;
 import com.android.systemui.Dumpable;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.power.domain.interactor.PowerInteractor;
@@ -80,7 +79,7 @@
 
     private void setScreenState(int screenState) {
         mScreenState = screenState;
-        Trace.traceCounter(Trace.TRACE_TAG_APP, "screenState", screenState);
+        TrackTracer.instantForGroup("screen", "screenState", screenState);
     }
 
     public interface Observer {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java b/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java
index c0ffda6..c261cfe 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WakefulnessLifecycle.java
@@ -24,11 +24,11 @@
 import android.os.Bundle;
 import android.os.PowerManager;
 import android.os.RemoteException;
-import android.os.Trace;
 import android.util.DisplayMetrics;
 
 import androidx.annotation.Nullable;
 
+import com.android.app.tracing.coroutines.TrackTracer;
 import com.android.systemui.Dumpable;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dump.DumpManager;
@@ -197,7 +197,7 @@
 
     private void setWakefulness(@Wakefulness int wakefulness) {
         mWakefulness = wakefulness;
-        Trace.traceCounter(Trace.TRACE_TAG_APP, "wakefulness", wakefulness);
+        TrackTracer.instantForGroup("screen", "wakefulness", wakefulness);
     }
 
     private void updateLastWakeOriginLocation() {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
index 5b28a3f..a74384f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/WindowManagerLockscreenVisibilityManager.kt
@@ -156,6 +156,13 @@
         setWmLockscreenState(lockscreenShowing = lockscreenShown)
     }
 
+    /**
+     * Called when the keyguard going away remote animation is started, and we have a
+     * RemoteAnimationTarget to animate.
+     *
+     * This is triggered either by this class calling ATMS#keyguardGoingAway, or by WM directly,
+     * such as when an activity with FLAG_DISMISS_KEYGUARD is launched over a dismissible keyguard.
+     */
     fun onKeyguardGoingAwayRemoteAnimationStart(
         @WindowManager.TransitionOldType transit: Int,
         apps: Array<RemoteAnimationTarget>,
@@ -163,19 +170,32 @@
         nonApps: Array<RemoteAnimationTarget>,
         finishedCallback: IRemoteAnimationFinishedCallback,
     ) {
-        // Make sure this is true - we set it true when requesting keyguardGoingAway, but there are
-        // cases where WM starts this transition on its own.
-        isKeyguardGoingAway = true
+        // If we weren't expecting the keyguard to be going away, WM triggered this transition.
+        if (!isKeyguardGoingAway) {
+            // Since WM triggered this, we're likely not transitioning to GONE yet. See if we can
+            // start that transition.
+            val startedDismiss =
+                keyguardDismissTransitionInteractor.startDismissKeyguardTransition(
+                    reason = "Going away remote animation started"
+                )
 
-        // Ensure that we've started a dismiss keyguard transition. WindowManager can start the
-        // going away animation on its own, if an activity launches and then requests dismissing the
-        // keyguard. In this case, this is the first and only signal we'll receive to start
-        // 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.
-        keyguardDismissTransitionInteractor.startDismissKeyguardTransition(
-            reason = "Going away remote animation started"
-        )
+            if (!startedDismiss) {
+                // If the transition wasn't started, we're already GONE. This can happen with timing
+                // issues, where the remote animation took a long time to start, and something else
+                // caused us to unlock in the meantime. Since we're already GONE, simply end the
+                // remote animatiom immediately.
+                Log.d(
+                    TAG,
+                    "onKeyguardGoingAwayRemoteAnimationStart: " +
+                        "Dismiss transition was not started; we're already GONE. " +
+                        "Ending remote animation.",
+                )
+                finishedCallback.onAnimationFinished()
+                return
+            }
+
+            isKeyguardGoingAway = true
+        }
 
         if (apps.isNotEmpty()) {
             goingAwayRemoteAnimationFinishedCallback = finishedCallback
@@ -278,6 +298,6 @@
     }
 
     companion object {
-        private val TAG = WindowManagerLockscreenVisibilityManager::class.java.simpleName
+        private val TAG = "WindowManagerLsVis"
     }
 }
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 d95a126..a7a43249 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -60,7 +60,6 @@
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionBootInteractor;
 import com.android.systemui.keyguard.domain.interactor.StartKeyguardTransitionModule;
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransitionModule;
-import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransitionModule;
 import com.android.systemui.keyguard.ui.view.AlternateBouncerWindowViewBinder;
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardQuickAffordancesCombinedViewModelModule;
 import com.android.systemui.log.SessionTracker;
@@ -110,6 +109,7 @@
             DeviceEntryIconTransitionModule.class,
             FalsingModule.class,
             PrimaryBouncerTransitionModule.class,
+            PrimaryBouncerTransitionImplModule.class,
             KeyguardDataQuickAffordanceModule.class,
             KeyguardQuickAffordancesCombinedViewModelModule.class,
             KeyguardRepositoryModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/PrimaryBouncerTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/PrimaryBouncerTransitionModule.kt
new file mode 100644
index 0000000..cc070b6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/PrimaryBouncerTransitionModule.kt
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.dagger
+
+import android.content.res.Resources
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.keyguard.ui.transitions.BlurConfig
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition
+import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToPrimaryBouncerTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.AodToPrimaryBouncerTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.DozingToPrimaryBouncerTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.GlanceableHubToPrimaryBouncerTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.LockscreenToPrimaryBouncerTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.OccludedToPrimaryBouncerTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToAodTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToDozingTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGlanceableHubTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToLockscreenTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToOccludedTransitionViewModel
+import com.android.systemui.res.R
+import com.android.systemui.window.flag.WindowBlurFlag
+import dagger.Binds
+import dagger.Module
+import dagger.Provides
+import dagger.multibindings.IntoSet
+import dagger.multibindings.Multibinds
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+/**
+ * Base module that defines the [PrimaryBouncerTransition] multibinding. All variants of SystemUI
+ * can install this module to get the default empty version of the multibinding
+ */
+@Module
+interface PrimaryBouncerTransitionModule {
+    @Multibinds fun primaryBouncerTransitions(): Set<PrimaryBouncerTransition>
+
+    companion object {
+        @Provides
+        @SysUISingleton
+        fun provideBlurConfig(@Main resources: Resources): BlurConfig {
+            val minBlurRadius = resources.getDimensionPixelSize(R.dimen.min_window_blur_radius)
+            val maxBlurRadius =
+                if (WindowBlurFlag.isEnabled) {
+                    resources.getDimensionPixelSize(R.dimen.max_shade_window_blur_radius)
+                } else {
+                    resources.getDimensionPixelSize(R.dimen.max_window_blur_radius)
+                }
+            return BlurConfig(minBlurRadius.toFloat(), maxBlurRadius.toFloat())
+        }
+    }
+}
+
+/**
+ * Module that installs all the implementations of [PrimaryBouncerTransition] from different
+ * keyguard states to and away from the primary bouncer.
+ */
+@ExperimentalCoroutinesApi
+@Module
+interface PrimaryBouncerTransitionImplModule {
+    @Binds
+    @IntoSet
+    fun fromAod(impl: AodToPrimaryBouncerTransitionViewModel): PrimaryBouncerTransition
+
+    @Binds
+    @IntoSet
+    fun fromAlternateBouncer(
+        impl: AlternateBouncerToPrimaryBouncerTransitionViewModel
+    ): PrimaryBouncerTransition
+
+    @Binds
+    @IntoSet
+    fun fromDozing(impl: DozingToPrimaryBouncerTransitionViewModel): PrimaryBouncerTransition
+
+    @Binds
+    @IntoSet
+    fun fromLockscreen(
+        impl: LockscreenToPrimaryBouncerTransitionViewModel
+    ): PrimaryBouncerTransition
+
+    @Binds
+    @IntoSet
+    fun fromGlanceableHub(
+        impl: GlanceableHubToPrimaryBouncerTransitionViewModel
+    ): PrimaryBouncerTransition
+
+    @Binds
+    @IntoSet
+    fun fromOccluded(impl: OccludedToPrimaryBouncerTransitionViewModel): PrimaryBouncerTransition
+
+    @Binds
+    @IntoSet
+    fun toAod(impl: PrimaryBouncerToAodTransitionViewModel): PrimaryBouncerTransition
+
+    @Binds
+    @IntoSet
+    fun toLockscreen(impl: PrimaryBouncerToLockscreenTransitionViewModel): PrimaryBouncerTransition
+
+    @Binds
+    @IntoSet
+    fun toDozing(impl: PrimaryBouncerToDozingTransitionViewModel): PrimaryBouncerTransition
+
+    @Binds
+    @IntoSet
+    fun toGlanceableHub(
+        impl: PrimaryBouncerToGlanceableHubTransitionViewModel
+    ): PrimaryBouncerTransition
+
+    @Binds
+    @IntoSet
+    fun toGone(impl: PrimaryBouncerToGoneTransitionViewModel): PrimaryBouncerTransition
+
+    @Binds
+    @IntoSet
+    fun toOccluded(impl: PrimaryBouncerToOccludedTransitionViewModel): PrimaryBouncerTransition
+}
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 ac04dd5..a39982d 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
@@ -17,6 +17,7 @@
 package com.android.systemui.keyguard.data.repository
 
 import android.graphics.Point
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.internal.widget.LockPatternUtils
 import com.android.keyguard.KeyguardUpdateMonitor
 import com.android.keyguard.KeyguardUpdateMonitorCallback
@@ -64,7 +65,6 @@
 import kotlinx.coroutines.flow.mapLatest
 import kotlinx.coroutines.flow.onStart
 import kotlinx.coroutines.flow.stateIn
-import com.android.app.tracing.coroutines.launchTraced as launch
 
 /** Defines interface for classes that encapsulate application state for the keyguard. */
 interface KeyguardRepository {
@@ -248,13 +248,6 @@
     val keyguardDoneAnimationsFinished: Flow<Unit>
 
     /**
-     * Receive whether clock should be centered on lockscreen.
-     *
-     * @deprecated When scene container flag is on use clockShouldBeCentered from domain level.
-     */
-    val clockShouldBeCentered: Flow<Boolean>
-
-    /**
      * Whether the primary authentication is required for the given user due to lockdown or
      * encryption after reboot.
      */
@@ -306,8 +299,6 @@
 
     suspend fun setKeyguardDone(keyguardDoneType: KeyguardDone)
 
-    fun setClockShouldBeCentered(shouldBeCentered: Boolean)
-
     /**
      * Updates signal that the keyguard done animations are finished
      *
@@ -390,9 +381,6 @@
 
     override val panelAlpha: MutableStateFlow<Float> = MutableStateFlow(1f)
 
-    private val _clockShouldBeCentered = MutableStateFlow(true)
-    override val clockShouldBeCentered: Flow<Boolean> = _clockShouldBeCentered.asStateFlow()
-
     override val topClippingBounds = MutableStateFlow<Int?>(null)
 
     override val isKeyguardShowing: MutableStateFlow<Boolean> =
@@ -681,10 +669,6 @@
         _isQuickSettingsVisible.value = isVisible
     }
 
-    override fun setClockShouldBeCentered(shouldBeCentered: Boolean) {
-        _clockShouldBeCentered.value = shouldBeCentered
-    }
-
     override fun setKeyguardEnabled(enabled: Boolean) {
         _isKeyguardEnabled.value = enabled
     }
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 354fc3d..24f2493 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
@@ -212,8 +212,11 @@
                 Log.i(TAG, "Duplicate call to start the transition, rejecting: $info")
                 return@withContext null
             }
+            val isAnimatorRunning = lastAnimator?.isRunning() ?: false
+            val isManualTransitionRunning =
+                updateTransitionId != null && lastStep.transitionState != TransitionState.FINISHED
             val startingValue =
-                if (lastStep.transitionState != TransitionState.FINISHED) {
+                if (isAnimatorRunning || isManualTransitionRunning) {
                     Log.i(TAG, "Transition still active: $lastStep, canceling")
                     when (info.modeOnCanceled) {
                         TransitionModeOnCanceled.LAST_VALUE -> lastStep.value
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 9896365..b42da52 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
@@ -132,6 +132,8 @@
                             if (SceneContainerFlag.isEnabled) return@collect
                             startTransitionTo(
                                 toState = KeyguardState.GONE,
+                                modeOnCanceled = TransitionModeOnCanceled.REVERSE,
+                                ownerReason = "canWakeDirectlyToGone = true",
                             )
                         } else if (shouldTransitionToLockscreen) {
                             val modeOnCanceled =
@@ -146,7 +148,7 @@
                             startTransitionTo(
                                 toState = KeyguardState.LOCKSCREEN,
                                 modeOnCanceled = modeOnCanceled,
-                                ownerReason = "listen for aod to awake"
+                                ownerReason = "listen for aod to awake",
                             )
                         } else if (shouldTransitionToOccluded) {
                             startTransitionTo(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt
index f792935..ab5fdd6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardClockInteractor.kt
@@ -25,6 +25,10 @@
 import com.android.systemui.keyguard.shared.model.ClockSize
 import com.android.systemui.keyguard.shared.model.ClockSizeSetting
 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.media.controls.domain.pipeline.interactor.MediaCarouselInteractor
 import com.android.systemui.plugins.clocks.ClockController
 import com.android.systemui.plugins.clocks.ClockId
@@ -39,6 +43,7 @@
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.filterNotNull
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.stateIn
 
@@ -117,7 +122,43 @@
                 }
             }
         } else {
-            keyguardInteractor.clockShouldBeCentered
+            combine(
+                    shadeInteractor.isShadeLayoutWide,
+                    activeNotificationsInteractor.areAnyNotificationsPresent,
+                    keyguardInteractor.dozeTransitionModel,
+                    keyguardTransitionInteractor.startedKeyguardTransitionStep.map { it.to == AOD },
+                    keyguardTransitionInteractor.startedKeyguardTransitionStep.map {
+                        it.to == LOCKSCREEN
+                    },
+                    keyguardTransitionInteractor.startedKeyguardTransitionStep.map {
+                        it.to == DOZING
+                    },
+                    keyguardInteractor.isPulsing,
+                    keyguardTransitionInteractor.startedKeyguardTransitionStep.map { it.to == GONE },
+                ) {
+                    isShadeLayoutWide,
+                    areAnyNotificationsPresent,
+                    dozeTransitionModel,
+                    startedToAod,
+                    startedToLockScreen,
+                    startedToDoze,
+                    isPulsing,
+                    startedToGone ->
+                    when {
+                        !isShadeLayoutWide -> true
+                        // [areAnyNotificationsPresent] also reacts to notification stack in
+                        // homescreen
+                        // it may cause unnecessary `false` emission when there's notification in
+                        // homescreen
+                        // but none in lockscreen when going from GONE to AOD / DOZING
+                        // use null to skip emitting wrong value
+                        startedToGone || startedToDoze -> null
+                        startedToLockScreen -> !areAnyNotificationsPresent
+                        startedToAod -> !isPulsing
+                        else -> true
+                    }
+                }
+                .filterNotNull()
         }
 
     fun setClockSize(size: ClockSize) {
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
index 4793d95..089e5dc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissTransitionInteractor.kt
@@ -48,9 +48,12 @@
      *
      * This is called exclusively by sources that can authoritatively say we should be unlocked,
      * including KeyguardSecurityContainerController and WindowManager.
+     *
+     * Returns [false] if the transition was not started, because we're already GONE or we don't
+     * know how to dismiss keyguard from the current state.
      */
-    fun startDismissKeyguardTransition(reason: String = "") {
-        if (SceneContainerFlag.isEnabled) return
+    fun startDismissKeyguardTransition(reason: String = ""): Boolean {
+        if (SceneContainerFlag.isEnabled) return false
         Log.d(TAG, "#startDismissKeyguardTransition(reason=$reason)")
         val startedState =
             if (transitionRaceCondition()) {
@@ -65,13 +68,20 @@
             AOD -> fromAodTransitionInteractor.dismissAod()
             DOZING -> fromDozingTransitionInteractor.dismissFromDozing()
             KeyguardState.OCCLUDED -> fromOccludedTransitionInteractor.dismissFromOccluded()
-            KeyguardState.GONE ->
+            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.")
+                return false
+            }
+            else -> {
+                Log.e(TAG, "We don't know how to dismiss keyguard from state $startedState.")
+                return false
+            }
         }
+
+        return true
     }
 
     companion object {
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 0193d7cb..8f7f2a0 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
@@ -44,7 +44,6 @@
 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
 import com.android.systemui.scene.domain.interactor.SceneInteractor
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
@@ -84,7 +83,6 @@
 @Inject
 constructor(
     private val repository: KeyguardRepository,
-    powerInteractor: PowerInteractor,
     bouncerRepository: KeyguardBouncerRepository,
     @ShadeDisplayAware configurationInteractor: ConfigurationInteractor,
     shadeRepository: ShadeRepository,
@@ -216,11 +214,7 @@
                     // should actually be quite strange to leave AOD and then go straight to
                     // DREAMING so this should be fine.
                     delay(IS_ABLE_TO_DREAM_DELAY_MS)
-                    isDreaming
-                        .sample(powerInteractor.isAwake) { isDreaming, isAwake ->
-                            isDreaming && isAwake
-                        }
-                        .debounce(50L)
+                    isDreaming.debounce(50L)
                 } else {
                     flowOf(false)
                 }
@@ -418,8 +412,6 @@
                 initialValue = 0f,
             )
 
-    val clockShouldBeCentered: Flow<Boolean> = repository.clockShouldBeCentered
-
     /** Whether to animate the next doze mode transition. */
     val animateDozingTransitions: Flow<Boolean> by lazy {
         if (SceneContainerFlag.isEnabled) {
@@ -485,10 +477,6 @@
         repository.setAnimateDozingTransitions(animate)
     }
 
-    fun setClockShouldBeCentered(shouldBeCentered: Boolean) {
-        repository.setClockShouldBeCentered(shouldBeCentered)
-    }
-
     fun setLastRootViewTapPosition(point: Point?) {
         repository.lastRootViewTapPosition.value = point
     }
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 a133f06..3bdc32d 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
@@ -116,9 +116,10 @@
      * - We're wake and unlocking (fingerprint auth occurred while asleep).
      * - We're allowed to ignore auth and return to GONE, due to timeouts not elapsing.
      * - We're DREAMING and dismissible.
-     * - We're already GONE. Technically you're already awake when GONE, but this makes it easier to
-     *   reason about this state (for example, if canWakeDirectlyToGone, don't tell WM to pause the
-     *   top activity - something you should never do while GONE as well).
+     * - We're already GONE and not transitioning out of GONE. Technically you're already awake when
+     *   GONE, but this makes it easier to reason about this state (for example, if
+     *   canWakeDirectlyToGone, don't tell WM to pause the top activity - something you should never
+     *   do while GONE as well).
      */
     val canWakeDirectlyToGone =
         combine(
@@ -138,7 +139,8 @@
                     canIgnoreAuthAndReturnToGone ||
                     (currentState == KeyguardState.DREAMING &&
                         keyguardInteractor.isKeyguardDismissible.value) ||
-                    currentState == KeyguardState.GONE
+                    (currentState == KeyguardState.GONE &&
+                        transitionInteractor.getStartedState() == KeyguardState.GONE)
             }
             .distinctUntilChanged()
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/BlurConfig.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/BlurConfig.kt
new file mode 100644
index 0000000..3eb8522
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/BlurConfig.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.transitions
+
+import javax.inject.Inject
+
+/** Config that provides the max and min blur radius for the window blurs. */
+data class BlurConfig(val minBlurRadiusPx: Float, val maxBlurRadiusPx: Float) {
+    // No-op config that will be used by dagger of other SysUI variants which don't blur the
+    // background surface.
+    @Inject constructor() : this(0.0f, 0.0f)
+
+    companion object {
+        // Blur the shade much lesser than the background surface so that the surface is
+        // distinguishable from the background.
+        @JvmStatic fun Float.maxBlurRadiusToNotificationPanelBlurRadius(): Float = this / 3.0f
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/PrimaryBouncerTransition.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/PrimaryBouncerTransition.kt
index 4a0817b..eb1afb4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/PrimaryBouncerTransition.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/transitions/PrimaryBouncerTransition.kt
@@ -16,133 +16,26 @@
 
 package com.android.systemui.keyguard.ui.transitions
 
-import android.content.res.Resources
-import android.util.MathUtils
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToPrimaryBouncerTransitionViewModel
-import com.android.systemui.keyguard.ui.viewmodel.AodToPrimaryBouncerTransitionViewModel
-import com.android.systemui.keyguard.ui.viewmodel.DozingToPrimaryBouncerTransitionViewModel
-import com.android.systemui.keyguard.ui.viewmodel.GlanceableHubToPrimaryBouncerTransitionViewModel
-import com.android.systemui.keyguard.ui.viewmodel.LockscreenToPrimaryBouncerTransitionViewModel
-import com.android.systemui.keyguard.ui.viewmodel.OccludedToPrimaryBouncerTransitionViewModel
-import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToAodTransitionViewModel
-import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToDozingTransitionViewModel
-import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGlanceableHubTransitionViewModel
-import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel
-import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToLockscreenTransitionViewModel
-import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToOccludedTransitionViewModel
-import com.android.systemui.res.R
-import com.android.systemui.window.flag.WindowBlurFlag
-import dagger.Binds
-import dagger.Module
-import dagger.Provides
-import dagger.multibindings.IntoSet
-import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
+import android.util.MathUtils.lerp
 import kotlinx.coroutines.flow.Flow
 
 /**
  * Each PrimaryBouncerTransition is responsible for updating various UI states based on the nature
  * of the transition.
  *
- * MUST list implementing classes in dagger module [PrimaryBouncerTransitionModule].
+ * MUST list implementing classes in dagger module
+ * [com.android.systemui.keyguard.dagger.PrimaryBouncerTransitionImplModule].
  */
 interface PrimaryBouncerTransition {
     /** Radius of blur applied to the window's root view. */
     val windowBlurRadius: Flow<Float>
 
+    /** Radius of blur applied to the notifications on expanded shade */
+    val notificationBlurRadius: Flow<Float>
+
     fun transitionProgressToBlurRadius(
         starBlurRadius: Float,
         endBlurRadius: Float,
         transitionProgress: Float,
-    ): Float {
-        return MathUtils.lerp(starBlurRadius, endBlurRadius, transitionProgress)
-    }
-}
-
-/**
- * Module that installs all the transitions from different keyguard states to and away from the
- * primary bouncer.
- */
-@ExperimentalCoroutinesApi
-@Module
-interface PrimaryBouncerTransitionModule {
-    @Binds
-    @IntoSet
-    fun fromAod(impl: AodToPrimaryBouncerTransitionViewModel): PrimaryBouncerTransition
-
-    @Binds
-    @IntoSet
-    fun fromAlternateBouncer(
-        impl: AlternateBouncerToPrimaryBouncerTransitionViewModel
-    ): PrimaryBouncerTransition
-
-    @Binds
-    @IntoSet
-    fun fromDozing(impl: DozingToPrimaryBouncerTransitionViewModel): PrimaryBouncerTransition
-
-    @Binds
-    @IntoSet
-    fun fromLockscreen(
-        impl: LockscreenToPrimaryBouncerTransitionViewModel
-    ): PrimaryBouncerTransition
-
-    @Binds
-    @IntoSet
-    fun fromGlanceableHub(
-        impl: GlanceableHubToPrimaryBouncerTransitionViewModel
-    ): PrimaryBouncerTransition
-
-    @Binds
-    @IntoSet
-    fun fromOccluded(impl: OccludedToPrimaryBouncerTransitionViewModel): PrimaryBouncerTransition
-
-    @Binds
-    @IntoSet
-    fun toAod(impl: PrimaryBouncerToAodTransitionViewModel): PrimaryBouncerTransition
-
-    @Binds
-    @IntoSet
-    fun toLockscreen(impl: PrimaryBouncerToLockscreenTransitionViewModel): PrimaryBouncerTransition
-
-    @Binds
-    @IntoSet
-    fun toDozing(impl: PrimaryBouncerToDozingTransitionViewModel): PrimaryBouncerTransition
-
-    @Binds
-    @IntoSet
-    fun toGlanceableHub(
-        impl: PrimaryBouncerToGlanceableHubTransitionViewModel
-    ): PrimaryBouncerTransition
-
-    @Binds
-    @IntoSet
-    fun toGone(impl: PrimaryBouncerToGoneTransitionViewModel): PrimaryBouncerTransition
-
-    @Binds
-    @IntoSet
-    fun toOccluded(impl: PrimaryBouncerToOccludedTransitionViewModel): PrimaryBouncerTransition
-
-    companion object {
-        @Provides
-        @SysUISingleton
-        fun provideBlurConfig(@Main resources: Resources): BlurConfig {
-            val minBlurRadius = resources.getDimensionPixelSize(R.dimen.min_window_blur_radius)
-            val maxBlurRadius =
-                if (WindowBlurFlag.isEnabled) {
-                    resources.getDimensionPixelSize(R.dimen.max_shade_window_blur_radius)
-                } else {
-                    resources.getDimensionPixelSize(R.dimen.max_window_blur_radius)
-                }
-            return BlurConfig(minBlurRadius.toFloat(), maxBlurRadius.toFloat())
-        }
-    }
-}
-
-/** Config that provides the max and min blur radius for the window blurs. */
-data class BlurConfig(val minBlurRadiusPx: Float, val maxBlurRadiusPx: Float) {
-    // No-op config that will be used by dagger of other SysUI variants which don't blur the
-    // background surface.
-    @Inject constructor() : this(0.0f, 0.0f)
+    ): Float = lerp(starBlurRadius, endBlurRadius, transitionProgress)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModel.kt
index f174557..92bb5e6 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerToPrimaryBouncerTransitionViewModel.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
+import com.android.systemui.Flags
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.FromAlternateBouncerTransitionInteractor
 import com.android.systemui.keyguard.shared.model.Edge
@@ -23,6 +24,7 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.BlurConfig
+import com.android.systemui.keyguard.ui.transitions.BlurConfig.Companion.maxBlurRadiusToNotificationPanelBlurRadius
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
 import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
@@ -73,7 +75,28 @@
 
     val lockscreenAlpha: Flow<Float> = if (WindowBlurFlag.isEnabled) alphaFlow else emptyFlow()
 
-    val notificationAlpha: Flow<Float> = alphaFlow
+    val notificationAlpha: Flow<Float> =
+        if (Flags.bouncerUiRevamp()) {
+            shadeDependentFlows.transitionFlow(
+                flowWhenShadeIsNotExpanded = lockscreenAlpha,
+                flowWhenShadeIsExpanded = transitionAnimation.immediatelyTransitionTo(1f),
+            )
+        } else {
+            alphaFlow
+        }
+
+    override val notificationBlurRadius: Flow<Float> =
+        if (Flags.bouncerUiRevamp()) {
+            shadeDependentFlows.transitionFlow(
+                flowWhenShadeIsNotExpanded = emptyFlow(),
+                flowWhenShadeIsExpanded =
+                    transitionAnimation.immediatelyTransitionTo(
+                        blurConfig.maxBlurRadiusPx.maxBlurRadiusToNotificationPanelBlurRadius()
+                    ),
+            )
+        } else {
+            emptyFlow<Float>()
+        }
 
     override val deviceEntryParentViewAlpha: Flow<Float> =
         transitionAnimation.immediatelyTransitionTo(0f)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt
index 2497def..d981eeb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToOccludedTransitionViewModel.kt
@@ -17,6 +17,7 @@
 package com.android.systemui.keyguard.ui.viewmodel
 
 import android.util.MathUtils
+import com.android.systemui.Flags.lightRevealMigration
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.FromAodTransitionInteractor
 import com.android.systemui.keyguard.shared.model.Edge
@@ -32,9 +33,7 @@
 @SysUISingleton
 class AodToOccludedTransitionViewModel
 @Inject
-constructor(
-    animationFlow: KeyguardTransitionAnimationFlow,
-) : DeviceEntryIconTransition {
+constructor(animationFlow: KeyguardTransitionAnimationFlow) : DeviceEntryIconTransition {
     private val transitionAnimation =
         animationFlow.setup(
             duration = FromAodTransitionInteractor.TO_OCCLUDED_DURATION,
@@ -52,10 +51,20 @@
         var currentAlpha = 0f
         return transitionAnimation.sharedFlow(
             duration = 250.milliseconds,
-            startTime = 100.milliseconds, // Wait for the light reveal to "hit" the LS elements.
-            onStart = { currentAlpha = viewState.alpha() },
+            startTime =
+                if (lightRevealMigration()) {
+                    100.milliseconds // Wait for the light reveal to "hit" the LS elements.
+                } else {
+                    0.milliseconds
+                },
+            onStart = {
+                if (lightRevealMigration()) {
+                    currentAlpha = viewState.alpha()
+                } else {
+                    currentAlpha = 0f
+                }
+            },
             onStep = { MathUtils.lerp(currentAlpha, 0f, it) },
-            onCancel = { 0f },
         )
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerTransitionViewModel.kt
index dbb6a49..e3b5587 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToPrimaryBouncerTransitionViewModel.kt
@@ -53,4 +53,7 @@
 
     override val windowBlurRadius: Flow<Float> =
         transitionAnimation.immediatelyTransitionTo(blurConfig.maxBlurRadiusPx)
+
+    override val notificationBlurRadius: Flow<Float> =
+        transitionAnimation.immediatelyTransitionTo(0.0f)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModel.kt
index d8b617a..c937d5c 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DozingToPrimaryBouncerTransitionViewModel.kt
@@ -64,4 +64,6 @@
             },
             onFinish = { blurConfig.maxBlurRadiusPx },
         )
+    override val notificationBlurRadius: Flow<Float> =
+        transitionAnimation.immediatelyTransitionTo(0f)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToPrimaryBouncerTransitionViewModel.kt
index 597df15..5ab4583 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToPrimaryBouncerTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GlanceableHubToPrimaryBouncerTransitionViewModel.kt
@@ -42,4 +42,7 @@
 
     override val windowBlurRadius: Flow<Float> =
         transitionAnimation.immediatelyTransitionTo(blurConfig.maxBlurRadiusPx)
+
+    override val notificationBlurRadius: Flow<Float> =
+        transitionAnimation.immediatelyTransitionTo(0.0f)
 }
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 c373fd0..44c4c87 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
@@ -16,6 +16,7 @@
 
 package com.android.systemui.keyguard.ui.viewmodel
 
+import com.android.systemui.Flags
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.keyguard.domain.interactor.FromLockscreenTransitionInteractor
 import com.android.systemui.keyguard.shared.model.Edge
@@ -23,6 +24,7 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
 import com.android.systemui.keyguard.ui.transitions.BlurConfig
+import com.android.systemui.keyguard.ui.transitions.BlurConfig.Companion.maxBlurRadiusToNotificationPanelBlurRadius
 import com.android.systemui.keyguard.ui.transitions.DeviceEntryIconTransition
 import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
@@ -32,6 +34,7 @@
 import kotlin.time.Duration.Companion.milliseconds
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.emptyFlow
 
 /**
  * Breaks down LOCKSCREEN->PRIMARY BOUNCER transition into discrete steps for corresponding views to
@@ -70,6 +73,29 @@
 
     val lockscreenAlpha: Flow<Float> = shortcutsAlpha
 
+    val notificationAlpha: Flow<Float> =
+        if (Flags.bouncerUiRevamp()) {
+            shadeDependentFlows.transitionFlow(
+                flowWhenShadeIsNotExpanded = lockscreenAlpha,
+                flowWhenShadeIsExpanded = transitionAnimation.immediatelyTransitionTo(1f),
+            )
+        } else {
+            lockscreenAlpha
+        }
+
+    override val notificationBlurRadius: Flow<Float> =
+        if (Flags.bouncerUiRevamp()) {
+            shadeDependentFlows.transitionFlow(
+                flowWhenShadeIsNotExpanded = emptyFlow(),
+                flowWhenShadeIsExpanded =
+                    transitionAnimation.immediatelyTransitionTo(
+                        blurConfig.maxBlurRadiusPx.maxBlurRadiusToNotificationPanelBlurRadius()
+                    ),
+            )
+        } else {
+            emptyFlow()
+        }
+
     override val deviceEntryParentViewAlpha: Flow<Float> =
         shadeDependentFlows.transitionFlow(
             flowWhenShadeIsNotExpanded =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToPrimaryBouncerTransitionViewModel.kt
index 4459810..4d3e272 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToPrimaryBouncerTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToPrimaryBouncerTransitionViewModel.kt
@@ -42,4 +42,7 @@
 
     override val windowBlurRadius: Flow<Float> =
         transitionAnimation.immediatelyTransitionTo(blurConfig.maxBlurRadiusPx)
+
+    override val notificationBlurRadius: Flow<Float> =
+        transitionAnimation.immediatelyTransitionTo(0.0f)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModel.kt
index fab8008..224191b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToAodTransitionViewModel.kt
@@ -91,4 +91,7 @@
             },
             onFinish = { blurConfig.minBlurRadiusPx },
         )
+
+    override val notificationBlurRadius: Flow<Float> =
+        transitionAnimation.immediatelyTransitionTo(0.0f)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToDozingTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToDozingTransitionViewModel.kt
index eebdf2e..0f8495f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToDozingTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToDozingTransitionViewModel.kt
@@ -80,4 +80,6 @@
             },
             onFinish = { blurConfig.minBlurRadiusPx },
         )
+    override val notificationBlurRadius: Flow<Float> =
+        transitionAnimation.immediatelyTransitionTo(0.0f)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGlanceableHubTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGlanceableHubTransitionViewModel.kt
index 3636b74..a13eef2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGlanceableHubTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGlanceableHubTransitionViewModel.kt
@@ -43,4 +43,7 @@
 
     override val windowBlurRadius: Flow<Float> =
         transitionAnimation.immediatelyTransitionTo(blurConfig.minBlurRadiusPx)
+
+    override val notificationBlurRadius: Flow<Float> =
+        transitionAnimation.immediatelyTransitionTo(0.0f)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt
index 4ed3e6c..d1233f2 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModel.kt
@@ -166,6 +166,9 @@
             createBouncerWindowBlurFlow(primaryBouncerInteractor::willRunDismissFromKeyguard)
         }
 
+    override val notificationBlurRadius: Flow<Float> =
+        transitionAnimation.immediatelyTransitionTo(0.0f)
+
     val scrimAlpha: Flow<ScrimAlpha> =
         bouncerToGoneFlows.scrimAlpha(TO_GONE_DURATION, PRIMARY_BOUNCER)
 }
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 2edc93cb..c53a408 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
@@ -91,4 +91,7 @@
                     },
                 ),
         )
+
+    override val notificationBlurRadius: Flow<Float> =
+        transitionAnimation.immediatelyTransitionTo(0.0f)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToOccludedTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToOccludedTransitionViewModel.kt
index 3a54a26..fe1708e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToOccludedTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToOccludedTransitionViewModel.kt
@@ -42,4 +42,7 @@
 
     override val windowBlurRadius: Flow<Float> =
         transitionAnimation.immediatelyTransitionTo(blurConfig.minBlurRadiusPx)
+
+    override val notificationBlurRadius: Flow<Float> =
+        transitionAnimation.immediatelyTransitionTo(0.0f)
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/Media3ActionFactory.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/Media3ActionFactory.kt
index 0954482..a6b9442 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/Media3ActionFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/Media3ActionFactory.kt
@@ -31,6 +31,7 @@
 import androidx.media3.session.MediaController as Media3Controller
 import androidx.media3.session.SessionCommand
 import androidx.media3.session.SessionToken
+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
@@ -128,7 +129,11 @@
                     drawable,
                     null, // no action to perform when clicked
                     context.getString(R.string.controls_media_button_connecting),
-                    context.getDrawable(R.drawable.ic_media_connecting_container),
+                    if (Flags.mediaControlsUiUpdate()) {
+                        context.getDrawable(R.drawable.ic_media_connecting_status_container)
+                    } else {
+                        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,
                 )
@@ -230,17 +235,33 @@
                 Player.COMMAND_PLAY_PAUSE -> {
                     if (!controller.isPlaying) {
                         MediaAction(
-                            context.getDrawable(R.drawable.ic_media_play),
+                            if (Flags.mediaControlsUiUpdate()) {
+                                context.getDrawable(R.drawable.ic_media_play_button)
+                            } else {
+                                context.getDrawable(R.drawable.ic_media_play)
+                            },
                             { executeAction(packageName, token, Player.COMMAND_PLAY_PAUSE) },
                             context.getString(R.string.controls_media_button_play),
-                            context.getDrawable(R.drawable.ic_media_play_container),
+                            if (Flags.mediaControlsUiUpdate()) {
+                                context.getDrawable(R.drawable.ic_media_play_button_container)
+                            } else {
+                                context.getDrawable(R.drawable.ic_media_play_container)
+                            },
                         )
                     } else {
                         MediaAction(
-                            context.getDrawable(R.drawable.ic_media_pause),
+                            if (Flags.mediaControlsUiUpdate()) {
+                                context.getDrawable(R.drawable.ic_media_pause_button)
+                            } else {
+                                context.getDrawable(R.drawable.ic_media_pause)
+                            },
                             { executeAction(packageName, token, Player.COMMAND_PLAY_PAUSE) },
                             context.getString(R.string.controls_media_button_pause),
-                            context.getDrawable(R.drawable.ic_media_pause_container),
+                            if (Flags.mediaControlsUiUpdate()) {
+                                context.getDrawable(R.drawable.ic_media_pause_button_container)
+                            } else {
+                                context.getDrawable(R.drawable.ic_media_pause_container)
+                            },
                         )
                     }
                 }
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
index 4f97913..9bf556c 100644
--- 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
@@ -29,6 +29,7 @@
 import android.service.notification.StatusBarNotification
 import android.util.Log
 import androidx.media.utils.MediaConstants
+import com.android.systemui.Flags
 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
@@ -69,7 +70,11 @@
                 drawable,
                 null, // no action to perform when clicked
                 context.getString(R.string.controls_media_button_connecting),
-                context.getDrawable(R.drawable.ic_media_connecting_container),
+                if (Flags.mediaControlsUiUpdate()) {
+                    context.getDrawable(R.drawable.ic_media_connecting_status_container)
+                } else {
+                    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,
             )
@@ -157,18 +162,34 @@
     return when (action) {
         PlaybackState.ACTION_PLAY -> {
             MediaAction(
-                context.getDrawable(R.drawable.ic_media_play),
+                if (Flags.mediaControlsUiUpdate()) {
+                    context.getDrawable(R.drawable.ic_media_play_button)
+                } else {
+                    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),
+                if (Flags.mediaControlsUiUpdate()) {
+                    context.getDrawable(R.drawable.ic_media_play_button_container)
+                } else {
+                    context.getDrawable(R.drawable.ic_media_play_container)
+                },
             )
         }
         PlaybackState.ACTION_PAUSE -> {
             MediaAction(
-                context.getDrawable(R.drawable.ic_media_pause),
+                if (Flags.mediaControlsUiUpdate()) {
+                    context.getDrawable(R.drawable.ic_media_pause_button)
+                } else {
+                    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),
+                if (Flags.mediaControlsUiUpdate()) {
+                    context.getDrawable(R.drawable.ic_media_pause_button_container)
+                } else {
+                    context.getDrawable(R.drawable.ic_media_pause_container)
+                },
             )
         }
         PlaybackState.ACTION_SKIP_TO_PREVIOUS -> {
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 3928a71..a2ddc20 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
@@ -1016,9 +1016,24 @@
                 expandedLayout.load(context, R.xml.media_recommendations_expanded)
             }
         }
+        readjustPlayPauseWidth()
         refreshState()
     }
 
+    private fun readjustPlayPauseWidth() {
+        // TODO: move to xml file when flag is removed.
+        if (Flags.mediaControlsUiUpdate()) {
+            collapsedLayout.constrainWidth(
+                R.id.actionPlayPause,
+                context.resources.getDimensionPixelSize(R.dimen.qs_media_action_play_pause_width),
+            )
+            expandedLayout.constrainWidth(
+                R.id.actionPlayPause,
+                context.resources.getDimensionPixelSize(R.dimen.qs_media_action_play_pause_width),
+            )
+        }
+    }
+
     /** Get a view state based on the width and height set by the scene */
     private fun obtainSceneContainerViewState(state: MediaHostState?): TransitionViewState? {
         logger.logMediaSize("scene container", widthInSceneContainerPx, heightInSceneContainerPx)
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
index af37eea..1a2238c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
@@ -234,8 +234,6 @@
                     (ViewGroup.MarginLayoutParams) mItemLayout.getLayoutParams();
             params.rightMargin = showEndTouchArea ? mController.getItemMarginEndSelectable()
                     : mController.getItemMarginEndDefault();
-            mTitleIcon.setBackgroundTintList(
-                    ColorStateList.valueOf(mController.getColorItemContent()));
         }
 
         void setTwoLineLayout(MediaDevice device, boolean bFocused, boolean showSeekBar,
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaSwitchingController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaSwitchingController.java
index 1dbb317..15afd22 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaSwitchingController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaSwitchingController.java
@@ -37,9 +37,6 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.graphics.Bitmap;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffColorFilter;
-import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
 import android.media.AudioManager;
@@ -535,17 +532,9 @@
             // Use default Bluetooth device icon to handle getIcon() is null case.
             drawable = mContext.getDrawable(com.android.internal.R.drawable.ic_bt_headphones_a2dp);
         }
-        if (!(drawable instanceof BitmapDrawable)) {
-            setColorFilter(drawable, isActiveItem(device));
-        }
         return BluetoothUtils.createIconWithDrawable(drawable);
     }
 
-    void setColorFilter(Drawable drawable, boolean isActive) {
-        drawable.setColorFilter(new PorterDuffColorFilter(mColorItemContent,
-                PorterDuff.Mode.SRC_IN));
-    }
-
     boolean isActiveItem(MediaDevice device) {
         boolean isConnected = mLocalMediaManager.getCurrentConnectedDevice().getId().equals(
                 device.getId());
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionDialogDelegate.kt
index e17255a..b6395aa 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionDialogDelegate.kt
@@ -16,24 +16,16 @@
 package com.android.systemui.mediaprojection.permission
 
 import android.app.AlertDialog
-import android.content.Context
 import android.os.Bundle
 import android.view.Gravity
-import android.view.LayoutInflater
 import android.view.View
-import android.view.ViewGroup
-import android.view.ViewStub
 import android.view.WindowManager
-import android.view.accessibility.AccessibilityNodeInfo
-import android.widget.AdapterView
-import android.widget.ArrayAdapter
 import android.widget.ImageView
 import android.widget.Spinner
 import android.widget.TextView
 import androidx.annotation.CallSuper
 import androidx.annotation.ColorRes
 import androidx.annotation.DrawableRes
-import androidx.annotation.LayoutRes
 import androidx.annotation.StringRes
 import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
 import com.android.systemui.res.R
@@ -48,7 +40,7 @@
     @DrawableRes private val dialogIconDrawable: Int? = null,
     @ColorRes private val dialogIconTint: Int? = null,
     @ScreenShareMode val defaultSelectedMode: Int = screenShareOptions.first().mode,
-) : DialogDelegate<T>, AdapterView.OnItemSelectedListener {
+) : DialogDelegate<T> {
     private lateinit var dialogTitle: TextView
     private lateinit var cancelButton: TextView
     private lateinit var screenShareModeSpinner: Spinner
@@ -66,7 +58,6 @@
             hostUid,
             mediaProjectionMetricsLogger,
             defaultSelectedMode,
-            dialog,
         )
     }
 
@@ -84,12 +75,10 @@
         dialogTitle = dialog.requireViewById(R.id.screen_share_dialog_title)
         cancelButton = dialog.requireViewById(android.R.id.button2)
         updateIcon()
-        createOptionsView(getOptionsViewLayoutId())
         if (!::viewBinder.isInitialized) {
             viewBinder = createViewBinder()
         }
-        viewBinder.bind()
-        initScreenShareSpinner()
+        viewBinder.bind(dialog.requireViewById(R.id.screen_share_permission_dialog))
     }
 
     private fun updateIcon() {
@@ -102,34 +91,6 @@
         }
     }
 
-    private fun initScreenShareSpinner() {
-        val adapter = OptionsAdapter(dialog.context.applicationContext, screenShareOptions)
-        screenShareModeSpinner = dialog.requireViewById(R.id.screen_share_mode_options)
-        screenShareModeSpinner.adapter = adapter
-        screenShareModeSpinner.onItemSelectedListener = this
-
-        // disable redundant Touch & Hold accessibility action for Switch Access
-        screenShareModeSpinner.accessibilityDelegate =
-            object : View.AccessibilityDelegate() {
-                override fun onInitializeAccessibilityNodeInfo(
-                    host: View,
-                    info: AccessibilityNodeInfo,
-                ) {
-                    info.removeAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK)
-                    super.onInitializeAccessibilityNodeInfo(host, info)
-                }
-            }
-        screenShareModeSpinner.isLongClickable = false
-        val defaultModePosition = screenShareOptions.indexOfFirst { it.mode == defaultSelectedMode }
-        screenShareModeSpinner.setSelection(defaultModePosition, /* animate= */ false)
-    }
-
-    override fun onItemSelected(adapterView: AdapterView<*>?, view: View, pos: Int, id: Long) {
-        viewBinder.onItemSelected(pos)
-    }
-
-    override fun onNothingSelected(parent: AdapterView<*>?) {}
-
     fun getSelectedScreenShareOption(): ScreenShareOption {
         return viewBinder.selectedScreenShareOption
     }
@@ -147,44 +108,4 @@
     protected fun setCancelButtonOnClickListener(listener: View.OnClickListener?) {
         cancelButton.setOnClickListener(listener)
     }
-
-    // Create additional options that is shown under the share mode spinner
-    // Eg. the audio and tap toggles in SysUI Recorder
-    @LayoutRes protected open fun getOptionsViewLayoutId(): Int? = null
-
-    private fun createOptionsView(@LayoutRes layoutId: Int?) {
-        if (layoutId == null) return
-        val stub = dialog.requireViewById<View>(R.id.options_stub) as ViewStub
-        stub.layoutResource = layoutId
-        stub.inflate()
-    }
-}
-
-private class OptionsAdapter(context: Context, private val options: List<ScreenShareOption>) :
-    ArrayAdapter<String>(
-        context,
-        R.layout.screen_share_dialog_spinner_text,
-        options.map { context.getString(it.spinnerText, it.displayName) },
-    ) {
-
-    override fun isEnabled(position: Int): Boolean {
-        return options[position].spinnerDisabledText == null
-    }
-
-    override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View {
-        val inflater = LayoutInflater.from(parent.context)
-        val view = inflater.inflate(R.layout.screen_share_dialog_spinner_item_text, parent, false)
-        val titleTextView = view.requireViewById<TextView>(android.R.id.text1)
-        val errorTextView = view.requireViewById<TextView>(android.R.id.text2)
-        titleTextView.text = getItem(position)
-        errorTextView.text = options[position].spinnerDisabledText
-        if (isEnabled(position)) {
-            errorTextView.visibility = View.GONE
-            titleTextView.isEnabled = true
-        } else {
-            errorTextView.visibility = View.VISIBLE
-            titleTextView.isEnabled = false
-        }
-        return view
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionViewBinder.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionViewBinder.kt
index 728255d..c6e4db7 100644
--- a/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/permission/BaseMediaProjectionPermissionViewBinder.kt
@@ -16,9 +16,17 @@
 
 package com.android.systemui.mediaprojection.permission
 
-import android.app.AlertDialog
+import android.content.Context
+import android.view.LayoutInflater
 import android.view.View
+import android.view.ViewGroup
+import android.view.ViewStub
+import android.view.accessibility.AccessibilityNodeInfo
+import android.widget.AdapterView
+import android.widget.ArrayAdapter
+import android.widget.Spinner
 import android.widget.TextView
+import androidx.annotation.LayoutRes
 import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
 import com.android.systemui.res.R
 
@@ -28,10 +36,11 @@
     private val hostUid: Int,
     private val mediaProjectionMetricsLogger: MediaProjectionMetricsLogger,
     @ScreenShareMode val defaultSelectedMode: Int = screenShareOptions.first().mode,
-    private val dialog: AlertDialog,
-) {
+) : AdapterView.OnItemSelectedListener {
+    protected lateinit var containerView: View
     private lateinit var warning: TextView
     private lateinit var startButton: TextView
+    private lateinit var screenShareModeSpinner: Spinner
     var selectedScreenShareOption: ScreenShareOption =
         screenShareOptions.first { it.mode == defaultSelectedMode }
     private var shouldLogCancel: Boolean = true
@@ -44,33 +53,60 @@
         }
     }
 
-    open fun bind() {
-        warning = dialog.requireViewById(R.id.text_warning)
-        startButton = dialog.requireViewById(android.R.id.button1)
+    open fun bind(view: View) {
+        containerView = view
+        warning = containerView.requireViewById(R.id.text_warning)
+        startButton = containerView.requireViewById(android.R.id.button1)
         initScreenShareOptions()
+        createOptionsView(getOptionsViewLayoutId())
     }
 
     private fun initScreenShareOptions() {
         selectedScreenShareOption = screenShareOptions.first { it.mode == defaultSelectedMode }
         setOptionSpecificFields()
+        initScreenShareSpinner()
     }
 
-    /** Sets fields on the dialog that change based on which option is selected. */
+    /** Sets fields on the views that change based on which option is selected. */
     private fun setOptionSpecificFields() {
         warning.text = warningText
         startButton.text = startButtonText
     }
 
-    open fun onItemSelected(pos: Int) {
+    private fun initScreenShareSpinner() {
+        val adapter = OptionsAdapter(containerView.context.applicationContext, screenShareOptions)
+        screenShareModeSpinner = containerView.requireViewById(R.id.screen_share_mode_options)
+        screenShareModeSpinner.adapter = adapter
+        screenShareModeSpinner.onItemSelectedListener = this
+
+        // disable redundant Touch & Hold accessibility action for Switch Access
+        screenShareModeSpinner.accessibilityDelegate =
+            object : View.AccessibilityDelegate() {
+                override fun onInitializeAccessibilityNodeInfo(
+                    host: View,
+                    info: AccessibilityNodeInfo,
+                ) {
+                    info.removeAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK)
+                    super.onInitializeAccessibilityNodeInfo(host, info)
+                }
+            }
+        screenShareModeSpinner.isLongClickable = false
+        val defaultModePosition = screenShareOptions.indexOfFirst { it.mode == defaultSelectedMode }
+        screenShareModeSpinner.setSelection(defaultModePosition, /* animate= */ false)
+    }
+
+    override fun onItemSelected(adapterView: AdapterView<*>?, view: View, pos: Int, id: Long) {
         selectedScreenShareOption = screenShareOptions[pos]
         setOptionSpecificFields()
     }
 
+    override fun onNothingSelected(parent: AdapterView<*>?) {}
+
     private val warningText: String
-        get() = dialog.context.getString(selectedScreenShareOption.warningText, appName)
+        get() = containerView.context.getString(selectedScreenShareOption.warningText, appName)
 
     private val startButtonText: String
-        get() = dialog.context.getString(selectedScreenShareOption.startButtonText)
+        get() = containerView.context.getString(selectedScreenShareOption.startButtonText)
 
     fun setStartButtonOnClickListener(listener: View.OnClickListener?) {
         startButton.setOnClickListener { view ->
@@ -78,4 +114,44 @@
             listener?.onClick(view)
         }
     }
+
+    // Create additional options that is shown under the share mode spinner
+    // Eg. the audio and tap toggles in SysUI Recorder
+    @LayoutRes protected open fun getOptionsViewLayoutId(): Int? = null
+
+    private fun createOptionsView(@LayoutRes layoutId: Int?) {
+        if (layoutId == null) return
+        val stub = containerView.requireViewById<View>(R.id.options_stub) as ViewStub
+        stub.layoutResource = layoutId
+        stub.inflate()
+    }
+}
+
+private class OptionsAdapter(context: Context, private val options: List<ScreenShareOption>) :
+    ArrayAdapter<String>(
+        context,
+        R.layout.screen_share_dialog_spinner_text,
+        options.map { context.getString(it.spinnerText, it.displayName) },
+    ) {
+
+    override fun isEnabled(position: Int): Boolean {
+        return options[position].spinnerDisabledText == null
+    }
+
+    override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View {
+        val inflater = LayoutInflater.from(parent.context)
+        val view = inflater.inflate(R.layout.screen_share_dialog_spinner_item_text, parent, false)
+        val titleTextView = view.requireViewById<TextView>(android.R.id.text1)
+        val errorTextView = view.requireViewById<TextView>(android.R.id.text2)
+        titleTextView.text = getItem(position)
+        errorTextView.text = options[position].spinnerDisabledText
+        if (isEnabled(position)) {
+            errorTextView.visibility = View.GONE
+            titleTextView.isEnabled = true
+        } else {
+            errorTextView.visibility = View.VISIBLE
+            titleTextView.isEnabled = false
+        }
+        return view
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index e9b7534..3f14b55e 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -44,6 +44,7 @@
 import android.inputmethodservice.InputMethodService;
 import android.inputmethodservice.InputMethodService.BackDispositionMode;
 import android.inputmethodservice.InputMethodService.ImeWindowVisibility;
+import android.os.Handler;
 import android.os.RemoteException;
 import android.os.Trace;
 import android.util.Log;
@@ -61,6 +62,7 @@
 import com.android.internal.view.AppearanceRegion;
 import com.android.systemui.Dumpable;
 import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.model.SysUiState;
 import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
@@ -182,15 +184,18 @@
     private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
     private final StatusBarStateController mStatusBarStateController;
     private DisplayTracker mDisplayTracker;
+    private final Handler mBgHandler;
 
     @Inject
     public TaskbarDelegate(Context context,
             LightBarTransitionsController.Factory lightBarTransitionsControllerFactory,
             StatusBarKeyguardViewManager statusBarKeyguardViewManager,
-            StatusBarStateController statusBarStateController) {
+            StatusBarStateController statusBarStateController,
+            @Background Handler bgHandler) {
         mLightBarTransitionsControllerFactory = lightBarTransitionsControllerFactory;
 
         mContext = context;
+        mBgHandler = bgHandler;
         mDisplayManager = mContext.getSystemService(DisplayManager.class);
         mPipListener = (bounds) -> {
             mEdgeBackGestureHandler.setPipStashExclusionBounds(bounds);
@@ -245,7 +250,9 @@
                 new LightBarTransitionsController.DarkIntensityApplier() {
                     @Override
                     public void applyDarkIntensity(float darkIntensity) {
-                        mOverviewProxyService.onNavButtonsDarkIntensityChanged(darkIntensity);
+                        mBgHandler.post(() -> {
+                            mOverviewProxyService.onNavButtonsDarkIntensityChanged(darkIntensity);
+                        });
                     }
 
                     @Override
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 c895732..f9df676 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/views/NavigationBar.java
@@ -720,9 +720,24 @@
 
         if (DEBUG) Log.v(TAG, "addNavigationBar: about to add " + mView);
 
-        mViewCaptureAwareWindowManager.addView(mFrame,
-                getBarLayoutParams(mContext.getResources().getConfiguration().windowConfiguration
-                        .getRotation()));
+        try {
+            mViewCaptureAwareWindowManager.addView(
+                    mFrame,
+                    getBarLayoutParams(
+                            mContext.getResources()
+                                    .getConfiguration()
+                                    .windowConfiguration
+                                    .getRotation()));
+        } catch (WindowManager.InvalidDisplayException e) {
+            // Wrapping this in a try/catch to avoid crashes when a display is instantly removed
+            // after being added, and initialization hasn't finished yet.
+            Log.e(
+                    TAG,
+                    "Unable to add view to WindowManager. Display with id "
+                            + mDisplayId
+                            + " does not exist anymore",
+                    e);
+        }
         mDisplayId = mContext.getDisplayId();
         mIsOnDefaultDisplay = mDisplayId == mDisplayTracker.getDefaultDisplayId();
 
@@ -764,6 +779,15 @@
             Trace.beginSection("NavigationBar#removeViewImmediate");
             try {
                 mViewCaptureAwareWindowManager.removeViewImmediate(mView.getRootView());
+            } catch (IllegalArgumentException e) {
+                // Wrapping this in a try/catch to avoid crashes when a display is instantly removed
+                // after being added, and initialization hasn't finished yet.
+                // When that happens, adding the View to WindowManager fails, and therefore removing
+                // it here will fail too, since it wasn't added in the first place.
+                Log.e(
+                        TAG,
+                        "Failed to removed view from WindowManager. The View wasn't attached.",
+                        e);
             } finally {
                 Trace.endSection();
             }
@@ -859,7 +883,15 @@
         if (mOrientationHandle != null) {
             resetSecondaryHandle();
             getBarTransitions().removeDarkIntensityListener(mOrientationHandleIntensityListener);
-            mViewCaptureAwareWindowManager.removeView(mOrientationHandle);
+            try {
+                mViewCaptureAwareWindowManager.removeView(mOrientationHandle);
+            } catch (IllegalArgumentException e) {
+                // Wrapping this in a try/catch to avoid crashes when a display is instantly removed
+                // after being added, and initialization hasn't finished yet.
+                // When that happens, adding the View to WindowManager fails, and therefore removing
+                // it here will fail too, since it wasn't added in the first place.
+                Log.e(TAG, "Trying to remove a View that is not attached", e);
+            }
             mOrientationHandle.getViewTreeObserver().removeOnGlobalLayoutListener(
                     mOrientationHandleGlobalLayoutListener);
         }
@@ -930,7 +962,18 @@
         mOrientationParams.setTitle("SecondaryHomeHandle" + mContext.getDisplayId());
         mOrientationParams.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION
                 | WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT;
-        mViewCaptureAwareWindowManager.addView(mOrientationHandle, mOrientationParams);
+        try {
+            mViewCaptureAwareWindowManager.addView(mOrientationHandle, mOrientationParams);
+        } catch (WindowManager.InvalidDisplayException e) {
+            // Wrapping this in a try/catch to avoid crashes when a display is instantly removed
+            // after being added, and initialization hasn't finished yet.
+            Log.e(
+                    TAG,
+                    "Unable to add view to WindowManager. Display with id "
+                            + mDisplayId
+                            + " does not exist anymore",
+                    e);
+        }
         mOrientationHandle.setVisibility(View.GONE);
 
         logNavbarOrientation("initSecondaryHomeHandleForRotation");
diff --git a/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt b/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt
index 8a3ee12..f15a7b3 100644
--- a/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/power/domain/interactor/PowerInteractor.kt
@@ -224,6 +224,10 @@
         }
     }
 
+    fun onWalletLaunchGestureDetected() {
+        repository.updateWakefulness(powerButtonLaunchGestureTriggered = true)
+    }
+
     companion object {
         private const val FSI_WAKE_WHY = "full_screen_intent"
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/flags/QsDetailedView.kt b/packages/SystemUI/src/com/android/systemui/qs/flags/QsDetailedView.kt
index ffeec4e..c302cb2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/flags/QsDetailedView.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/flags/QsDetailedView.kt
@@ -79,6 +79,14 @@
     @JvmStatic
     inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME)
 
+    /**
+     * Called to ensure code is only run when the flag is enabled. 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)
+
     /** Returns a developer-readable string that describes the current requirement list. */
     @JvmStatic
     fun requirementDescription(): String {
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 9677d47..3b3fb9b 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
@@ -23,6 +23,7 @@
 import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor
 import com.android.systemui.qs.pipeline.domain.interactor.RestoreReconciliationInteractor
 import com.android.systemui.qs.pipeline.shared.QSPipelineFlagsRepository
+import com.android.systemui.qs.shared.QSSettingsPackageRepository
 import javax.inject.Inject
 
 @SysUISingleton
@@ -33,12 +34,14 @@
     private val accessibilityTilesInteractor: AccessibilityTilesInteractor,
     private val autoAddInteractor: AutoAddInteractor,
     private val featureFlags: QSPipelineFlagsRepository,
+    private val settingsPackageRepository: QSSettingsPackageRepository,
     private val restoreReconciliationInteractor: RestoreReconciliationInteractor,
 ) : CoreStartable {
 
     override fun start() {
         accessibilityTilesInteractor.init(currentTilesInteractor)
         autoAddInteractor.init(currentTilesInteractor)
+        settingsPackageRepository.init()
         restoreReconciliationInteractor.start()
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/shared/QSSettingsPackageRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/shared/QSSettingsPackageRepository.kt
new file mode 100644
index 0000000..592e9da
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/shared/QSSettingsPackageRepository.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.qs.shared
+
+import android.content.Context
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.os.UserHandle
+import android.provider.Settings
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.user.data.repository.UserRepository
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+
+/**
+ * Provides the cached package name of the default Settings application.
+ *
+ * This repository retrieves and stores the package name to avoid repeated lookups. The package name
+ * is retrieved in a background thread when the `init()` method is called.
+ */
+@SysUISingleton
+@Suppress("ShadeDisplayAwareContextChecker")
+class QSSettingsPackageRepository
+@Inject
+constructor(
+    private val context: Context,
+    @Background private val backgroundScope: CoroutineScope,
+    private val userRepository: UserRepository,
+) {
+    private var settingsPackageName: String? = null
+
+    /**
+     * Initializes the repository by determining and caching the package name of the Settings app.
+     */
+    fun init() {
+        backgroundScope.launch {
+            val mainUserId = userRepository.mainUserId
+            val mainUserContext =
+                context.createContextAsUser(UserHandle.of(mainUserId), /* flags */ 0)
+            val pm = mainUserContext.packageManager
+            settingsPackageName =
+                pm.queryIntentActivities(
+                        Intent(Settings.ACTION_SETTINGS),
+                        PackageManager.MATCH_SYSTEM_ONLY or PackageManager.MATCH_DEFAULT_ONLY,
+                    )
+                    .firstOrNull()
+                    ?.activityInfo
+                    ?.packageName ?: DEFAULT_SETTINGS_PACKAGE_NAME
+        }
+    }
+
+    /**
+     * Returns the cached package name of the Settings app.
+     *
+     * If the package name has not been initialized yet, this method will return the default
+     * Settings package name.
+     *
+     * @return The package name of the Settings app.
+     */
+    fun getSettingsPackageName(): String {
+        return settingsPackageName ?: DEFAULT_SETTINGS_PACKAGE_NAME
+    }
+
+    companion object {
+        private const val DEFAULT_SETTINGS_PACKAGE_NAME = "com.android.settings"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
index d401b6e..a98b8e5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
@@ -784,7 +784,11 @@
 
     private fun updateLongPressEffect(handlesLongClick: Boolean) {
         // The long press effect in the tile can't be updated if it is still running
-        if (longPressEffect?.state != QSLongPressEffect.State.IDLE) return
+        if (
+            longPressEffect?.state != QSLongPressEffect.State.IDLE &&
+                longPressEffect?.state != QSLongPressEffect.State.CLICKED
+        )
+            return
 
         longPressEffect.qsTile?.state?.handlesLongClick = handlesLongClick
         if (handlesLongClick && longPressEffect.initializeEffect(longPressEffectDuration)) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorCorrectionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorCorrectionTile.java
index c2e609d..1f93681 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorCorrectionTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorCorrectionTile.java
@@ -38,6 +38,7 @@
 import com.android.systemui.qs.QsEventLogger;
 import com.android.systemui.qs.UserSettingObserver;
 import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.qs.shared.QSSettingsPackageRepository;
 import com.android.systemui.qs.tileimpl.QSTileImpl;
 import com.android.systemui.res.R;
 import com.android.systemui.settings.UserTracker;
@@ -53,6 +54,7 @@
     @Nullable
     private Icon mIcon = null;
     private final UserSettingObserver mSetting;
+    private final QSSettingsPackageRepository mQSSettingsPackageRepository;
 
     @Inject
     public ColorCorrectionTile(
@@ -66,11 +68,13 @@
             ActivityStarter activityStarter,
             QSLogger qsLogger,
             UserTracker userTracker,
-            SecureSettings secureSettings
+            SecureSettings secureSettings,
+            QSSettingsPackageRepository qsSettingsPackageRepository
     ) {
         super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
                 statusBarStateController, activityStarter, qsLogger);
 
+        mQSSettingsPackageRepository = qsSettingsPackageRepository;
         mSetting = new UserSettingObserver(secureSettings, mHandler,
                 Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, userTracker.getUserId()) {
             @Override
@@ -106,7 +110,8 @@
 
     @Override
     public Intent getLongClickIntent() {
-        return new Intent(Settings.ACTION_COLOR_CORRECTION_SETTINGS);
+        return new Intent(Settings.ACTION_COLOR_CORRECTION_SETTINGS)
+                .setPackage(mQSSettingsPackageRepository.getSettingsPackageName());
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
index ce80133..38e9a20 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
@@ -39,6 +39,7 @@
 import com.android.systemui.qs.QsEventLogger;
 import com.android.systemui.qs.UserSettingObserver;
 import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.qs.shared.QSSettingsPackageRepository;
 import com.android.systemui.qs.tileimpl.QSTileImpl;
 import com.android.systemui.res.R;
 import com.android.systemui.settings.UserTracker;
@@ -51,6 +52,7 @@
 
     public static final String TILE_SPEC = "inversion";
     private final UserSettingObserver mSetting;
+    private final QSSettingsPackageRepository mQSSettingsPackageRepository;
 
     @Inject
     public ColorInversionTile(
@@ -64,11 +66,13 @@
             ActivityStarter activityStarter,
             QSLogger qsLogger,
             UserTracker userTracker,
-            SecureSettings secureSettings
+            SecureSettings secureSettings,
+            QSSettingsPackageRepository qsSettingsPackageRepository
     ) {
         super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
                 statusBarStateController, activityStarter, qsLogger);
 
+        mQSSettingsPackageRepository = qsSettingsPackageRepository;
         mSetting = new UserSettingObserver(secureSettings, mHandler,
                 Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, userTracker.getUserId()) {
             @Override
@@ -104,7 +108,8 @@
 
     @Override
     public Intent getLongClickIntent() {
-        return new Intent(Settings.ACTION_COLOR_INVERSION_SETTINGS);
+        return new Intent(Settings.ACTION_COLOR_INVERSION_SETTINGS)
+                .setPackage(mQSSettingsPackageRepository.getSettingsPackageName());
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FontScalingTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/FontScalingTile.kt
index 43e84a0..4050f2a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FontScalingTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FontScalingTile.kt
@@ -34,6 +34,7 @@
 import com.android.systemui.qs.QSHost
 import com.android.systemui.qs.QsEventLogger
 import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.qs.shared.QSSettingsPackageRepository
 import com.android.systemui.qs.tileimpl.QSTileImpl
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.phone.SystemUIDialog
@@ -56,6 +57,7 @@
     private val keyguardStateController: KeyguardStateController,
     private val dialogTransitionAnimator: DialogTransitionAnimator,
     private val fontScalingDialogDelegateProvider: Provider<FontScalingDialogDelegate>,
+    private val settingsPackageRepository: QSSettingsPackageRepository,
 ) :
     QSTileImpl<QSTile.State?>(
         host,
@@ -118,6 +120,7 @@
 
     override fun getLongClickIntent(): Intent? {
         return Intent(Settings.ACTION_TEXT_READING_SETTINGS)
+            .setPackage(settingsPackageRepository.getSettingsPackageName())
     }
 
     override fun getTileLabel(): CharSequence {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
index ec8d30b..e93cec8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ScreenRecordTile.java
@@ -41,12 +41,14 @@
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.qs.QSTile;
+import com.android.systemui.plugins.qs.TileDetailsViewModel;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.QsEventLogger;
 import com.android.systemui.qs.logging.QSLogger;
 import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor;
 import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.qs.tiles.dialog.ScreenRecordDetailsViewModel;
 import com.android.systemui.res.R;
 import com.android.systemui.screenrecord.RecordingController;
 import com.android.systemui.screenrecord.data.model.ScreenRecordModel;
@@ -54,6 +56,8 @@
 import com.android.systemui.statusbar.phone.KeyguardDismissUtil;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 
+import java.util.function.Consumer;
+
 import javax.inject.Inject;
 
 /**
@@ -122,17 +126,78 @@
 
     @Override
     protected void handleClick(@Nullable Expandable expandable) {
+        handleClick(() -> showDialog(expandable));
+    }
+
+    private void showDialog(@Nullable Expandable expandable) {
+        final Dialog dialog = mController.createScreenRecordDialog(
+                this::onStartRecordingClicked);
+
+        executeWhenUnlockedKeyguard(() -> {
+            // We animate from the touched view only if we are not on the keyguard, given that if we
+            // are we will dismiss it which will also collapse the shade.
+            boolean shouldAnimateFromExpandable =
+                    expandable != null && !mKeyguardStateController.isShowing();
+
+            if (shouldAnimateFromExpandable) {
+                DialogTransitionAnimator.Controller controller =
+                        expandable.dialogTransitionController(new DialogCuj(
+                                InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN,
+                                INTERACTION_JANK_TAG));
+                if (controller != null) {
+                    mDialogTransitionAnimator.show(dialog,
+                            controller, /* animateBackgroundBoundsChange= */ true);
+                } else {
+                    dialog.show();
+                }
+            } else {
+                dialog.show();
+            }
+        });
+    }
+
+    private void onStartRecordingClicked() {
+        // We dismiss the shade. Since starting the recording will also dismiss the dialog (if
+        // there is one showing), we disable the exit animation which looks weird when it happens
+        // at the same time as the shade collapsing.
+        mDialogTransitionAnimator.disableAllCurrentDialogsExitAnimations();
+        mPanelInteractor.collapsePanels();
+    }
+
+    private void executeWhenUnlockedKeyguard(Runnable dismissActionCallback) {
+        ActivityStarter.OnDismissAction dismissAction = () -> {
+            dismissActionCallback.run();
+
+            int uid = mUserContextProvider.getUserContext().getUserId();
+            mMediaProjectionMetricsLogger.notifyPermissionRequestDisplayed(uid);
+
+            return false;
+        };
+
+        mKeyguardDismissUtil.executeWhenUnlocked(dismissAction, false /* requiresShadeOpen */,
+                true /* afterKeyguardDone */);
+    }
+
+    private void handleClick(Runnable showPromptCallback) {
         if (mController.isStarting()) {
             cancelCountdown();
         } else if (mController.isRecording()) {
             stopRecording();
         } else {
-            mUiHandler.post(() -> showPrompt(expandable));
+            mUiHandler.post(showPromptCallback);
         }
         refreshState();
     }
 
     @Override
+    public boolean getDetailsViewModel(Consumer<TileDetailsViewModel> callback) {
+        handleClick(() ->
+                callback.accept(new ScreenRecordDetailsViewModel())
+        );
+        return true;
+    }
+
+    @Override
     protected void handleUpdateState(BooleanState state, Object arg) {
         boolean isStarting = mController.isStarting();
         boolean isRecording = mController.isRecording();
@@ -178,49 +243,6 @@
         return mContext.getString(R.string.quick_settings_screen_record_label);
     }
 
-    private void showPrompt(@Nullable Expandable expandable) {
-        // We animate from the touched view only if we are not on the keyguard, given that if we
-        // are we will dismiss it which will also collapse the shade.
-        boolean shouldAnimateFromExpandable =
-                expandable != null && !mKeyguardStateController.isShowing();
-
-        // Create the recording dialog that will collapse the shade only if we start the recording.
-        Runnable onStartRecordingClicked = () -> {
-            // We dismiss the shade. Since starting the recording will also dismiss the dialog, we
-            // disable the exit animation which looks weird when it happens at the same time as the
-            // shade collapsing.
-            mDialogTransitionAnimator.disableAllCurrentDialogsExitAnimations();
-            mPanelInteractor.collapsePanels();
-        };
-
-        final Dialog dialog = mController.createScreenRecordDialog(onStartRecordingClicked);
-
-        ActivityStarter.OnDismissAction dismissAction = () -> {
-            if (shouldAnimateFromExpandable) {
-                DialogTransitionAnimator.Controller controller =
-                        expandable.dialogTransitionController(new DialogCuj(
-                                InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN,
-                                INTERACTION_JANK_TAG));
-                if (controller != null) {
-                    mDialogTransitionAnimator.show(dialog,
-                            controller, /* animateBackgroundBoundsChange= */ true);
-                } else {
-                    dialog.show();
-                }
-            } else {
-                dialog.show();
-            }
-
-            int uid = mUserContextProvider.getUserContext().getUserId();
-            mMediaProjectionMetricsLogger.notifyPermissionRequestDisplayed(uid);
-
-            return false;
-        };
-
-        mKeyguardDismissUtil.executeWhenUnlocked(dismissAction, false /* requiresShadeOpen */,
-                true /* afterKeyguardDone */);
-    }
-
     private void cancelCountdown() {
         Log.d(TAG, "Cancelling countdown");
         mController.cancelCountdown();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java
index 7516ca0..b21c3e4 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetAdapter.java
@@ -54,20 +54,21 @@
 
     private static final String TAG = "InternetAdapter";
 
-    private final InternetDialogController mInternetDialogController;
+    private final InternetDetailsContentController mInternetDetailsContentController;
     private final CoroutineScope mCoroutineScope;
     @Nullable
     private List<WifiEntry> mWifiEntries;
     @VisibleForTesting
     protected int mWifiEntriesCount;
     @VisibleForTesting
-    protected int mMaxEntriesCount = InternetDialogController.MAX_WIFI_ENTRY_COUNT;
+    protected int mMaxEntriesCount = InternetDetailsContentController.MAX_WIFI_ENTRY_COUNT;
 
     protected View mHolderView;
     protected Context mContext;
 
-    public InternetAdapter(InternetDialogController controller, CoroutineScope coroutineScope) {
-        mInternetDialogController = controller;
+    public InternetAdapter(InternetDetailsContentController controller,
+            CoroutineScope coroutineScope) {
+        mInternetDetailsContentController = controller;
         mCoroutineScope = coroutineScope;
     }
 
@@ -77,7 +78,8 @@
         mContext = viewGroup.getContext();
         mHolderView = LayoutInflater.from(mContext).inflate(R.layout.internet_list_item,
                 viewGroup, false);
-        return new InternetViewHolder(mHolderView, mInternetDialogController, mCoroutineScope);
+        return new InternetViewHolder(mHolderView, mInternetDetailsContentController,
+                mCoroutineScope);
     }
 
     @Override
@@ -137,16 +139,17 @@
         final TextView mWifiSummaryText;
         final ImageView mWifiEndIcon;
         final Context mContext;
-        final InternetDialogController mInternetDialogController;
+        final InternetDetailsContentController mInternetDetailsContentController;
         final CoroutineScope mCoroutineScope;
         @Nullable
         private Job mJob;
 
-        InternetViewHolder(View view, InternetDialogController internetDialogController,
+        InternetViewHolder(View view,
+                InternetDetailsContentController internetDetailsContentController,
                 CoroutineScope coroutineScope) {
             super(view);
             mContext = view.getContext();
-            mInternetDialogController = internetDialogController;
+            mInternetDetailsContentController = internetDetailsContentController;
             mCoroutineScope = coroutineScope;
             mContainerLayout = view.requireViewById(R.id.internet_container);
             mWifiListLayout = view.requireViewById(R.id.wifi_list);
@@ -169,7 +172,7 @@
             mWifiListLayout.setEnabled(shouldEnabled(wifiEntry));
             if (connectedState != WifiEntry.CONNECTED_STATE_DISCONNECTED) {
                 mWifiListLayout.setOnClickListener(
-                        v -> mInternetDialogController.launchWifiDetailsSetting(
+                        v -> mInternetDetailsContentController.launchWifiDetailsSetting(
                                 wifiEntry.getKey(), v));
                 return;
             }
@@ -193,7 +196,7 @@
                 if (mJob == null) {
                     mJob = WifiUtils.checkWepAllowed(mContext, mCoroutineScope, wifiEntry.getSsid(),
                             WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG, intent -> {
-                                mInternetDialogController.startActivityForDialog(intent);
+                                mInternetDetailsContentController.startActivityForDialog(intent);
                                 return null;
                             }, () -> {
                                 wifiConnect(wifiEntry, view);
@@ -211,19 +214,20 @@
                         true /* connectForCaller */);
                 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                 intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
-                mInternetDialogController.startActivityForDialog(intent);
+                mInternetDetailsContentController.startActivityForDialog(intent);
                 return;
             }
 
             if (wifiEntry.canConnect()) {
-                mInternetDialogController.connect(wifiEntry);
+                mInternetDetailsContentController.connect(wifiEntry);
                 return;
             }
 
             if (wifiEntry.isSaved()) {
                 Log.w(TAG, "The saved Wi-Fi network does not allow to connect. SSID:"
                         + wifiEntry.getSsid());
-                mInternetDialogController.launchWifiDetailsSetting(wifiEntry.getKey(), view);
+                mInternetDetailsContentController.launchWifiDetailsSetting(wifiEntry.getKey(),
+                        view);
             }
         }
 
@@ -239,7 +243,7 @@
 
         @Nullable
         Drawable getWifiDrawable(@NonNull WifiEntry wifiEntry) {
-            Drawable drawable = mInternetDialogController.getWifiDrawable(wifiEntry);
+            Drawable drawable = mInternetDetailsContentController.getWifiDrawable(wifiEntry);
             if (drawable == null) {
                 return null;
             }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentController.java
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentController.java
index 7036ef91..340cb68 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentController.java
@@ -117,9 +117,9 @@
 /**
  * Controller for Internet Dialog.
  */
-public class InternetDialogController implements AccessPointController.AccessPointCallback {
+public class InternetDetailsContentController implements AccessPointController.AccessPointCallback {
 
-    private static final String TAG = "InternetDialogController";
+    private static final String TAG = "InternetDetailsContentController";
     private static final String ACTION_WIFI_SCANNING_SETTINGS =
             "android.settings.WIFI_SCANNING_SETTINGS";
     /**
@@ -244,7 +244,8 @@
     }
 
     @Inject
-    public InternetDialogController(@ShadeDisplayAware Context context, UiEventLogger uiEventLogger,
+    public InternetDetailsContentController(@ShadeDisplayAware Context context,
+            UiEventLogger uiEventLogger,
             ActivityStarter starter, AccessPointController accessPointController,
             SubscriptionManager subscriptionManager, TelephonyManager telephonyManager,
             @Nullable WifiManager wifiManager, ConnectivityManager connectivityManager,
@@ -260,7 +261,7 @@
             FeatureFlags featureFlags
     ) {
         if (DEBUG) {
-            Log.d(TAG, "Init InternetDialogController");
+            Log.d(TAG, "Init InternetDetailsContentController");
         }
         mHandler = handler;
         mWorkerHandler = workerHandler;
@@ -372,7 +373,6 @@
         mConnectivityManager.setAirplaneMode(false);
     }
 
-    @VisibleForTesting
     protected int getDefaultDataSubscriptionId() {
         return mSubscriptionManager.getDefaultDataSubscriptionId();
     }
@@ -1108,13 +1108,13 @@
     static class WifiEntryConnectCallback implements WifiEntry.ConnectCallback {
         final ActivityStarter mActivityStarter;
         final WifiEntry mWifiEntry;
-        final InternetDialogController mInternetDialogController;
+        final InternetDetailsContentController mInternetDetailsContentController;
 
         WifiEntryConnectCallback(ActivityStarter activityStarter, WifiEntry connectWifiEntry,
-                InternetDialogController internetDialogController) {
+                InternetDetailsContentController internetDetailsContentController) {
             mActivityStarter = activityStarter;
             mWifiEntry = connectWifiEntry;
-            mInternetDialogController = internetDialogController;
+            mInternetDetailsContentController = internetDetailsContentController;
         }
 
         @Override
@@ -1129,7 +1129,8 @@
                 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                 mActivityStarter.startActivity(intent, false /* dismissShade */);
             } else if (status == CONNECT_STATUS_FAILURE_UNKNOWN) {
-                mInternetDialogController.makeOverlayToast(R.string.wifi_failed_connect_message);
+                mInternetDetailsContentController.makeOverlayToast(
+                        R.string.wifi_failed_connect_message);
             } else {
                 if (DEBUG) {
                     Log.d(TAG, "connect failure reason=" + status);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentManager.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentManager.kt
new file mode 100644
index 0000000..c64532a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentManager.kt
@@ -0,0 +1,991 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.dialog
+
+import android.app.AlertDialog
+import android.content.Context
+import android.content.DialogInterface
+import android.graphics.drawable.Drawable
+import android.net.Network
+import android.net.NetworkCapabilities
+import android.os.Handler
+import android.telephony.ServiceState
+import android.telephony.SignalStrength
+import android.telephony.SubscriptionManager
+import android.telephony.TelephonyDisplayInfo
+import android.text.Html
+import android.text.Layout
+import android.text.TextUtils
+import android.text.method.LinkMovementMethod
+import android.util.Log
+import android.view.View
+import android.view.ViewStub
+import android.view.WindowManager
+import android.widget.Button
+import android.widget.ImageView
+import android.widget.LinearLayout
+import android.widget.ProgressBar
+import android.widget.Switch
+import android.widget.TextView
+import androidx.annotation.MainThread
+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
+import com.android.internal.logging.UiEvent
+import com.android.internal.logging.UiEventLogger
+import com.android.internal.telephony.flags.Flags
+import com.android.settingslib.satellite.SatelliteDialogUtils.TYPE_IS_WIFI
+import com.android.settingslib.satellite.SatelliteDialogUtils.mayStartSatelliteWarningDialog
+import com.android.settingslib.wifi.WifiEnterpriseRestrictionUtils
+import com.android.systemui.Prefs
+import com.android.systemui.accessibility.floatingmenu.AnnotationLinkSpan
+import com.android.systemui.animation.DialogTransitionAnimator
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.qs.flags.QsDetailedView
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.wifitrackerlib.WifiEntry
+import com.google.common.annotations.VisibleForTesting
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import java.util.concurrent.Executor
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+
+/**
+ * View content for the Internet tile details that handles all UI interactions and state management.
+ *
+ * @param internetDialog non-null if the details should be shown as part of a dialog and null
+ *   otherwise.
+ */
+// TODO: b/377388104 Make this content for details view only.
+class InternetDetailsContentManager
+@AssistedInject
+constructor(
+    private val internetDetailsContentController: InternetDetailsContentController,
+    @Assisted(CAN_CONFIG_MOBILE_DATA) private val canConfigMobileData: Boolean,
+    @Assisted(CAN_CONFIG_WIFI) private val canConfigWifi: Boolean,
+    @Assisted private val coroutineScope: CoroutineScope,
+    @Assisted private var context: Context,
+    @Assisted private var internetDialog: SystemUIDialog?,
+    private val uiEventLogger: UiEventLogger,
+    private val dialogTransitionAnimator: DialogTransitionAnimator,
+    @Main private val handler: Handler,
+    @Background private val backgroundExecutor: Executor,
+    private val keyguard: KeyguardStateController,
+) {
+    // Lifecycle
+    private lateinit var lifecycleRegistry: LifecycleRegistry
+    @VisibleForTesting internal var lifecycleOwner: LifecycleOwner? = null
+    @VisibleForTesting internal val internetContentData = MutableLiveData<InternetContent>()
+    @VisibleForTesting internal var connectedWifiEntry: WifiEntry? = null
+    @VisibleForTesting internal var isProgressBarVisible = false
+
+    // UI Components
+    private lateinit var contentView: View
+    private lateinit var internetDialogTitleView: TextView
+    private lateinit var internetDialogSubTitleView: TextView
+    private lateinit var divider: View
+    private lateinit var progressBar: ProgressBar
+    private lateinit var ethernetLayout: LinearLayout
+    private lateinit var mobileNetworkLayout: LinearLayout
+    private var secondaryMobileNetworkLayout: LinearLayout? = null
+    private lateinit var turnWifiOnLayout: LinearLayout
+    private lateinit var wifiToggleTitleTextView: TextView
+    private lateinit var wifiScanNotifyLayout: LinearLayout
+    private lateinit var wifiScanNotifyTextView: TextView
+    private lateinit var connectedWifiListLayout: LinearLayout
+    private lateinit var connectedWifiIcon: ImageView
+    private lateinit var connectedWifiTitleTextView: TextView
+    private lateinit var connectedWifiSummaryTextView: TextView
+    private lateinit var wifiSettingsIcon: ImageView
+    private lateinit var wifiRecyclerView: RecyclerView
+    private lateinit var seeAllLayout: LinearLayout
+    private lateinit var signalIcon: ImageView
+    private lateinit var mobileTitleTextView: TextView
+    private lateinit var mobileSummaryTextView: TextView
+    private lateinit var airplaneModeSummaryTextView: TextView
+    private lateinit var mobileDataToggle: Switch
+    private lateinit var mobileToggleDivider: View
+    private lateinit var wifiToggle: Switch
+    private lateinit var shareWifiButton: Button
+    private lateinit var airplaneModeButton: Button
+    private var alertDialog: AlertDialog? = null
+    private lateinit var doneButton: Button
+
+    private val canChangeWifiState =
+        WifiEnterpriseRestrictionUtils.isChangeWifiStateAllowed(context)
+    private var wifiNetworkHeight = 0
+    private var backgroundOn: Drawable? = null
+    private var backgroundOff: Drawable? = null
+    private var clickJob: Job? = null
+    private var defaultDataSubId = internetDetailsContentController.defaultDataSubscriptionId
+    @VisibleForTesting
+    internal var adapter = InternetAdapter(internetDetailsContentController, coroutineScope)
+    @VisibleForTesting internal var wifiEntriesCount: Int = 0
+    @VisibleForTesting internal var hasMoreWifiEntries: Boolean = false
+
+    @AssistedFactory
+    interface Factory {
+        fun create(
+            @Assisted(CAN_CONFIG_MOBILE_DATA) canConfigMobileData: Boolean,
+            @Assisted(CAN_CONFIG_WIFI) canConfigWifi: Boolean,
+            coroutineScope: CoroutineScope,
+            context: Context,
+            internetDialog: SystemUIDialog?,
+        ): InternetDetailsContentManager
+    }
+
+    /**
+     * Binds the content manager to the provided content view.
+     *
+     * This method initializes the lifecycle, views, click listeners, and UI of the details content.
+     * It also updates the UI with the current Wi-Fi network information.
+     *
+     * @param contentView The view to which the content manager should be bound.
+     */
+    fun bind(contentView: View) {
+        if (DEBUG) {
+            Log.d(TAG, "Bind InternetDetailsContentManager")
+        }
+
+        this.contentView = contentView
+
+        initializeLifecycle()
+        initializeViews()
+        updateDetailsUI(getStartingInternetContent())
+        initializeAndConfigure()
+    }
+
+    /**
+     * Initializes the LifecycleRegistry if it hasn't been initialized yet. It sets the initial
+     * state of the LifecycleRegistry to Lifecycle.State.CREATED.
+     */
+    fun initializeLifecycle() {
+        if (!::lifecycleRegistry.isInitialized) {
+            lifecycleOwner =
+                object : LifecycleOwner {
+                    override val lifecycle: Lifecycle
+                        get() = lifecycleRegistry
+                }
+            lifecycleRegistry = LifecycleRegistry(lifecycleOwner!!)
+        }
+        lifecycleRegistry.currentState = Lifecycle.State.CREATED
+    }
+
+    private fun initializeViews() {
+        // Set accessibility properties
+        contentView.accessibilityPaneTitle =
+            context.getText(R.string.accessibility_desc_quick_settings)
+
+        // Get dimension resources
+        wifiNetworkHeight =
+            context.resources.getDimensionPixelSize(R.dimen.internet_dialog_wifi_network_height)
+
+        // Initialize LiveData observer
+        internetContentData.observe(lifecycleOwner!!) { internetContent ->
+            updateDetailsUI(internetContent)
+        }
+
+        // Network layouts
+        internetDialogTitleView = contentView.requireViewById(R.id.internet_dialog_title)
+        internetDialogSubTitleView = contentView.requireViewById(R.id.internet_dialog_subtitle)
+        divider = contentView.requireViewById(R.id.divider)
+        progressBar = contentView.requireViewById(R.id.wifi_searching_progress)
+
+        // Set wifi, mobile and ethernet layouts
+        setWifiLayout()
+        setMobileLayout()
+        ethernetLayout = contentView.requireViewById(R.id.ethernet_layout)
+
+        // Done button is only visible for the dialog view
+        doneButton = contentView.requireViewById(R.id.done_button)
+        if (internetDialog == null) {
+            doneButton.visibility = View.GONE
+        } else {
+            // Set done button if qs details view is not enabled.
+            doneButton.setOnClickListener { internetDialog!!.dismiss() }
+        }
+
+        // Share WiFi
+        shareWifiButton = contentView.requireViewById(R.id.share_wifi_button)
+        shareWifiButton.setOnClickListener { view ->
+            if (
+                internetDetailsContentController.mayLaunchShareWifiSettings(
+                    connectedWifiEntry,
+                    view,
+                )
+            ) {
+                uiEventLogger.log(InternetDetailsEvent.SHARE_WIFI_QS_BUTTON_CLICKED)
+            }
+        }
+
+        // Airplane mode
+        airplaneModeButton = contentView.requireViewById(R.id.apm_button)
+        airplaneModeButton.setOnClickListener {
+            internetDetailsContentController.setAirplaneModeDisabled()
+        }
+        airplaneModeSummaryTextView = contentView.requireViewById(R.id.airplane_mode_summary)
+
+        // Background drawables
+        backgroundOn = context.getDrawable(R.drawable.settingslib_switch_bar_bg_on)
+        backgroundOff = context.getDrawable(R.drawable.internet_dialog_selected_effect)
+    }
+
+    private fun setWifiLayout() {
+        // Initialize Wi-Fi related views
+        turnWifiOnLayout = contentView.requireViewById(R.id.turn_on_wifi_layout)
+        wifiToggleTitleTextView = contentView.requireViewById(R.id.wifi_toggle_title)
+        wifiScanNotifyLayout = contentView.requireViewById(R.id.wifi_scan_notify_layout)
+        wifiScanNotifyTextView = contentView.requireViewById(R.id.wifi_scan_notify_text)
+        connectedWifiListLayout = contentView.requireViewById(R.id.wifi_connected_layout)
+        connectedWifiIcon = contentView.requireViewById(R.id.wifi_connected_icon)
+        connectedWifiTitleTextView = contentView.requireViewById(R.id.wifi_connected_title)
+        connectedWifiSummaryTextView = contentView.requireViewById(R.id.wifi_connected_summary)
+        wifiSettingsIcon = contentView.requireViewById(R.id.wifi_settings_icon)
+        wifiToggle = contentView.requireViewById(R.id.wifi_toggle)
+        wifiRecyclerView =
+            contentView.requireViewById<RecyclerView>(R.id.wifi_list_layout).apply {
+                layoutManager = LinearLayoutManager(context)
+                adapter = this@InternetDetailsContentManager.adapter
+            }
+        seeAllLayout = contentView.requireViewById(R.id.see_all_layout)
+
+        // Set click listeners for Wi-Fi related views
+        wifiToggle.setOnClickListener {
+            val isChecked = wifiToggle.isChecked
+            handleWifiToggleClicked(isChecked)
+        }
+        connectedWifiListLayout.setOnClickListener(this::onClickConnectedWifi)
+        seeAllLayout.setOnClickListener(this::onClickSeeMoreButton)
+    }
+
+    private fun setMobileLayout() {
+        // Initialize mobile data related views
+        mobileNetworkLayout = contentView.requireViewById(R.id.mobile_network_layout)
+        signalIcon = contentView.requireViewById(R.id.signal_icon)
+        mobileTitleTextView = contentView.requireViewById(R.id.mobile_title)
+        mobileSummaryTextView = contentView.requireViewById(R.id.mobile_summary)
+        mobileDataToggle = contentView.requireViewById(R.id.mobile_toggle)
+        mobileToggleDivider = contentView.requireViewById(R.id.mobile_toggle_divider)
+
+        // Set click listeners for mobile data related views
+        mobileNetworkLayout.setOnClickListener {
+            val autoSwitchNonDdsSubId: Int =
+                internetDetailsContentController.getActiveAutoSwitchNonDdsSubId()
+            if (autoSwitchNonDdsSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+                showTurnOffAutoDataSwitchDialog(autoSwitchNonDdsSubId)
+            }
+            internetDetailsContentController.connectCarrierNetwork()
+        }
+
+        // Mobile data toggle
+        mobileDataToggle.setOnClickListener {
+            val isChecked = mobileDataToggle.isChecked
+            if (!isChecked && shouldShowMobileDialog()) {
+                mobileDataToggle.isChecked = true
+                showTurnOffMobileDialog()
+            } else if (internetDetailsContentController.isMobileDataEnabled != isChecked) {
+                internetDetailsContentController.setMobileDataEnabled(
+                    context,
+                    defaultDataSubId,
+                    isChecked,
+                    false,
+                )
+            }
+        }
+    }
+
+    /**
+     * This function ensures the component is in the RESUMED state and sets up the internet details
+     * content controller.
+     *
+     * If the component is already in the RESUMED state, this function does nothing.
+     */
+    fun initializeAndConfigure() {
+        // If the current state is RESUMED, it's already initialized.
+        if (lifecycleRegistry.currentState == Lifecycle.State.RESUMED) {
+            return
+        }
+
+        lifecycleRegistry.currentState = Lifecycle.State.RESUMED
+        internetDetailsContentController.onStart(internetDetailsCallback, canConfigWifi)
+        if (!canConfigWifi) {
+            hideWifiViews()
+        }
+    }
+
+    private fun getDialogTitleText(): CharSequence {
+        return internetDetailsContentController.getDialogTitleText()
+    }
+
+    private fun updateDetailsUI(internetContent: InternetContent) {
+        if (DEBUG) {
+            Log.d(TAG, "updateDetailsUI ")
+        }
+        if (QsDetailedView.isEnabled) {
+            internetDialogTitleView.visibility = View.GONE
+            internetDialogSubTitleView.visibility = View.GONE
+        } else {
+            internetDialogTitleView.text = internetContent.internetDialogTitleString
+            internetDialogSubTitleView.text = internetContent.internetDialogSubTitle
+        }
+        airplaneModeButton.visibility =
+            if (internetContent.isAirplaneModeEnabled) View.VISIBLE else View.GONE
+
+        updateEthernetUI(internetContent)
+        updateMobileUI(internetContent)
+        updateWifiUI(internetContent)
+    }
+
+    private fun getStartingInternetContent(): InternetContent {
+        return InternetContent(
+            internetDialogTitleString = getDialogTitleText(),
+            internetDialogSubTitle = getSubtitleText(),
+            isWifiEnabled = internetDetailsContentController.isWifiEnabled,
+            isDeviceLocked = internetDetailsContentController.isDeviceLocked,
+        )
+    }
+
+    private fun getSubtitleText(): String {
+        return internetDetailsContentController.getSubtitleText(isProgressBarVisible).toString()
+    }
+
+    @VisibleForTesting
+    internal fun hideWifiViews() {
+        setProgressBarVisible(false)
+        turnWifiOnLayout.visibility = View.GONE
+        connectedWifiListLayout.visibility = View.GONE
+        wifiRecyclerView.visibility = View.GONE
+        seeAllLayout.visibility = View.GONE
+        shareWifiButton.visibility = View.GONE
+    }
+
+    private fun setProgressBarVisible(visible: Boolean) {
+        if (isProgressBarVisible == visible) {
+            return
+        }
+
+        // Set the indeterminate value from false to true each time to ensure that the progress bar
+        // resets its animation and starts at the leftmost starting point each time it is displayed.
+        isProgressBarVisible = visible
+        progressBar.visibility = if (visible) View.VISIBLE else View.GONE
+        progressBar.isIndeterminate = visible
+        divider.visibility = if (visible) View.GONE else View.VISIBLE
+        internetDialogSubTitleView.text = getSubtitleText()
+    }
+
+    private fun showTurnOffAutoDataSwitchDialog(subId: Int) {
+        var carrierName: CharSequence? = getMobileNetworkTitle(defaultDataSubId)
+        if (TextUtils.isEmpty(carrierName)) {
+            carrierName = getDefaultCarrierName()
+        }
+        alertDialog =
+            AlertDialog.Builder(context)
+                .setTitle(context.getString(R.string.auto_data_switch_disable_title, carrierName))
+                .setMessage(R.string.auto_data_switch_disable_message)
+                .setNegativeButton(R.string.auto_data_switch_dialog_negative_button) { _, _ -> }
+                .setPositiveButton(R.string.auto_data_switch_dialog_positive_button) { _, _ ->
+                    internetDetailsContentController.setAutoDataSwitchMobileDataPolicy(
+                        subId,
+                        /* enable= */ false,
+                    )
+                    secondaryMobileNetworkLayout?.visibility = View.GONE
+                }
+                .create()
+        alertDialog!!.window?.setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG)
+        SystemUIDialog.setShowForAllUsers(alertDialog, true)
+        SystemUIDialog.registerDismissListener(alertDialog)
+        SystemUIDialog.setWindowOnTop(alertDialog, keyguard.isShowing())
+        if (QsDetailedView.isEnabled) {
+            alertDialog!!.show()
+        } else {
+            dialogTransitionAnimator.showFromDialog(alertDialog!!, internetDialog!!, null, false)
+            Log.e(TAG, "Internet dialog is shown with the refactor code")
+        }
+    }
+
+    private fun shouldShowMobileDialog(): Boolean {
+        val mobileDataTurnedOff =
+            Prefs.getBoolean(context, Prefs.Key.QS_HAS_TURNED_OFF_MOBILE_DATA, false)
+        return internetDetailsContentController.isMobileDataEnabled && !mobileDataTurnedOff
+    }
+
+    private fun getMobileNetworkTitle(subId: Int): CharSequence {
+        return internetDetailsContentController.getMobileNetworkTitle(subId)
+    }
+
+    private fun showTurnOffMobileDialog() {
+        val context = contentView.context
+        var carrierName: CharSequence? = getMobileNetworkTitle(defaultDataSubId)
+        val isInService: Boolean =
+            internetDetailsContentController.isVoiceStateInService(defaultDataSubId)
+        if (TextUtils.isEmpty(carrierName) || !isInService) {
+            carrierName = getDefaultCarrierName()
+        }
+        alertDialog =
+            AlertDialog.Builder(context)
+                .setTitle(R.string.mobile_data_disable_title)
+                .setMessage(context.getString(R.string.mobile_data_disable_message, carrierName))
+                .setNegativeButton(android.R.string.cancel) { _: DialogInterface?, _: Int -> }
+                .setPositiveButton(
+                    com.android.internal.R.string.alert_windows_notification_turn_off_action
+                ) { _: DialogInterface?, _: Int ->
+                    internetDetailsContentController.setMobileDataEnabled(
+                        context,
+                        defaultDataSubId,
+                        false,
+                        false,
+                    )
+                    mobileDataToggle.isChecked = false
+                    Prefs.putBoolean(context, Prefs.Key.QS_HAS_TURNED_OFF_MOBILE_DATA, true)
+                }
+                .create()
+        alertDialog!!.window?.setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG)
+        SystemUIDialog.setShowForAllUsers(alertDialog, true)
+        SystemUIDialog.registerDismissListener(alertDialog)
+        SystemUIDialog.setWindowOnTop(alertDialog, keyguard.isShowing())
+        if (QsDetailedView.isEnabled) {
+            alertDialog!!.show()
+        } else {
+            dialogTransitionAnimator.showFromDialog(alertDialog!!, internetDialog!!, null, false)
+        }
+    }
+
+    private fun onClickConnectedWifi(view: View?) {
+        if (connectedWifiEntry == null) {
+            return
+        }
+        internetDetailsContentController.launchWifiDetailsSetting(connectedWifiEntry!!.key, view)
+    }
+
+    private fun onClickSeeMoreButton(view: View?) {
+        internetDetailsContentController.launchNetworkSetting(view)
+    }
+
+    private fun handleWifiToggleClicked(isChecked: Boolean) {
+        if (Flags.oemEnabledSatelliteFlag()) {
+            if (clickJob != null && !clickJob!!.isCompleted) {
+                return
+            }
+            clickJob =
+                mayStartSatelliteWarningDialog(contentView.context, coroutineScope, TYPE_IS_WIFI) {
+                    isAllowClick: Boolean ->
+                    if (isAllowClick) {
+                        setWifiEnabled(isChecked)
+                    } else {
+                        wifiToggle.isChecked = !isChecked
+                    }
+                }
+            return
+        }
+        setWifiEnabled(isChecked)
+    }
+
+    private fun setWifiEnabled(isEnabled: Boolean) {
+        if (internetDetailsContentController.isWifiEnabled == isEnabled) {
+            return
+        }
+        internetDetailsContentController.isWifiEnabled = isEnabled
+    }
+
+    @MainThread
+    private fun updateEthernetUI(internetContent: InternetContent) {
+        ethernetLayout.visibility = if (internetContent.hasEthernet) View.VISIBLE else View.GONE
+    }
+
+    private fun updateWifiUI(internetContent: InternetContent) {
+        if (!canConfigWifi) {
+            return
+        }
+
+        updateWifiToggle(internetContent)
+        updateConnectedWifi(internetContent)
+        updateWifiListAndSeeAll(internetContent)
+        updateWifiScanNotify(internetContent)
+    }
+
+    private fun updateMobileUI(internetContent: InternetContent) {
+        if (!internetContent.shouldUpdateMobileNetwork) {
+            return
+        }
+
+        val isNetworkConnected =
+            internetContent.activeNetworkIsCellular || internetContent.isCarrierNetworkActive
+        // 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,
+                /*msg = */ "updateMobileUI, isCarrierNetworkActive = " +
+                    internetContent.isCarrierNetworkActive,
+            )
+        }
+
+        if (
+            !internetContent.hasActiveSubIdOnDds &&
+                (!internetContent.isWifiEnabled || !internetContent.isCarrierNetworkActive)
+        ) {
+            mobileNetworkLayout.visibility = View.GONE
+            secondaryMobileNetworkLayout?.visibility = View.GONE
+            return
+        }
+
+        mobileNetworkLayout.visibility = View.VISIBLE
+        mobileDataToggle.setChecked(internetDetailsContentController.isMobileDataEnabled)
+        mobileTitleTextView.text = getMobileNetworkTitle(defaultDataSubId)
+        val summary = getMobileNetworkSummary(defaultDataSubId)
+        if (!TextUtils.isEmpty(summary)) {
+            mobileSummaryTextView.text = Html.fromHtml(summary, Html.FROM_HTML_MODE_LEGACY)
+            mobileSummaryTextView.setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
+            mobileSummaryTextView.visibility = View.VISIBLE
+        } else {
+            mobileSummaryTextView.visibility = View.GONE
+        }
+        backgroundExecutor.execute {
+            val drawable = getSignalStrengthDrawable(defaultDataSubId)
+            handler.post { signalIcon.setImageDrawable(drawable) }
+        }
+
+        mobileDataToggle.visibility = if (canConfigMobileData) View.VISIBLE else View.INVISIBLE
+        mobileToggleDivider.visibility = if (canConfigMobileData) View.VISIBLE else View.INVISIBLE
+        val primaryColor =
+            if (isNetworkConnected) R.color.connected_network_primary_color
+            else R.color.disconnected_network_primary_color
+        mobileToggleDivider.setBackgroundColor(context.getColor(primaryColor))
+
+        // Display the info for the non-DDS if it's actively being used
+        val autoSwitchNonDdsSubId: Int = internetContent.activeAutoSwitchNonDdsSubId
+
+        val nonDdsVisibility =
+            if (autoSwitchNonDdsSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) View.VISIBLE
+            else View.GONE
+
+        val secondaryRes =
+            if (isNetworkConnected) R.style.TextAppearance_InternetDialog_Secondary_Active
+            else R.style.TextAppearance_InternetDialog_Secondary
+        if (nonDdsVisibility == View.VISIBLE) {
+            // non DDS is the currently active sub, set primary visual for it
+            setNonDDSActive(autoSwitchNonDdsSubId)
+        } else {
+            mobileNetworkLayout.background = if (isNetworkConnected) backgroundOn else backgroundOff
+            mobileTitleTextView.setTextAppearance(
+                if (isNetworkConnected) R.style.TextAppearance_InternetDialog_Active
+                else R.style.TextAppearance_InternetDialog
+            )
+            mobileSummaryTextView.setTextAppearance(secondaryRes)
+        }
+
+        secondaryMobileNetworkLayout?.visibility = nonDdsVisibility
+
+        // Set airplane mode to the summary for carrier network
+        if (internetContent.isAirplaneModeEnabled) {
+            airplaneModeSummaryTextView.apply {
+                visibility = View.VISIBLE
+                text = context.getText(R.string.airplane_mode)
+                setTextAppearance(secondaryRes)
+            }
+        } else {
+            airplaneModeSummaryTextView.visibility = View.GONE
+        }
+    }
+
+    private fun setNonDDSActive(autoSwitchNonDdsSubId: Int) {
+        val stub: ViewStub = contentView.findViewById(R.id.secondary_mobile_network_stub)
+        stub.inflate()
+        secondaryMobileNetworkLayout =
+            contentView.findViewById(R.id.secondary_mobile_network_layout)
+        secondaryMobileNetworkLayout?.setOnClickListener { view: View? ->
+            this.onClickConnectedSecondarySub(view)
+        }
+        secondaryMobileNetworkLayout?.background = backgroundOn
+
+        contentView.requireViewById<TextView>(R.id.secondary_mobile_title).apply {
+            text = getMobileNetworkTitle(autoSwitchNonDdsSubId)
+            setTextAppearance(R.style.TextAppearance_InternetDialog_Active)
+        }
+
+        val summary = getMobileNetworkSummary(autoSwitchNonDdsSubId)
+        contentView.requireViewById<TextView>(R.id.secondary_mobile_summary).apply {
+            if (!TextUtils.isEmpty(summary)) {
+                text = Html.fromHtml(summary, Html.FROM_HTML_MODE_LEGACY)
+                breakStrategy = Layout.BREAK_STRATEGY_SIMPLE
+                setTextAppearance(R.style.TextAppearance_InternetDialog_Active)
+            }
+        }
+
+        val secondarySignalIcon: ImageView = contentView.requireViewById(R.id.secondary_signal_icon)
+        backgroundExecutor.execute {
+            val drawable = getSignalStrengthDrawable(autoSwitchNonDdsSubId)
+            handler.post { secondarySignalIcon.setImageDrawable(drawable) }
+        }
+
+        contentView.requireViewById<ImageView>(R.id.secondary_settings_icon).apply {
+            setColorFilter(context.getColor(R.color.connected_network_primary_color))
+        }
+
+        // set secondary visual for default data sub
+        mobileNetworkLayout.background = backgroundOff
+        mobileTitleTextView.setTextAppearance(R.style.TextAppearance_InternetDialog)
+        mobileSummaryTextView.setTextAppearance(R.style.TextAppearance_InternetDialog_Secondary)
+        signalIcon.setColorFilter(context.getColor(R.color.connected_network_secondary_color))
+    }
+
+    @MainThread
+    private fun updateWifiToggle(internetContent: InternetContent) {
+        if (wifiToggle.isChecked != internetContent.isWifiEnabled) {
+            wifiToggle.isChecked = internetContent.isWifiEnabled
+        }
+        if (internetContent.isDeviceLocked) {
+            wifiToggleTitleTextView.setTextAppearance(
+                if ((connectedWifiEntry != null)) R.style.TextAppearance_InternetDialog_Active
+                else R.style.TextAppearance_InternetDialog
+            )
+        }
+        turnWifiOnLayout.background =
+            if ((internetContent.isDeviceLocked && connectedWifiEntry != null)) backgroundOn
+            else null
+
+        if (!canChangeWifiState && wifiToggle.isEnabled) {
+            wifiToggle.isEnabled = false
+            wifiToggleTitleTextView.isEnabled = false
+            contentView.requireViewById<TextView>(R.id.wifi_toggle_summary).apply {
+                isEnabled = false
+                visibility = View.VISIBLE
+            }
+        }
+    }
+
+    @MainThread
+    private fun updateConnectedWifi(internetContent: InternetContent) {
+        if (
+            !internetContent.isWifiEnabled ||
+                connectedWifiEntry == null ||
+                internetContent.isDeviceLocked
+        ) {
+            connectedWifiListLayout.visibility = View.GONE
+            shareWifiButton.visibility = View.GONE
+            return
+        }
+        connectedWifiListLayout.visibility = View.VISIBLE
+        connectedWifiTitleTextView.text = connectedWifiEntry!!.title
+        connectedWifiSummaryTextView.text = connectedWifiEntry!!.getSummary(false)
+        connectedWifiIcon.setImageDrawable(
+            internetDetailsContentController.getInternetWifiDrawable(connectedWifiEntry!!)
+        )
+        wifiSettingsIcon.setColorFilter(context.getColor(R.color.connected_network_primary_color))
+
+        val canShareWifi =
+            internetDetailsContentController.getConfiguratorQrCodeGeneratorIntentOrNull(
+                connectedWifiEntry
+            ) != null
+        shareWifiButton.visibility = if (canShareWifi) View.VISIBLE else View.GONE
+
+        secondaryMobileNetworkLayout?.visibility = View.GONE
+    }
+
+    @MainThread
+    private fun updateWifiListAndSeeAll(internetContent: InternetContent) {
+        if (!internetContent.isWifiEnabled || internetContent.isDeviceLocked) {
+            wifiRecyclerView.visibility = View.GONE
+            seeAllLayout.visibility = View.GONE
+            return
+        }
+        val wifiListMaxCount = getWifiListMaxCount()
+        if (adapter.itemCount > wifiListMaxCount) {
+            hasMoreWifiEntries = true
+        }
+        adapter.setMaxEntriesCount(wifiListMaxCount)
+        val wifiListMinHeight = wifiNetworkHeight * wifiListMaxCount
+        if (wifiRecyclerView.minimumHeight != wifiListMinHeight) {
+            wifiRecyclerView.minimumHeight = wifiListMinHeight
+        }
+        wifiRecyclerView.visibility = View.VISIBLE
+        seeAllLayout.visibility = if (hasMoreWifiEntries) View.VISIBLE else View.INVISIBLE
+    }
+
+    @MainThread
+    private fun updateWifiScanNotify(internetContent: InternetContent) {
+        if (
+            internetContent.isWifiEnabled ||
+                !internetContent.isWifiScanEnabled ||
+                internetContent.isDeviceLocked
+        ) {
+            wifiScanNotifyLayout.visibility = View.GONE
+            return
+        }
+
+        if (TextUtils.isEmpty(wifiScanNotifyTextView.text)) {
+            val linkInfo =
+                AnnotationLinkSpan.LinkInfo(AnnotationLinkSpan.LinkInfo.DEFAULT_ANNOTATION) {
+                    view: View? ->
+                    internetDetailsContentController.launchWifiScanningSetting(view)
+                }
+            wifiScanNotifyTextView.text =
+                AnnotationLinkSpan.linkify(
+                    context.getText(R.string.wifi_scan_notify_message),
+                    linkInfo,
+                )
+            wifiScanNotifyTextView.movementMethod = LinkMovementMethod.getInstance()
+        }
+        wifiScanNotifyLayout.visibility = View.VISIBLE
+    }
+
+    @VisibleForTesting
+    @MainThread
+    internal fun getWifiListMaxCount(): Int {
+        // Use the maximum count of networks to calculate the remaining count for Wi-Fi networks.
+        var count = MAX_NETWORK_COUNT
+        if (ethernetLayout.visibility == View.VISIBLE) {
+            count -= 1
+        }
+        if (mobileNetworkLayout.visibility == View.VISIBLE) {
+            count -= 1
+        }
+
+        // If the remaining count is greater than the maximum count of the Wi-Fi network, the
+        // maximum count of the Wi-Fi network is used.
+        if (count > InternetDetailsContentController.MAX_WIFI_ENTRY_COUNT) {
+            count = InternetDetailsContentController.MAX_WIFI_ENTRY_COUNT
+        }
+        if (connectedWifiListLayout.visibility == View.VISIBLE) {
+            count -= 1
+        }
+        return count
+    }
+
+    private fun getMobileNetworkSummary(subId: Int): String {
+        return internetDetailsContentController.getMobileNetworkSummary(subId)
+    }
+
+    /** For DSDS auto data switch */
+    private fun onClickConnectedSecondarySub(view: View?) {
+        internetDetailsContentController.launchMobileNetworkSettings(view)
+    }
+
+    private fun getSignalStrengthDrawable(subId: Int): Drawable {
+        return internetDetailsContentController.getSignalStrengthDrawable(subId)
+    }
+
+    /**
+     * Unbinds all listeners and resources associated with the view. This method should be called
+     * when the view is no longer needed.
+     */
+    fun unBind() {
+        if (DEBUG) {
+            Log.d(TAG, "unBind")
+        }
+        lifecycleRegistry.currentState = Lifecycle.State.DESTROYED
+        mobileNetworkLayout.setOnClickListener(null)
+        connectedWifiListLayout.setOnClickListener(null)
+        secondaryMobileNetworkLayout?.setOnClickListener(null)
+        seeAllLayout.setOnClickListener(null)
+        wifiToggle.setOnCheckedChangeListener(null)
+        doneButton.setOnClickListener(null)
+        shareWifiButton.setOnClickListener(null)
+        airplaneModeButton.setOnClickListener(null)
+        internetDetailsContentController.onStop()
+    }
+
+    /**
+     * Update the internet details content when receiving the callback.
+     *
+     * @param shouldUpdateMobileNetwork `true` for update the mobile network layout, otherwise
+     *   `false`.
+     */
+    @VisibleForTesting
+    internal fun updateContent(shouldUpdateMobileNetwork: Boolean) {
+        backgroundExecutor.execute {
+            internetContentData.postValue(getInternetContent(shouldUpdateMobileNetwork))
+        }
+    }
+
+    private fun getInternetContent(shouldUpdateMobileNetwork: Boolean): InternetContent {
+        return InternetContent(
+            shouldUpdateMobileNetwork = shouldUpdateMobileNetwork,
+            internetDialogTitleString = getDialogTitleText(),
+            internetDialogSubTitle = getSubtitleText(),
+            activeNetworkIsCellular =
+                if (shouldUpdateMobileNetwork)
+                    internetDetailsContentController.activeNetworkIsCellular()
+                else false,
+            isCarrierNetworkActive =
+                if (shouldUpdateMobileNetwork)
+                    internetDetailsContentController.isCarrierNetworkActive()
+                else false,
+            isAirplaneModeEnabled = internetDetailsContentController.isAirplaneModeEnabled,
+            hasEthernet = internetDetailsContentController.hasEthernet(),
+            isWifiEnabled = internetDetailsContentController.isWifiEnabled,
+            hasActiveSubIdOnDds = internetDetailsContentController.hasActiveSubIdOnDds(),
+            isDeviceLocked = internetDetailsContentController.isDeviceLocked,
+            isWifiScanEnabled = internetDetailsContentController.isWifiScanEnabled(),
+            activeAutoSwitchNonDdsSubId =
+                internetDetailsContentController.getActiveAutoSwitchNonDdsSubId(),
+        )
+    }
+
+    /**
+     * Handles window focus changes. If the activity loses focus and the system UI dialog is
+     * showing, it dismisses the current alert dialog to prevent it from persisting in the
+     * background.
+     *
+     * @param dialog The internet system UI dialog whose focus state has changed.
+     * @param hasFocus True if the window has gained focus, false otherwise.
+     */
+    fun onWindowFocusChanged(dialog: SystemUIDialog, hasFocus: Boolean) {
+        if (alertDialog != null && !alertDialog!!.isShowing) {
+            if (!hasFocus && dialog.isShowing) {
+                dialog.dismiss()
+            }
+        }
+    }
+
+    private fun getDefaultCarrierName(): String? {
+        return context.getString(R.string.mobile_data_disable_message_default_carrier)
+    }
+
+    @VisibleForTesting
+    internal val internetDetailsCallback =
+        object : InternetDetailsContentController.InternetDialogCallback {
+            override fun onRefreshCarrierInfo() {
+                updateContent(shouldUpdateMobileNetwork = true)
+            }
+
+            override fun onSimStateChanged() {
+                updateContent(shouldUpdateMobileNetwork = true)
+            }
+
+            @WorkerThread
+            override fun onCapabilitiesChanged(
+                network: Network?,
+                networkCapabilities: NetworkCapabilities?,
+            ) {
+                updateContent(shouldUpdateMobileNetwork = true)
+            }
+
+            @WorkerThread
+            override fun onLost(network: Network) {
+                updateContent(shouldUpdateMobileNetwork = true)
+            }
+
+            override fun onSubscriptionsChanged(dataSubId: Int) {
+                defaultDataSubId = dataSubId
+                updateContent(shouldUpdateMobileNetwork = true)
+            }
+
+            override fun onServiceStateChanged(serviceState: ServiceState?) {
+                updateContent(shouldUpdateMobileNetwork = true)
+            }
+
+            @WorkerThread
+            override fun onDataConnectionStateChanged(state: Int, networkType: Int) {
+                updateContent(shouldUpdateMobileNetwork = true)
+            }
+
+            override fun onSignalStrengthsChanged(signalStrength: SignalStrength?) {
+                updateContent(shouldUpdateMobileNetwork = true)
+            }
+
+            override fun onUserMobileDataStateChanged(enabled: Boolean) {
+                updateContent(shouldUpdateMobileNetwork = true)
+            }
+
+            override fun onDisplayInfoChanged(telephonyDisplayInfo: TelephonyDisplayInfo?) {
+                updateContent(shouldUpdateMobileNetwork = true)
+            }
+
+            override fun onCarrierNetworkChange(active: Boolean) {
+                updateContent(shouldUpdateMobileNetwork = true)
+            }
+
+            override fun dismissDialog() {
+                if (DEBUG) {
+                    Log.d(TAG, "dismissDialog")
+                }
+                if (internetDialog != null) {
+                    internetDialog!!.dismiss()
+                    internetDialog = null
+                }
+            }
+
+            override fun onAccessPointsChanged(
+                wifiEntries: MutableList<WifiEntry>?,
+                connectedEntry: WifiEntry?,
+                ifHasMoreWifiEntries: Boolean,
+            ) {
+                // Should update the carrier network layout when it is connected under airplane
+                // mode ON.
+                val shouldUpdateCarrierNetwork =
+                    (mobileNetworkLayout.visibility == View.VISIBLE) &&
+                        internetDetailsContentController.isAirplaneModeEnabled
+                handler.post {
+                    connectedWifiEntry = connectedEntry
+                    wifiEntriesCount = wifiEntries?.size ?: 0
+                    hasMoreWifiEntries = ifHasMoreWifiEntries
+                    updateContent(shouldUpdateCarrierNetwork)
+                    adapter.setWifiEntries(wifiEntries, wifiEntriesCount)
+                    adapter.notifyDataSetChanged()
+                }
+            }
+
+            override fun onWifiScan(isScan: Boolean) {
+                setProgressBarVisible(isScan)
+            }
+        }
+
+    enum class InternetDetailsEvent(private val id: Int) : UiEventLogger.UiEventEnum {
+        @UiEvent(doc = "The Internet details became visible on the screen.")
+        INTERNET_DETAILS_VISIBLE(2071),
+        @UiEvent(doc = "The share wifi button is clicked.") SHARE_WIFI_QS_BUTTON_CLICKED(1462);
+
+        override fun getId(): Int {
+            return id
+        }
+    }
+
+    @VisibleForTesting
+    data class InternetContent(
+        val internetDialogTitleString: CharSequence,
+        val internetDialogSubTitle: CharSequence,
+        val isAirplaneModeEnabled: Boolean = false,
+        val hasEthernet: Boolean = false,
+        val shouldUpdateMobileNetwork: Boolean = false,
+        val activeNetworkIsCellular: Boolean = false,
+        val isCarrierNetworkActive: Boolean = false,
+        val isWifiEnabled: Boolean = false,
+        val hasActiveSubIdOnDds: Boolean = false,
+        val isDeviceLocked: Boolean = false,
+        val isWifiScanEnabled: Boolean = false,
+        val activeAutoSwitchNonDdsSubId: Int = SubscriptionManager.INVALID_SUBSCRIPTION_ID,
+    )
+
+    companion object {
+        private const val TAG = "InternetDetailsContent"
+        private val DEBUG: Boolean = Log.isLoggable(TAG, Log.DEBUG)
+        private const val MAX_NETWORK_COUNT = 4
+        const val CAN_CONFIG_MOBILE_DATA = "can_config_mobile_data"
+        const val CAN_CONFIG_WIFI = "can_config_wifi"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailedViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsViewModel.kt
similarity index 100%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailedViewModel.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDetailsViewModel.kt
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateLegacy.java
similarity index 89%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateLegacy.java
index 5e9deec..a418b2a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateLegacy.java
@@ -17,7 +17,7 @@
 
 import static com.android.settingslib.satellite.SatelliteDialogUtils.TYPE_IS_WIFI;
 import static com.android.systemui.Prefs.Key.QS_HAS_TURNED_OFF_MOBILE_DATA;
-import static com.android.systemui.qs.tiles.dialog.InternetDialogController.MAX_WIFI_ENTRY_COUNT;
+import static com.android.systemui.qs.tiles.dialog.InternetDetailsContentController.MAX_WIFI_ENTRY_COUNT;
 
 import android.app.AlertDialog;
 import android.content.Context;
@@ -70,6 +70,7 @@
 import com.android.systemui.animation.DialogTransitionAnimator;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.qs.flags.QsDetailedView;
 import com.android.systemui.res.R;
 import com.android.systemui.shade.ShadeDisplayAware;
 import com.android.systemui.shade.domain.interactor.ShadeDialogContextInteractor;
@@ -90,9 +91,9 @@
 /**
  * Dialog for showing mobile network, connected Wi-Fi network and Wi-Fi networks.
  */
-public class InternetDialogDelegate implements
+public class InternetDialogDelegateLegacy implements
         SystemUIDialog.Delegate,
-        InternetDialogController.InternetDialogCallback {
+        InternetDetailsContentController.InternetDialogCallback {
     private static final String TAG = "InternetDialog";
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
@@ -120,7 +121,7 @@
     @Nullable
     private AlertDialog mAlertDialog;
     private final UiEventLogger mUiEventLogger;
-    private final InternetDialogController mInternetDialogController;
+    private final InternetDetailsContentController mInternetDetailsContentController;
     private TextView mInternetDialogTitle;
     private TextView mInternetDialogSubTitle;
     private View mDivider;
@@ -184,7 +185,7 @@
 
     @AssistedFactory
     public interface Factory {
-        InternetDialogDelegate create(
+        InternetDialogDelegateLegacy create(
                 @Assisted(ABOVE_STATUS_BAR) boolean aboveStatusBar,
                 @Assisted(CAN_CONFIG_MOBILE_DATA) boolean canConfigMobileData,
                 @Assisted(CAN_CONFIG_WIFI) boolean canConfigWifi,
@@ -192,10 +193,10 @@
     }
 
     @AssistedInject
-    public InternetDialogDelegate(
+    public InternetDialogDelegateLegacy(
             @ShadeDisplayAware Context context,
             InternetDialogManager internetDialogManager,
-            InternetDialogController internetDialogController,
+            InternetDetailsContentController internetDetailsContentController,
             @Assisted(CAN_CONFIG_MOBILE_DATA) boolean canConfigMobileData,
             @Assisted(CAN_CONFIG_WIFI) boolean canConfigWifi,
             @Assisted(ABOVE_STATUS_BAR) boolean aboveStatusBar,
@@ -207,6 +208,9 @@
             KeyguardStateController keyguardStateController,
             SystemUIDialog.Factory systemUIDialogFactory,
             ShadeDialogContextInteractor shadeDialogContextInteractor) {
+        // If `QsDetailedView` is enabled, it should show the details view.
+        QsDetailedView.assertInLegacyMode();
+
         mAboveStatusBar = aboveStatusBar;
         mSystemUIDialogFactory = systemUIDialogFactory;
         mShadeDialogContextInteractor = shadeDialogContextInteractor;
@@ -218,8 +222,8 @@
         mHandler = handler;
         mBackgroundExecutor = executor;
         mInternetDialogManager = internetDialogManager;
-        mInternetDialogController = internetDialogController;
-        mDefaultDataSubId = mInternetDialogController.getDefaultDataSubscriptionId();
+        mInternetDetailsContentController = internetDetailsContentController;
+        mDefaultDataSubId = mInternetDetailsContentController.getDefaultDataSubscriptionId();
         mCanConfigMobileData = canConfigMobileData;
         mCanConfigWifi = canConfigWifi;
         mCanChangeWifiState = WifiEnterpriseRestrictionUtils.isChangeWifiStateAllowed(context);
@@ -227,7 +231,7 @@
         mCoroutineScope = coroutineScope;
         mUiEventLogger = uiEventLogger;
         mDialogTransitionAnimator = dialogTransitionAnimator;
-        mAdapter = new InternetAdapter(mInternetDialogController, coroutineScope);
+        mAdapter = new InternetAdapter(mInternetDetailsContentController, coroutineScope);
     }
 
     @Override
@@ -309,7 +313,8 @@
         setOnClickListener(dialog);
         mTurnWifiOnLayout.setBackground(null);
         mAirplaneModeButton.setVisibility(
-                mInternetDialogController.isAirplaneModeEnabled() ? View.VISIBLE : View.GONE);
+                mInternetDetailsContentController.isAirplaneModeEnabled() ? View.VISIBLE
+                        : View.GONE);
         mWifiRecyclerView.setLayoutManager(new LinearLayoutManager(context));
         mWifiRecyclerView.setAdapter(mAdapter);
 
@@ -324,7 +329,7 @@
 
         mLifecycleRegistry.setCurrentState(Lifecycle.State.RESUMED);
 
-        mInternetDialogController.onStart(this, mCanConfigWifi);
+        mInternetDetailsContentController.onStart(this, mCanConfigWifi);
         if (!mCanConfigWifi) {
             hideWifiViews();
         }
@@ -356,7 +361,7 @@
         mDoneButton.setOnClickListener(null);
         mShareWifiButton.setOnClickListener(null);
         mAirplaneModeButton.setOnClickListener(null);
-        mInternetDialogController.onStop();
+        mInternetDetailsContentController.onStop();
         mInternetDialogManager.destroyDialog();
     }
 
@@ -413,18 +418,20 @@
         internetContent.mInternetDialogSubTitle = getSubtitleText();
         if (shouldUpdateMobileNetwork) {
             internetContent.mActiveNetworkIsCellular =
-                    mInternetDialogController.activeNetworkIsCellular();
+                    mInternetDetailsContentController.activeNetworkIsCellular();
             internetContent.mIsCarrierNetworkActive =
-                    mInternetDialogController.isCarrierNetworkActive();
+                    mInternetDetailsContentController.isCarrierNetworkActive();
         }
-        internetContent.mIsAirplaneModeEnabled = mInternetDialogController.isAirplaneModeEnabled();
-        internetContent.mHasEthernet = mInternetDialogController.hasEthernet();
-        internetContent.mIsWifiEnabled = mInternetDialogController.isWifiEnabled();
-        internetContent.mHasActiveSubIdOnDds = mInternetDialogController.hasActiveSubIdOnDds();
-        internetContent.mIsDeviceLocked = mInternetDialogController.isDeviceLocked();
-        internetContent.mIsWifiScanEnabled = mInternetDialogController.isWifiScanEnabled();
+        internetContent.mIsAirplaneModeEnabled =
+                mInternetDetailsContentController.isAirplaneModeEnabled();
+        internetContent.mHasEthernet = mInternetDetailsContentController.hasEthernet();
+        internetContent.mIsWifiEnabled = mInternetDetailsContentController.isWifiEnabled();
+        internetContent.mHasActiveSubIdOnDds =
+                mInternetDetailsContentController.hasActiveSubIdOnDds();
+        internetContent.mIsDeviceLocked = mInternetDetailsContentController.isDeviceLocked();
+        internetContent.mIsWifiScanEnabled = mInternetDetailsContentController.isWifiScanEnabled();
         internetContent.mActiveAutoSwitchNonDdsSubId =
-                mInternetDialogController.getActiveAutoSwitchNonDdsSubId();
+                mInternetDetailsContentController.getActiveAutoSwitchNonDdsSubId();
         return internetContent;
     }
 
@@ -432,8 +439,8 @@
         InternetContent internetContent = new InternetContent();
         internetContent.mInternetDialogTitleString = getDialogTitleText();
         internetContent.mInternetDialogSubTitle = getSubtitleText();
-        internetContent.mIsWifiEnabled = mInternetDialogController.isWifiEnabled();
-        internetContent.mIsDeviceLocked = mInternetDialogController.isDeviceLocked();
+        internetContent.mIsWifiEnabled = mInternetDetailsContentController.isWifiEnabled();
+        internetContent.mIsDeviceLocked = mInternetDetailsContentController.isDeviceLocked();
         return internetContent;
     }
 
@@ -447,15 +454,15 @@
             if (autoSwitchNonDdsSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
                 showTurnOffAutoDataSwitchDialog(dialog, autoSwitchNonDdsSubId);
             }
-            mInternetDialogController.connectCarrierNetwork();
+            mInternetDetailsContentController.connectCarrierNetwork();
         });
         mMobileDataToggle.setOnClickListener(v -> {
             boolean isChecked = mMobileDataToggle.isChecked();
             if (!isChecked && shouldShowMobileDialog()) {
                 mMobileDataToggle.setChecked(true);
                 showTurnOffMobileDialog(dialog);
-            } else if (mInternetDialogController.isMobileDataEnabled() != isChecked) {
-                mInternetDialogController.setMobileDataEnabled(
+            } else if (mInternetDetailsContentController.isMobileDataEnabled() != isChecked) {
+                mInternetDetailsContentController.setMobileDataEnabled(
                         dialog.getContext(), mDefaultDataSubId, isChecked, false);
             }
         });
@@ -466,12 +473,13 @@
         });
         mDoneButton.setOnClickListener(v -> dialog.dismiss());
         mShareWifiButton.setOnClickListener(v -> {
-            if (mInternetDialogController.mayLaunchShareWifiSettings(mConnectedWifiEntry, v)) {
+            if (mInternetDetailsContentController.mayLaunchShareWifiSettings(mConnectedWifiEntry,
+                    v)) {
                 mUiEventLogger.log(InternetDialogEvent.SHARE_WIFI_QS_BUTTON_CLICKED);
             }
         });
         mAirplaneModeButton.setOnClickListener(v -> {
-            mInternetDialogController.setAirplaneModeDisabled();
+            mInternetDetailsContentController.setAirplaneModeDisabled();
         });
     }
 
@@ -495,10 +503,10 @@
     }
 
     private void setWifiEnable(boolean isChecked) {
-        if (mInternetDialogController.isWifiEnabled() == isChecked) {
+        if (mInternetDetailsContentController.isWifiEnabled() == isChecked) {
             return;
         }
-        mInternetDialogController.setWifiEnabled(isChecked);
+        mInternetDetailsContentController.setWifiEnabled(isChecked);
     }
 
     @MainThread
@@ -508,7 +516,7 @@
     }
 
     private void setMobileDataLayout(InternetContent internetContent) {
-        if (!internetContent.mShouldUpdateMobileNetwork && mDialog == null) {
+        if (!internetContent.mShouldUpdateMobileNetwork || mDialog == null) {
             return;
         }
         setMobileDataLayout(mDialog, internetContent);
@@ -534,7 +542,7 @@
             }
         } else {
             mMobileNetworkLayout.setVisibility(View.VISIBLE);
-            mMobileDataToggle.setChecked(mInternetDialogController.isMobileDataEnabled());
+            mMobileDataToggle.setChecked(mInternetDetailsContentController.isMobileDataEnabled());
             mMobileTitleText.setText(getMobileNetworkTitle(mDefaultDataSubId));
             String summary = getMobileNetworkSummary(mDefaultDataSubId);
             if (!TextUtils.isEmpty(summary)) {
@@ -679,10 +687,10 @@
         mConnectedWifiTitleText.setText(mConnectedWifiEntry.getTitle());
         mConnectedWifiSummaryText.setText(mConnectedWifiEntry.getSummary(false));
         mConnectedWifiIcon.setImageDrawable(
-                mInternetDialogController.getInternetWifiDrawable(mConnectedWifiEntry));
+                mInternetDetailsContentController.getInternetWifiDrawable(mConnectedWifiEntry));
         mWifiSettingsIcon.setColorFilter(
                 mDialog.getContext().getColor(R.color.connected_network_primary_color));
-        if (mInternetDialogController.getConfiguratorQrCodeGeneratorIntentOrNull(
+        if (mInternetDetailsContentController.getConfiguratorQrCodeGeneratorIntentOrNull(
                 mConnectedWifiEntry) != null) {
             mShareWifiButton.setVisibility(View.VISIBLE);
         } else {
@@ -748,7 +756,7 @@
         if (TextUtils.isEmpty(mWifiScanNotifyText.getText())) {
             final AnnotationLinkSpan.LinkInfo linkInfo = new AnnotationLinkSpan.LinkInfo(
                     AnnotationLinkSpan.LinkInfo.DEFAULT_ANNOTATION,
-                    mInternetDialogController::launchWifiScanningSetting);
+                    mInternetDetailsContentController::launchWifiScanningSetting);
             mWifiScanNotifyText.setText(AnnotationLinkSpan.linkify(
                     mDialog.getContext().getText(R.string.wifi_scan_notify_message), linkInfo));
             mWifiScanNotifyText.setMovementMethod(LinkMovementMethod.getInstance());
@@ -760,37 +768,38 @@
         if (mConnectedWifiEntry == null) {
             return;
         }
-        mInternetDialogController.launchWifiDetailsSetting(mConnectedWifiEntry.getKey(), view);
+        mInternetDetailsContentController.launchWifiDetailsSetting(mConnectedWifiEntry.getKey(),
+                view);
     }
 
     /** For DSDS auto data switch **/
     void onClickConnectedSecondarySub(View view) {
-        mInternetDialogController.launchMobileNetworkSettings(view);
+        mInternetDetailsContentController.launchMobileNetworkSettings(view);
     }
 
     void onClickSeeMoreButton(View view) {
-        mInternetDialogController.launchNetworkSetting(view);
+        mInternetDetailsContentController.launchNetworkSetting(view);
     }
 
     CharSequence getDialogTitleText() {
-        return mInternetDialogController.getDialogTitleText();
+        return mInternetDetailsContentController.getDialogTitleText();
     }
 
     @Nullable
     CharSequence getSubtitleText() {
-        return mInternetDialogController.getSubtitleText(mIsProgressBarVisible);
+        return mInternetDetailsContentController.getSubtitleText(mIsProgressBarVisible);
     }
 
     private Drawable getSignalStrengthDrawable(int subId) {
-        return mInternetDialogController.getSignalStrengthDrawable(subId);
+        return mInternetDetailsContentController.getSignalStrengthDrawable(subId);
     }
 
     CharSequence getMobileNetworkTitle(int subId) {
-        return mInternetDialogController.getMobileNetworkTitle(subId);
+        return mInternetDetailsContentController.getMobileNetworkTitle(subId);
     }
 
     String getMobileNetworkSummary(int subId) {
-        return mInternetDialogController.getMobileNetworkSummary(subId);
+        return mInternetDetailsContentController.getMobileNetworkSummary(subId);
     }
 
     private void setProgressBarVisible(boolean visible) {
@@ -810,7 +819,7 @@
         }
         boolean flag = Prefs.getBoolean(mDialog.getContext(), QS_HAS_TURNED_OFF_MOBILE_DATA,
                 false);
-        if (mInternetDialogController.isMobileDataEnabled() && !flag) {
+        if (mInternetDetailsContentController.isMobileDataEnabled() && !flag) {
             return true;
         }
         return false;
@@ -819,7 +828,8 @@
     private void showTurnOffMobileDialog(SystemUIDialog dialog) {
         Context context = dialog.getContext();
         CharSequence carrierName = getMobileNetworkTitle(mDefaultDataSubId);
-        boolean isInService = mInternetDialogController.isVoiceStateInService(mDefaultDataSubId);
+        boolean isInService = mInternetDetailsContentController.isVoiceStateInService(
+                mDefaultDataSubId);
         if (TextUtils.isEmpty(carrierName) || !isInService) {
             carrierName = context.getString(R.string.mobile_data_disable_message_default_carrier);
         }
@@ -831,7 +841,7 @@
                 .setPositiveButton(
                         com.android.internal.R.string.alert_windows_notification_turn_off_action,
                         (d, w) -> {
-                            mInternetDialogController.setMobileDataEnabled(context,
+                            mInternetDetailsContentController.setMobileDataEnabled(context,
                                     mDefaultDataSubId, false, false);
                             mMobileDataToggle.setChecked(false);
                             Prefs.putBoolean(context, QS_HAS_TURNED_OFF_MOBILE_DATA, true);
@@ -858,7 +868,7 @@
                         })
                 .setPositiveButton(R.string.auto_data_switch_dialog_positive_button,
                         (d, w) -> {
-                            mInternetDialogController
+                            mInternetDetailsContentController
                                     .setAutoDataSwitchMobileDataPolicy(subId, false);
                             if (mSecondaryMobileNetworkLayout != null) {
                                 mSecondaryMobileNetworkLayout.setVisibility(View.GONE);
@@ -938,7 +948,7 @@
             @Nullable WifiEntry connectedEntry, boolean hasMoreWifiEntries) {
         // Should update the carrier network layout when it is connected under airplane mode ON.
         boolean shouldUpdateCarrierNetwork = mMobileNetworkLayout.getVisibility() == View.VISIBLE
-                && mInternetDialogController.isAirplaneModeEnabled();
+                && mInternetDetailsContentController.isAirplaneModeEnabled();
         mHandler.post(() -> {
             mConnectedWifiEntry = connectedEntry;
             mWifiEntriesCount = wifiEntries == null ? 0 : wifiEntries.size();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogManager.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogManager.kt
index f674971..5f82e60 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogManager.kt
@@ -23,6 +23,7 @@
 import com.android.systemui.coroutines.newTracingContext
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.qs.flags.QsDetailedView
 import com.android.systemui.statusbar.phone.SystemUIDialog
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineDispatcher
@@ -32,31 +33,34 @@
 private const val TAG = "InternetDialogFactory"
 private val DEBUG = Log.isLoggable(TAG, Log.DEBUG)
 
-/** Factory to create [InternetDialogDelegate] objects. */
+/** Factory to create [InternetDialogDelegateLegacy] objects. */
 @SysUISingleton
 class InternetDialogManager
 @Inject
 constructor(
     private val dialogTransitionAnimator: DialogTransitionAnimator,
-    private val dialogFactory: InternetDialogDelegate.Factory,
+    private val dialogFactory: InternetDialogDelegateLegacy.Factory,
     @Background private val bgDispatcher: CoroutineDispatcher,
 ) {
     private lateinit var coroutineScope: CoroutineScope
+
     companion object {
         private const val INTERACTION_JANK_TAG = "internet"
         var dialog: SystemUIDialog? = null
     }
 
     /**
-     * Creates a [InternetDialogDelegate]. The dialog will be animated from [expandable] if it is
-     * not null.
+     * Creates a [InternetDialogDelegateLegacy]. The dialog will be animated from [expandable] if it
+     * is not null.
      */
     fun create(
         aboveStatusBar: Boolean,
         canConfigMobileData: Boolean,
         canConfigWifi: Boolean,
-        expandable: Expandable?
+        expandable: Expandable?,
     ) {
+        // If `QsDetailedView` is enabled, it should show the details view.
+        QsDetailedView.assertInLegacyMode()
         if (dialog != null) {
             if (DEBUG) {
                 Log.d(TAG, "InternetDialog is showing, do not create it twice.")
@@ -68,6 +72,7 @@
                 dialogFactory
                     .create(aboveStatusBar, canConfigMobileData, canConfigWifi, coroutineScope)
                     .createDialog()
+
             val controller =
                 expandable?.dialogTransitionController(
                     DialogCuj(InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN, INTERACTION_JANK_TAG)
@@ -76,10 +81,9 @@
                 dialogTransitionAnimator.show(
                     dialog!!,
                     controller,
-                    animateBackgroundBoundsChange = true
+                    animateBackgroundBoundsChange = true,
                 )
-            }
-                ?: dialog?.show()
+            } ?: dialog?.show()
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/ScreenRecordDetailsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/ScreenRecordDetailsViewModel.kt
new file mode 100644
index 0000000..42cb124
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/ScreenRecordDetailsViewModel.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.dialog
+
+import android.view.LayoutInflater
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.heightIn
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.viewinterop.AndroidView
+import com.android.systemui.plugins.qs.TileDetailsViewModel
+import com.android.systemui.res.R
+
+/** The view model used for the screen record details view in the Quick Settings */
+class ScreenRecordDetailsViewModel() : TileDetailsViewModel() {
+    @Composable
+    override fun GetContentView() {
+        // TODO(b/378514312): Finish implementing this function.
+        AndroidView(
+            modifier = Modifier.fillMaxWidth().heightIn(max = VIEW_MAX_HEIGHT),
+            factory = { context ->
+                // Inflate with the existing dialog xml layout
+                LayoutInflater.from(context).inflate(R.layout.screen_share_dialog, null)
+            },
+        )
+    }
+
+    override fun clickOnSettingsButton() {
+        // No settings button in this tile.
+    }
+
+    override fun getTitle(): String {
+        // TODO(b/388321032): Replace this string with a string in a translatable xml file,
+        return "Screen recording"
+    }
+
+    override fun getSubTitle(): String {
+        // No sub-title in this tile.
+        return ""
+    }
+
+    companion object {
+        private val VIEW_MAX_HEIGHT: Dp = 320.dp
+    }
+}
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 dfdec3b..b774643 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
@@ -19,6 +19,7 @@
 import android.content.Intent
 import android.provider.Settings
 import com.android.systemui.accessibility.data.repository.ColorCorrectionRepository
+import com.android.systemui.qs.shared.QSSettingsPackageRepository
 import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
 import com.android.systemui.qs.tiles.base.interactor.QSTileInput
 import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
@@ -32,21 +33,20 @@
 constructor(
     private val colorCorrectionRepository: ColorCorrectionRepository,
     private val qsTileIntentUserActionHandler: QSTileIntentUserInputHandler,
+    private val settingsPackageRepository: QSSettingsPackageRepository,
 ) : QSTileUserActionInteractor<ColorCorrectionTileModel> {
 
     override suspend fun handleInput(input: QSTileInput<ColorCorrectionTileModel>): Unit =
         with(input) {
             when (action) {
                 is QSTileUserAction.Click -> {
-                    colorCorrectionRepository.setIsEnabled(
-                        !data.isEnabled,
-                        user,
-                    )
+                    colorCorrectionRepository.setIsEnabled(!data.isEnabled, user)
                 }
                 is QSTileUserAction.LongClick -> {
                     qsTileIntentUserActionHandler.handle(
                         action.expandable,
                         Intent(Settings.ACTION_COLOR_CORRECTION_SETTINGS)
+                            .setPackage(settingsPackageRepository.getSettingsPackageName()),
                     )
                 }
                 is QSTileUserAction.ToggleClick -> {}
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 6ab5796..0ebb51e 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
@@ -24,6 +24,7 @@
 import com.android.systemui.animation.DialogTransitionAnimator
 import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.qs.shared.QSSettingsPackageRepository
 import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
 import com.android.systemui.qs.tiles.base.interactor.QSTileInput
 import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
@@ -46,6 +47,7 @@
     private val keyguardStateController: KeyguardStateController,
     private val dialogTransitionAnimator: DialogTransitionAnimator,
     private val activityStarter: ActivityStarter,
+    private val settingsPackageRepository: QSSettingsPackageRepository,
 ) : QSTileUserActionInteractor<FontScalingTileModel> {
 
     override suspend fun handleInput(input: QSTileInput<FontScalingTileModel>): Unit =
@@ -63,7 +65,7 @@
                                 ?.dialogTransitionController(
                                     DialogCuj(
                                         InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN,
-                                        INTERACTION_JANK_TAG
+                                        INTERACTION_JANK_TAG,
                                     )
                                 )
                                 ?.let { dialogTransitionAnimator.show(dialog, it) } ?: dialog.show()
@@ -78,7 +80,7 @@
                             /* cancelAction= */ null,
                             /* dismissShade= */ true,
                             /* afterKeyguardGone= */ true,
-                            /* deferred= */ false
+                            /* deferred= */ false,
                         )
                     }
                 }
@@ -86,6 +88,7 @@
                     qsTileIntentUserActionHandler.handle(
                         action.expandable,
                         Intent(Settings.ACTION_TEXT_READING_SETTINGS)
+                            .setPackage(settingsPackageRepository.getSettingsPackageName()),
                     )
                 }
                 is QSTileUserAction.ToggleClick -> {}
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 aa83877..f783497 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
@@ -19,6 +19,7 @@
 import android.content.Intent
 import android.provider.Settings
 import com.android.systemui.accessibility.data.repository.ColorInversionRepository
+import com.android.systemui.qs.shared.QSSettingsPackageRepository
 import com.android.systemui.qs.tiles.base.actions.QSTileIntentUserInputHandler
 import com.android.systemui.qs.tiles.base.interactor.QSTileInput
 import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
@@ -32,21 +33,20 @@
 constructor(
     private val colorInversionRepository: ColorInversionRepository,
     private val qsTileIntentUserActionHandler: QSTileIntentUserInputHandler,
+    private val settingsPackageRepository: QSSettingsPackageRepository,
 ) : QSTileUserActionInteractor<ColorInversionTileModel> {
 
     override suspend fun handleInput(input: QSTileInput<ColorInversionTileModel>): Unit =
         with(input) {
             when (action) {
                 is QSTileUserAction.Click -> {
-                    colorInversionRepository.setIsEnabled(
-                        !data.isEnabled,
-                        user,
-                    )
+                    colorInversionRepository.setIsEnabled(!data.isEnabled, user)
                 }
                 is QSTileUserAction.LongClick -> {
                     qsTileIntentUserActionHandler.handle(
                         action.expandable,
                         Intent(Settings.ACTION_COLOR_INVERSION_SETTINGS)
+                            .setPackage(settingsPackageRepository.getSettingsPackageName()),
                     )
                 }
                 is QSTileUserAction.ToggleClick -> {}
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 dc0c4dc..2bcd1d1 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
@@ -24,7 +24,6 @@
 import com.android.systemui.flags.RefactorFlagUtils
 import com.android.systemui.keyguard.KeyguardWmStateRefactor
 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. */
 object SceneContainerFlag {
@@ -36,8 +35,7 @@
         get() =
             sceneContainer() && // mainAconfigFlag
                 KeyguardWmStateRefactor.isEnabled &&
-                NotificationThrottleHun.isEnabled &&
-                PredictiveBackSysUiFlag.isEnabled
+                NotificationThrottleHun.isEnabled
 
     // NOTE: Changes should also be made in getSecondaryFlags and @EnableSceneContainer
 
@@ -49,7 +47,6 @@
         sequenceOf(
             KeyguardWmStateRefactor.token,
             NotificationThrottleHun.token,
-            PredictiveBackSysUiFlag.token,
             // NOTE: Changes should also be made in isEnabled and @EnableSceneContainer
         )
 
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneJankMonitor.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneJankMonitor.kt
new file mode 100644
index 0000000..48a49c6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneJankMonitor.kt
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.view
+
+import android.view.View
+import androidx.compose.runtime.getValue
+import com.android.compose.animation.scene.ContentKey
+import com.android.internal.jank.Cuj
+import com.android.internal.jank.Cuj.CujType
+import com.android.internal.jank.InteractionJankMonitor
+import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
+import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
+import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor
+import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.lifecycle.Hydrator
+import com.android.systemui.scene.shared.model.Scenes
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+
+/**
+ * Monitors scene transitions and reports the beginning and ending of each scene-related CUJ.
+ *
+ * This general-purpose monitor can be expanded to include other rules that respond to the beginning
+ * and/or ending of transitions and reports jank CUI markers to the [InteractionJankMonitor].
+ */
+class SceneJankMonitor
+@AssistedInject
+constructor(
+    authenticationInteractor: AuthenticationInteractor,
+    private val deviceUnlockedInteractor: DeviceUnlockedInteractor,
+    private val interactionJankMonitor: InteractionJankMonitor,
+) : ExclusiveActivatable() {
+
+    private val hydrator = Hydrator("SceneJankMonitor.hydrator")
+    private val authMethod: AuthenticationMethodModel? by
+        hydrator.hydratedStateOf(
+            traceName = "authMethod",
+            initialValue = null,
+            source = authenticationInteractor.authenticationMethod,
+        )
+
+    override suspend fun onActivated(): Nothing {
+        hydrator.activate()
+    }
+
+    /**
+     * Notifies that a transition is at its start.
+     *
+     * Should be called exactly once each time a new transition starts.
+     */
+    fun onTransitionStart(view: View, from: ContentKey, to: ContentKey, @CujType cuj: Int?) {
+        cuj.orCalculated(from, to) { nonNullCuj -> interactionJankMonitor.begin(view, nonNullCuj) }
+    }
+
+    /**
+     * Notifies that the previous transition is at its end.
+     *
+     * Should be called exactly once each time a transition ends.
+     */
+    fun onTransitionEnd(from: ContentKey, to: ContentKey, @CujType cuj: Int?) {
+        cuj.orCalculated(from, to) { nonNullCuj -> interactionJankMonitor.end(nonNullCuj) }
+    }
+
+    /**
+     * Returns this CUI marker (CUJ identifier), one that's calculated based on other state, or
+     * `null`, if no appropriate CUJ could be calculated.
+     */
+    private fun Int?.orCalculated(
+        from: ContentKey,
+        to: ContentKey,
+        ifNotNull: (nonNullCuj: Int) -> Unit,
+    ) {
+        val thisOrCalculatedCuj = this ?: calculatedCuj(from = from, to = to)
+
+        if (thisOrCalculatedCuj != null) {
+            ifNotNull(thisOrCalculatedCuj)
+        }
+    }
+
+    @CujType
+    private fun calculatedCuj(from: ContentKey, to: ContentKey): Int? {
+        val isDeviceUnlocked = deviceUnlockedInteractor.deviceUnlockStatus.value.isUnlocked
+        return when {
+            to == Scenes.Bouncer ->
+                when (authMethod) {
+                    is AuthenticationMethodModel.Pin,
+                    is AuthenticationMethodModel.Sim -> Cuj.CUJ_LOCKSCREEN_PIN_APPEAR
+                    is AuthenticationMethodModel.Pattern -> Cuj.CUJ_LOCKSCREEN_PATTERN_APPEAR
+                    is AuthenticationMethodModel.Password -> Cuj.CUJ_LOCKSCREEN_PASSWORD_APPEAR
+                    is AuthenticationMethodModel.None -> null
+                    null -> null
+                }
+            from == Scenes.Bouncer && isDeviceUnlocked ->
+                when (authMethod) {
+                    is AuthenticationMethodModel.Pin,
+                    is AuthenticationMethodModel.Sim -> Cuj.CUJ_LOCKSCREEN_PIN_DISAPPEAR
+                    is AuthenticationMethodModel.Pattern -> Cuj.CUJ_LOCKSCREEN_PATTERN_DISAPPEAR
+                    is AuthenticationMethodModel.Password -> Cuj.CUJ_LOCKSCREEN_PASSWORD_DISAPPEAR
+                    is AuthenticationMethodModel.None -> null
+                    null -> null
+                }
+            else -> null
+        }
+    }
+
+    @AssistedFactory
+    interface Factory {
+        fun create(): SceneJankMonitor
+    }
+}
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 c459068..b8da227 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
@@ -34,6 +34,7 @@
         layoutInsetController: LayoutInsetsController,
         sceneDataSourceDelegator: SceneDataSourceDelegator,
         qsSceneAdapter: Provider<QSSceneAdapter>,
+        sceneJankMonitorFactory: SceneJankMonitor.Factory,
     ) {
         setLayoutInsetsController(layoutInsetController)
         SceneWindowRootViewBinder.bind(
@@ -52,6 +53,7 @@
             },
             dataSourceDelegator = sceneDataSourceDelegator,
             qsSceneAdapter = qsSceneAdapter,
+            sceneJankMonitorFactory = sceneJankMonitorFactory,
         )
     }
 
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 f7061d9..7da007c 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
@@ -74,6 +74,7 @@
         onVisibilityChangedInternal: (isVisible: Boolean) -> Unit,
         dataSourceDelegator: SceneDataSourceDelegator,
         qsSceneAdapter: Provider<QSSceneAdapter>,
+        sceneJankMonitorFactory: SceneJankMonitor.Factory,
     ) {
         val unsortedSceneByKey: Map<SceneKey, Scene> = scenes.associateBy { scene -> scene.key }
         val sortedSceneByKey: Map<SceneKey, Scene> =
@@ -133,6 +134,7 @@
                                 dataSourceDelegator = dataSourceDelegator,
                                 qsSceneAdapter = qsSceneAdapter,
                                 containerConfig = containerConfig,
+                                sceneJankMonitorFactory = sceneJankMonitorFactory,
                             )
                             .also { it.id = R.id.scene_container_root_composable }
                     )
@@ -169,6 +171,7 @@
         dataSourceDelegator: SceneDataSourceDelegator,
         qsSceneAdapter: Provider<QSSceneAdapter>,
         containerConfig: SceneContainerConfig,
+        sceneJankMonitorFactory: SceneJankMonitor.Factory,
     ): View {
         return ComposeView(context).apply {
             setContent {
@@ -185,6 +188,7 @@
                             sceneTransitions = containerConfig.transitions,
                             dataSourceDelegator = dataSourceDelegator,
                             qsSceneAdapter = qsSceneAdapter,
+                            sceneJankMonitorFactory = sceneJankMonitorFactory,
                         )
                     }
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegate.kt
index 1da4c1d..7ec523b 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionDialogDelegate.kt
@@ -15,34 +15,16 @@
  */
 package com.android.systemui.screenrecord
 
-import android.annotation.SuppressLint
-import android.app.Activity
-import android.app.PendingIntent
 import android.content.Context
-import android.content.Intent
 import android.hardware.display.DisplayManager
 import android.os.Bundle
-import android.os.Handler
-import android.os.Looper
-import android.os.ResultReceiver
 import android.os.UserHandle
-import android.view.Display
-import android.view.MotionEvent.ACTION_MOVE
 import android.view.View
-import android.view.accessibility.AccessibilityNodeInfo
-import android.widget.AdapterView
-import android.widget.ArrayAdapter
-import android.widget.Spinner
-import android.widget.Switch
-import androidx.annotation.LayoutRes
 import androidx.annotation.StyleRes
 import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.mediaprojection.MediaProjectionCaptureTarget
 import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
-import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorActivity
 import com.android.systemui.mediaprojection.permission.BaseMediaProjectionPermissionDialogDelegate
 import com.android.systemui.mediaprojection.permission.BaseMediaProjectionPermissionViewBinder
-import com.android.systemui.mediaprojection.permission.ENTIRE_SCREEN
 import com.android.systemui.mediaprojection.permission.SINGLE_APP
 import com.android.systemui.mediaprojection.permission.ScreenShareMode
 import com.android.systemui.plugins.ActivityStarter
@@ -115,17 +97,17 @@
         ): ScreenRecordPermissionDialogDelegate
     }
 
-    private lateinit var tapsSwitch: Switch
-    private lateinit var audioSwitch: Switch
-    private lateinit var options: Spinner
-
     override fun createViewBinder(): BaseMediaProjectionPermissionViewBinder {
         return ScreenRecordPermissionViewBinder(
+            hostUserHandle,
             hostUid,
             mediaProjectionMetricsLogger,
             defaultSelectedMode,
             displayManager,
-            dialog,
+            controller,
+            activityStarter,
+            userContextProvider,
+            onStartRecordingClicked,
         )
     }
 
@@ -138,142 +120,11 @@
         setDialogTitle(R.string.screenrecord_permission_dialog_title)
         dialog.setTitle(R.string.screenrecord_title)
         setStartButtonOnClickListener { v: View? ->
-            onStartRecordingClicked?.run()
-            val selectedScreenShareOption = getSelectedScreenShareOption()
-            if (selectedScreenShareOption.mode == ENTIRE_SCREEN) {
-                requestScreenCapture(/* captureTarget= */ null, selectedScreenShareOption.displayId)
-            }
-            if (selectedScreenShareOption.mode == SINGLE_APP) {
-                val intent = Intent(dialog.context, MediaProjectionAppSelectorActivity::class.java)
-                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
-
-                // We can't start activity for result here so we use result receiver to get
-                // the selected target to capture
-                intent.putExtra(
-                    MediaProjectionAppSelectorActivity.EXTRA_CAPTURE_REGION_RESULT_RECEIVER,
-                    CaptureTargetResultReceiver(),
-                )
-
-                intent.putExtra(
-                    MediaProjectionAppSelectorActivity.EXTRA_HOST_APP_USER_HANDLE,
-                    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)
-            }
+            val screenRecordViewBinder: ScreenRecordPermissionViewBinder? =
+                viewBinder as ScreenRecordPermissionViewBinder?
+            screenRecordViewBinder?.startButtonOnClicked()
             dialog.dismiss()
         }
         setCancelButtonOnClickListener { dialog.dismiss() }
-        initRecordOptionsView()
-    }
-
-    @LayoutRes override fun getOptionsViewLayoutId(): Int = R.layout.screen_record_options
-
-    @SuppressLint("ClickableViewAccessibility")
-    private fun initRecordOptionsView() {
-        // TODO(b/378514312): Move this function to ScreenRecordPermissionViewBinder
-        audioSwitch = dialog.requireViewById(R.id.screenrecord_audio_switch)
-        tapsSwitch = dialog.requireViewById(R.id.screenrecord_taps_switch)
-
-        // Add these listeners so that the switch only responds to movement
-        // within its target region, to meet accessibility requirements
-        audioSwitch.setOnTouchListener { _, event -> event.action == ACTION_MOVE }
-        tapsSwitch.setOnTouchListener { _, event -> event.action == ACTION_MOVE }
-
-        options = dialog.requireViewById(R.id.screen_recording_options)
-        val a: ArrayAdapter<*> =
-            ScreenRecordingAdapter(
-                dialog.context,
-                android.R.layout.simple_spinner_dropdown_item,
-                MODES,
-            )
-        a.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
-        options.adapter = a
-        options.setOnItemClickListenerInt { _: AdapterView<*>?, _: View?, _: Int, _: Long ->
-            audioSwitch.isChecked = true
-        }
-
-        // disable redundant Touch & Hold accessibility action for Switch Access
-        options.accessibilityDelegate =
-            object : View.AccessibilityDelegate() {
-                override fun onInitializeAccessibilityNodeInfo(
-                    host: View,
-                    info: AccessibilityNodeInfo,
-                ) {
-                    info.removeAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK)
-                    super.onInitializeAccessibilityNodeInfo(host, info)
-                }
-            }
-        options.isLongClickable = false
-    }
-
-    /**
-     * Starts screen capture after some countdown
-     *
-     * @param captureTarget target to capture (could be e.g. a task) or null to record the whole
-     *   screen
-     */
-    private fun requestScreenCapture(
-        captureTarget: MediaProjectionCaptureTarget?,
-        displayId: Int = Display.DEFAULT_DISPLAY,
-    ) {
-        val userContext = userContextProvider.userContext
-        val showTaps = getSelectedScreenShareOption().mode != SINGLE_APP && tapsSwitch.isChecked
-        val audioMode =
-            if (audioSwitch.isChecked) options.selectedItem as ScreenRecordingAudioSource
-            else ScreenRecordingAudioSource.NONE
-        val startIntent =
-            PendingIntent.getForegroundService(
-                userContext,
-                RecordingService.REQUEST_CODE,
-                RecordingService.getStartIntent(
-                    userContext,
-                    Activity.RESULT_OK,
-                    audioMode.ordinal,
-                    showTaps,
-                    displayId,
-                    captureTarget,
-                ),
-                PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
-            )
-        val stopIntent =
-            PendingIntent.getService(
-                userContext,
-                RecordingService.REQUEST_CODE,
-                RecordingService.getStopIntent(userContext),
-                PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
-            )
-        controller.startCountdown(DELAY_MS, INTERVAL_MS, startIntent, stopIntent)
-    }
-
-    private inner class CaptureTargetResultReceiver :
-        ResultReceiver(Handler(Looper.getMainLooper())) {
-        override fun onReceiveResult(resultCode: Int, resultData: Bundle) {
-            if (resultCode == Activity.RESULT_OK) {
-                val captureTarget =
-                    resultData.getParcelable(
-                        MediaProjectionAppSelectorActivity.KEY_CAPTURE_TARGET,
-                        MediaProjectionCaptureTarget::class.java,
-                    )
-
-                // Start recording of the selected target
-                requestScreenCapture(captureTarget)
-            }
-        }
-    }
-
-    companion object {
-        private val MODES =
-            listOf(
-                ScreenRecordingAudioSource.INTERNAL,
-                ScreenRecordingAudioSource.MIC,
-                ScreenRecordingAudioSource.MIC_AND_INTERNAL,
-            )
-        private const val DELAY_MS: Long = 3000
-        private const val INTERVAL_MS: Long = 1000
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionViewBinder.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionViewBinder.kt
index 88f373e..9fcb3df 100644
--- a/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenrecord/ScreenRecordPermissionViewBinder.kt
@@ -17,27 +17,49 @@
 package com.android.systemui.screenrecord
 
 import android.annotation.SuppressLint
-import android.app.AlertDialog
+import android.app.Activity
+import android.app.PendingIntent
+import android.content.Intent
 import android.hardware.display.DisplayManager
 import android.os.Build
+import android.os.Bundle
+import android.os.Handler
+import android.os.Looper
+import android.os.ResultReceiver
+import android.os.UserHandle
 import android.view.Display
+import android.view.MotionEvent.ACTION_MOVE
 import android.view.View
 import android.view.View.GONE
 import android.view.View.VISIBLE
+import android.view.accessibility.AccessibilityNodeInfo
+import android.widget.AdapterView
+import android.widget.ArrayAdapter
+import android.widget.Spinner
+import android.widget.Switch
+import androidx.annotation.LayoutRes
+import com.android.systemui.mediaprojection.MediaProjectionCaptureTarget
 import com.android.systemui.mediaprojection.MediaProjectionMetricsLogger
+import com.android.systemui.mediaprojection.appselector.MediaProjectionAppSelectorActivity
 import com.android.systemui.mediaprojection.permission.BaseMediaProjectionPermissionViewBinder
 import com.android.systemui.mediaprojection.permission.ENTIRE_SCREEN
 import com.android.systemui.mediaprojection.permission.SINGLE_APP
 import com.android.systemui.mediaprojection.permission.ScreenShareMode
 import com.android.systemui.mediaprojection.permission.ScreenShareOption
+import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.res.R
+import com.android.systemui.settings.UserContextProvider
 
 class ScreenRecordPermissionViewBinder(
-    hostUid: Int,
+    private val hostUserHandle: UserHandle,
+    private val hostUid: Int,
     mediaProjectionMetricsLogger: MediaProjectionMetricsLogger,
     @ScreenShareMode defaultSelectedMode: Int,
     displayManager: DisplayManager,
-    private val dialog: AlertDialog,
+    private val controller: RecordingController,
+    private val activityStarter: ActivityStarter,
+    private val userContextProvider: UserContextProvider,
+    private val onStartRecordingClicked: Runnable?,
 ) :
     BaseMediaProjectionPermissionViewBinder(
         createOptionList(displayManager),
@@ -45,23 +67,93 @@
         hostUid = hostUid,
         mediaProjectionMetricsLogger,
         defaultSelectedMode,
-        dialog,
     ) {
+    private lateinit var tapsSwitch: Switch
+    private lateinit var audioSwitch: Switch
     private lateinit var tapsView: View
+    private lateinit var options: Spinner
 
-    override fun bind() {
-        super.bind()
+    override fun bind(view: View) {
+        super.bind(view)
         initRecordOptionsView()
+        setStartButtonOnClickListener { startButtonOnClicked() }
+    }
+
+    fun startButtonOnClicked() {
+        onStartRecordingClicked?.run()
+        if (selectedScreenShareOption.mode == ENTIRE_SCREEN) {
+            requestScreenCapture(
+                captureTarget = null,
+                displayId = selectedScreenShareOption.displayId,
+            )
+        }
+        if (selectedScreenShareOption.mode == SINGLE_APP) {
+            val intent =
+                Intent(containerView.context, MediaProjectionAppSelectorActivity::class.java)
+            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+
+            // We can't start activity for result here so we use result receiver to get
+            // the selected target to capture
+            intent.putExtra(
+                MediaProjectionAppSelectorActivity.EXTRA_CAPTURE_REGION_RESULT_RECEIVER,
+                CaptureTargetResultReceiver(),
+            )
+
+            intent.putExtra(
+                MediaProjectionAppSelectorActivity.EXTRA_HOST_APP_USER_HANDLE,
+                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)
+        }
     }
 
     @SuppressLint("ClickableViewAccessibility")
     private fun initRecordOptionsView() {
-        tapsView = dialog.requireViewById(R.id.show_taps)
+        audioSwitch = containerView.requireViewById(R.id.screenrecord_audio_switch)
+        tapsSwitch = containerView.requireViewById(R.id.screenrecord_taps_switch)
+
+        tapsView = containerView.requireViewById(R.id.show_taps)
         updateTapsViewVisibility()
+
+        // Add these listeners so that the switch only responds to movement
+        // within its target region, to meet accessibility requirements
+        audioSwitch.setOnTouchListener { _, event -> event.action == ACTION_MOVE }
+        tapsSwitch.setOnTouchListener { _, event -> event.action == ACTION_MOVE }
+
+        options = containerView.requireViewById(R.id.screen_recording_options)
+        val a: ArrayAdapter<*> =
+            ScreenRecordingAdapter(
+                containerView.context,
+                android.R.layout.simple_spinner_dropdown_item,
+                MODES,
+            )
+        a.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
+        options.adapter = a
+        options.setOnItemClickListenerInt { _: AdapterView<*>?, _: View?, _: Int, _: Long ->
+            audioSwitch.isChecked = true
+        }
+
+        // disable redundant Touch & Hold accessibility action for Switch Access
+        options.accessibilityDelegate =
+            object : View.AccessibilityDelegate() {
+                override fun onInitializeAccessibilityNodeInfo(
+                    host: View,
+                    info: AccessibilityNodeInfo,
+                ) {
+                    info.removeAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK)
+                    super.onInitializeAccessibilityNodeInfo(host, info)
+                }
+            }
+        options.isLongClickable = false
     }
 
-    override fun onItemSelected(pos: Int) {
-        super.onItemSelected(pos)
+    override fun onItemSelected(adapterView: AdapterView<*>?, view: View, pos: Int, id: Long) {
+        super.onItemSelected(adapterView, view, pos, id)
         updateTapsViewVisibility()
     }
 
@@ -69,7 +161,73 @@
         tapsView.visibility = if (selectedScreenShareOption.mode == SINGLE_APP) GONE else VISIBLE
     }
 
+    @LayoutRes override fun getOptionsViewLayoutId(): Int = R.layout.screen_record_options
+
+    /**
+     * Starts screen capture after some countdown
+     *
+     * @param captureTarget target to capture (could be e.g. a task) or null to record the whole
+     *   screen
+     */
+    private fun requestScreenCapture(
+        captureTarget: MediaProjectionCaptureTarget?,
+        displayId: Int = Display.DEFAULT_DISPLAY,
+    ) {
+        val userContext = userContextProvider.userContext
+        val showTaps = selectedScreenShareOption.mode != SINGLE_APP && tapsSwitch.isChecked
+        val audioMode =
+            if (audioSwitch.isChecked) options.selectedItem as ScreenRecordingAudioSource
+            else ScreenRecordingAudioSource.NONE
+        val startIntent =
+            PendingIntent.getForegroundService(
+                userContext,
+                RecordingService.REQUEST_CODE,
+                RecordingService.getStartIntent(
+                    userContext,
+                    Activity.RESULT_OK,
+                    audioMode.ordinal,
+                    showTaps,
+                    displayId,
+                    captureTarget,
+                ),
+                PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
+            )
+        val stopIntent =
+            PendingIntent.getService(
+                userContext,
+                RecordingService.REQUEST_CODE,
+                RecordingService.getStopIntent(userContext),
+                PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
+            )
+        controller.startCountdown(DELAY_MS, INTERVAL_MS, startIntent, stopIntent)
+    }
+
+    private inner class CaptureTargetResultReceiver :
+        ResultReceiver(Handler(Looper.getMainLooper())) {
+        override fun onReceiveResult(resultCode: Int, resultData: Bundle) {
+            if (resultCode == Activity.RESULT_OK) {
+                val captureTarget =
+                    resultData.getParcelable(
+                        MediaProjectionAppSelectorActivity.KEY_CAPTURE_TARGET,
+                        MediaProjectionCaptureTarget::class.java,
+                    )
+
+                // Start recording of the selected target
+                requestScreenCapture(captureTarget)
+            }
+        }
+    }
+
     companion object {
+        private val MODES =
+            listOf(
+                ScreenRecordingAudioSource.INTERNAL,
+                ScreenRecordingAudioSource.MIC,
+                ScreenRecordingAudioSource.MIC_AND_INTERNAL,
+            )
+
+        private const val DELAY_MS: Long = 3000
+        private const val INTERVAL_MS: Long = 1000
 
         private val RECORDABLE_DISPLAY_TYPES =
             intArrayOf(
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsProvider.kt b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsProvider.kt
index 1c232e9..3696b13 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionsProvider.kt
@@ -22,7 +22,6 @@
 import android.util.Log
 import androidx.appcompat.content.res.AppCompatResources
 import com.android.internal.logging.UiEventLogger
-import com.android.systemui.Flags.screenshotContextUrl
 import com.android.systemui.log.DebugLogger.debugLog
 import com.android.systemui.res.R
 import com.android.systemui.screenshot.ActionIntentCreator.createEdit
@@ -33,6 +32,7 @@
 import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_SHARE_TAPPED
 import com.android.systemui.screenshot.ui.viewmodel.ActionButtonAppearance
 import com.android.systemui.screenshot.ui.viewmodel.PreviewAction
+import com.android.systemui.shared.Flags.screenshotContextUrl
 import dagger.assisted.Assisted
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
diff --git a/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java b/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java
index 10ac2cf..0650f86 100644
--- a/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/scrim/ScrimDrawable.java
@@ -16,8 +16,6 @@
 
 package com.android.systemui.scrim;
 
-import static com.android.systemui.Flags.notificationShadeBlur;
-
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ValueAnimator;
@@ -216,7 +214,7 @@
     public void draw(@NonNull Canvas canvas) {
         mPaint.setColor(mMainColor);
         mPaint.setAlpha(mAlpha);
-        if (notificationShadeBlur() || WindowBlurFlag.isEnabled()) {
+        if (WindowBlurFlag.isEnabled()) {
             // TODO (b/381263600), wire this at ScrimController, move it to PrimaryBouncerTransition
             mPaint.setAlpha((int) (0.5f * mAlpha));
         }
diff --git a/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java b/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java
index 0f80e74..03a8d17 100644
--- a/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java
+++ b/packages/SystemUI/src/com/android/systemui/scrim/ScrimView.java
@@ -16,8 +16,6 @@
 
 package com.android.systemui.scrim;
 
-import static com.android.systemui.Flags.notificationShadeBlur;
-
 import static java.lang.Float.isNaN;
 
 import android.annotation.NonNull;
@@ -44,6 +42,7 @@
 import com.android.systemui.res.R;
 import com.android.systemui.shade.TouchLogger;
 import com.android.systemui.util.LargeScreenUtils;
+import com.android.systemui.window.flag.WindowBlurFlag;
 
 import java.util.concurrent.Executor;
 
@@ -253,7 +252,7 @@
             if (mBlendWithMainColor) {
                 mainTinted = ColorUtils.blendARGB(mColors.getMainColor(), mTintColor, tintAmount);
             }
-            if (notificationShadeBlur()) {
+            if (WindowBlurFlag.isEnabled()) {
                 int layerAbove = ColorUtils.setAlphaComponent(
                         getResources().getColor(R.color.shade_panel, null),
                         (int) (0.4f * 255));
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
index e1631cc..bbb13d5 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTracker.kt
@@ -61,9 +61,18 @@
     /** Callback for notifying of changes. */
     @WeaklyReferencedCallback
     interface Callback {
-        /** Notifies that the current user will be changed. */
+        /**
+         * Same as {@link onBeforeUserSwitching(Int, Runnable)} but the callback will be called
+         * automatically after the completion of this method.
+         */
         fun onBeforeUserSwitching(newUser: Int) {}
 
+        /** Notifies that the current user will be changed. */
+        fun onBeforeUserSwitching(newUser: Int, resultCallback: Runnable) {
+            onBeforeUserSwitching(newUser)
+            resultCallback.run()
+        }
+
         /**
          * Same as {@link onUserChanging(Int, Context, Runnable)} but the callback will be called
          * automatically after the completion of this method.
diff --git a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
index b7a3aed..42d8363 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/UserTrackerImpl.kt
@@ -196,8 +196,9 @@
     private fun registerUserSwitchObserver() {
         iActivityManager.registerUserSwitchObserver(
             object : UserSwitchObserver() {
-                override fun onBeforeUserSwitching(newUserId: Int) {
+                override fun onBeforeUserSwitching(newUserId: Int, reply: IRemoteCallback?) {
                     handleBeforeUserSwitching(newUserId)
+                    reply?.sendResult(null)
                 }
 
                 override fun onUserSwitching(newUserId: Int, reply: IRemoteCallback?) {
@@ -236,8 +237,7 @@
         setUserIdInternal(newUserId)
 
         notifySubscribers { callback, resultCallback ->
-                callback.onBeforeUserSwitching(newUserId)
-                resultCallback.run()
+                callback.onBeforeUserSwitching(newUserId, resultCallback)
             }
             .await()
     }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index e168025..c4306d3 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -49,7 +49,6 @@
 import android.animation.ValueAnimator;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.content.ContentResolver;
 import android.content.res.Resources;
 import android.graphics.Color;
 import android.graphics.Insets;
@@ -58,28 +57,23 @@
 import android.graphics.RenderEffect;
 import android.graphics.Shader;
 import android.os.Bundle;
-import android.os.Handler;
 import android.os.Trace;
-import android.os.UserManager;
 import android.util.IndentingPrintWriter;
 import android.util.Log;
 import android.util.MathUtils;
 import android.view.HapticFeedbackConstants;
 import android.view.InputDevice;
-import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
 import android.view.View;
 import android.view.View.AccessibilityDelegate;
 import android.view.ViewConfiguration;
 import android.view.ViewPropertyAnimator;
-import android.view.ViewStub;
 import android.view.ViewTreeObserver;
 import android.view.WindowInsets;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityNodeInfo;
-import android.view.animation.Interpolator;
 
 import com.android.app.animation.Interpolators;
 import com.android.internal.annotations.VisibleForTesting;
@@ -105,19 +99,17 @@
 import com.android.systemui.doze.DozeLog;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.dump.DumpsysTableLogger;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
 import com.android.systemui.fragments.FragmentService;
 import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
 import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
-import com.android.systemui.keyguard.domain.interactor.NaturalScrollingSettingObserver;
 import com.android.systemui.keyguard.shared.model.ClockSize;
 import com.android.systemui.keyguard.shared.model.Edge;
 import com.android.systemui.keyguard.shared.model.TransitionState;
 import com.android.systemui.keyguard.shared.model.TransitionStep;
 import com.android.systemui.keyguard.ui.binder.KeyguardLongPressViewBinder;
+import com.android.systemui.keyguard.ui.transitions.BlurConfig;
 import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel;
 import com.android.systemui.keyguard.ui.viewmodel.KeyguardTouchHandlingViewModel;
 import com.android.systemui.media.controls.domain.pipeline.MediaDataManager;
@@ -162,7 +154,6 @@
 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.footer.shared.FooterViewRefactor;
 import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
 import com.android.systemui.statusbar.notification.headsup.HeadsUpTouchHelper;
 import com.android.systemui.statusbar.notification.headsup.OnHeadsUpChangedListener;
@@ -174,10 +165,8 @@
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
 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.StackStateAnimator;
 import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor;
-import com.android.systemui.statusbar.phone.BounceInterpolator;
 import com.android.systemui.statusbar.phone.CentralSurfaces;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
@@ -254,7 +243,7 @@
      * Whether the Shade should animate to reflect Back gesture progress.
      * To minimize latency at runtime, we cache this, else we'd be reading it every time
      * updateQsExpansion() is called... and it's called very often.
-     *
+     * <p>
      * Whenever we change this flag, SysUI is restarted, so it's never going to be "stale".
      */
 
@@ -285,8 +274,6 @@
     private final ConfigurationController mConfigurationController;
     private final Provider<FlingAnimationUtils.Builder> mFlingAnimationUtilsBuilder;
     private final NotificationStackScrollLayoutController mNotificationStackScrollLayoutController;
-    private final LayoutInflater mLayoutInflater;
-    private final FeatureFlags mFeatureFlags;
     private final AccessibilityManager mAccessibilityManager;
     private final NotificationWakeUpCoordinator mWakeUpCoordinator;
     private final PulseExpansionHandler mPulseExpansionHandler;
@@ -311,7 +298,6 @@
     private final DozeLog mDozeLog;
     /** Whether or not the NotificationPanelView can be expanded or collapsed with a drag. */
     private final boolean mNotificationsDragEnabled;
-    private final Interpolator mBounceInterpolator;
     private final NotificationShadeWindowController mNotificationShadeWindowController;
     private final ShadeExpansionStateManager mShadeExpansionStateManager;
     private final ShadeRepository mShadeRepository;
@@ -321,7 +307,6 @@
     private final NotificationGutsManager mGutsManager;
     private final AlternateBouncerInteractor mAlternateBouncerInteractor;
     private final QuickSettingsControllerImpl mQsController;
-    private final NaturalScrollingSettingObserver mNaturalScrollingSettingObserver;
     private final TouchHandler mTouchHandler = new TouchHandler();
 
     private long mDownTime;
@@ -436,7 +421,6 @@
                     mPanelAlphaAnimator.getProperty(), Interpolators.ALPHA_IN);
 
     private final CommandQueue mCommandQueue;
-    private final UserManager mUserManager;
     private final MediaDataManager mMediaDataManager;
     @PanelState
     private int mCurrentPanelState = STATE_CLOSED;
@@ -462,7 +446,6 @@
     private boolean mIsGestureNavigation;
     private int mOldLayoutDirection;
 
-    private final ContentResolver mContentResolver;
     private float mMinFraction;
 
     private final KeyguardMediaController mKeyguardMediaController;
@@ -475,7 +458,6 @@
     private int mSplitShadeScrimTransitionDistance;
 
     private final NotificationListContainer mNotificationListContainer;
-    private final NotificationStackSizeCalculator mNotificationStackSizeCalculator;
     private final NPVCDownEventState.Buffer mLastDownEvents;
     private final KeyguardClockInteractor mKeyguardClockInteractor;
     private float mMinExpandHeight;
@@ -530,8 +512,6 @@
     private final KeyguardInteractor mKeyguardInteractor;
     private final PowerInteractor mPowerInteractor;
     private final CoroutineDispatcher mMainDispatcher;
-    private boolean mIsAnyMultiShadeExpanded;
-    private boolean mForceFlingAnimationForTest = false;
     private final SplitShadeStateController mSplitShadeStateController;
     private final Runnable mFlingCollapseRunnable = () -> fling(0, false /* expand */,
             mNextCollapseSpeedUpFactor, false /* expandBecauseOfFalsing */);
@@ -550,9 +530,6 @@
 
     @Inject
     public NotificationPanelViewController(NotificationPanelView view,
-            @Main Handler handler,
-            @ShadeDisplayAware LayoutInflater layoutInflater,
-            FeatureFlags featureFlags,
             NotificationWakeUpCoordinator coordinator,
             PulseExpansionHandler pulseExpansionHandler,
             DynamicPrivacyController dynamicPrivacyController,
@@ -584,7 +561,6 @@
             KeyguardStatusBarViewComponent.Factory keyguardStatusBarViewComponentFactory,
             LockscreenShadeTransitionController lockscreenShadeTransitionController,
             ScrimController scrimController,
-            UserManager userManager,
             MediaDataManager mediaDataManager,
             NotificationShadeDepthController notificationShadeDepthController,
             AmbientState ambientState,
@@ -595,7 +571,6 @@
             QuickSettingsControllerImpl quickSettingsController,
             FragmentService fragmentService,
             IStatusBarService statusBarService,
-            ContentResolver contentResolver,
             ShadeHeaderController shadeHeaderController,
             ScreenOffAnimationController screenOffAnimationController,
             LockscreenGestureLogger lockscreenGestureLogger,
@@ -606,7 +581,6 @@
             KeyguardUnlockAnimationController keyguardUnlockAnimationController,
             KeyguardIndicationController keyguardIndicationController,
             NotificationListContainer notificationListContainer,
-            NotificationStackSizeCalculator notificationStackSizeCalculator,
             UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
             SystemClock systemClock,
             KeyguardClockInteractor keyguardClockInteractor,
@@ -625,7 +599,6 @@
             SplitShadeStateController splitShadeStateController,
             PowerInteractor powerInteractor,
             KeyguardClockPositionAlgorithm keyguardClockPositionAlgorithm,
-            NaturalScrollingSettingObserver naturalScrollingSettingObserver,
             MSDLPlayer msdlPlayer,
             BrightnessMirrorShowingInteractor brightnessMirrorShowingInteractor) {
         SceneContainerFlag.assertInLegacyMode();
@@ -651,7 +624,6 @@
         mKeyguardInteractor = keyguardInteractor;
         mPowerInteractor = powerInteractor;
         mClockPositionAlgorithm = keyguardClockPositionAlgorithm;
-        mNaturalScrollingSettingObserver = naturalScrollingSettingObserver;
         mView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
             @Override
             public void onViewAttachedToWindow(View v) {
@@ -691,7 +663,6 @@
                 .setY2(0.84f)
                 .build();
         mLatencyTracker = latencyTracker;
-        mBounceInterpolator = new BounceInterpolator();
         mFalsingManager = falsingManager;
         mDozeLog = dozeLog;
         mNotificationsDragEnabled = mResources.getBoolean(
@@ -708,13 +679,11 @@
         mMediaHierarchyManager = mediaHierarchyManager;
         mNotificationsQSContainerController = notificationsQSContainerController;
         mNotificationListContainer = notificationListContainer;
-        mNotificationStackSizeCalculator = notificationStackSizeCalculator;
         mNavigationBarController = navigationBarController;
         mNotificationsQSContainerController.init();
         mNotificationStackScrollLayoutController = notificationStackScrollLayoutController;
         mKeyguardStatusBarViewComponentFactory = keyguardStatusBarViewComponentFactory;
         mDepthController = notificationShadeDepthController;
-        mContentResolver = contentResolver;
         mFragmentService = fragmentService;
         mStatusBarService = statusBarService;
         mSplitShadeStateController = splitShadeStateController;
@@ -722,8 +691,6 @@
                 mSplitShadeStateController.shouldUseSplitNotificationShade(mResources);
         mView.setWillNotDraw(!DEBUG_DRAWABLE);
         mShadeHeaderController = shadeHeaderController;
-        mLayoutInflater = layoutInflater;
-        mFeatureFlags = featureFlags;
         mAnimateBack = predictiveBackAnimateShade();
         mFalsingCollector = falsingCollector;
         mWakeUpCoordinator = coordinator;
@@ -736,7 +703,6 @@
         mPulseExpansionHandler = pulseExpansionHandler;
         mDozeParameters = dozeParameters;
         mScrimController = scrimController;
-        mUserManager = userManager;
         mMediaDataManager = mediaDataManager;
         mTapAgainViewController = tapAgainViewController;
         mSysUiState = sysUiState;
@@ -889,7 +855,7 @@
 
         // Dreaming->Lockscreen
         collectFlow(mView, mDreamingToLockscreenTransitionViewModel.getLockscreenAlpha(),
-                setDreamLockscreenTransitionAlpha(mNotificationStackScrollLayoutController),
+                setDreamLockscreenTransitionAlpha(),
                 mMainDispatcher);
 
         collectFlow(mView, mKeyguardTransitionInteractor.transition(
@@ -949,13 +915,12 @@
         if (!com.android.systemui.Flags.bouncerUiRevamp()) return;
 
         if (isBouncerShowing && isExpanded()) {
-            // Blur the shade much lesser than the background surface so that the surface is
-            // distinguishable from the background.
-            float shadeBlurEffect = mDepthController.getMaxBlurRadiusPx() / 3;
+            float shadeBlurEffect = BlurConfig.maxBlurRadiusToNotificationPanelBlurRadius(
+                    mDepthController.getMaxBlurRadiusPx());
             mView.setRenderEffect(RenderEffect.createBlurEffect(
                     shadeBlurEffect,
                     shadeBlurEffect,
-                    Shader.TileMode.MIRROR));
+                    Shader.TileMode.CLAMP));
         } else {
             mView.setRenderEffect(null);
         }
@@ -963,28 +928,31 @@
 
     @Override
     public void updateResources() {
-        Trace.beginSection("NSSLC#updateResources");
-        final boolean newSplitShadeEnabled =
-                mSplitShadeStateController.shouldUseSplitNotificationShade(mResources);
-        final boolean splitShadeChanged = mSplitShadeEnabled != newSplitShadeEnabled;
-        mSplitShadeEnabled = newSplitShadeEnabled;
-        mQsController.updateResources();
-        mNotificationsQSContainerController.updateResources();
-        updateKeyguardStatusViewAlignment(/* animate= */false);
-        mKeyguardMediaController.refreshMediaPosition(
-                "NotificationPanelViewController.updateResources");
+        try {
+            Trace.beginSection("NSSLC#updateResources");
+            final boolean newSplitShadeEnabled =
+                    mSplitShadeStateController.shouldUseSplitNotificationShade(mResources);
+            final boolean splitShadeChanged = mSplitShadeEnabled != newSplitShadeEnabled;
+            mSplitShadeEnabled = newSplitShadeEnabled;
+            mQsController.updateResources();
+            mNotificationsQSContainerController.updateResources();
+            updateKeyguardStatusViewAlignment();
+            mKeyguardMediaController.refreshMediaPosition(
+                    "NotificationPanelViewController.updateResources");
 
-        if (splitShadeChanged) {
-            if (isPanelVisibleBecauseOfHeadsUp()) {
-                // workaround for b/324642496, because HUNs set state to OPENING
-                onPanelStateChanged(STATE_CLOSED);
+            if (splitShadeChanged) {
+                if (isPanelVisibleBecauseOfHeadsUp()) {
+                    // workaround for b/324642496, because HUNs set state to OPENING
+                    onPanelStateChanged(STATE_CLOSED);
+                }
+                onSplitShadeEnabledChanged();
             }
-            onSplitShadeEnabledChanged();
-        }
 
-        mSplitShadeFullTransitionDistance =
-                mResources.getDimensionPixelSize(R.dimen.split_shade_full_transition_distance);
-        Trace.endSection();
+            mSplitShadeFullTransitionDistance =
+                    mResources.getDimensionPixelSize(R.dimen.split_shade_full_transition_distance);
+        } finally {
+            Trace.endSection();
+        }
     }
 
     private void onSplitShadeEnabledChanged() {
@@ -1011,29 +979,6 @@
         mQsController.updateQsState();
     }
 
-    private View reInflateStub(int viewId, int stubId, int layoutId, boolean enabled) {
-        View view = mView.findViewById(viewId);
-        if (view != null) {
-            int index = mView.indexOfChild(view);
-            mView.removeView(view);
-            if (enabled) {
-                view = mLayoutInflater.inflate(layoutId, mView, false);
-                mView.addView(view, index);
-            } else {
-                // Add the stub back so we can re-inflate it again if necessary
-                ViewStub stub = new ViewStub(mView.getContext(), layoutId);
-                stub.setId(stubId);
-                mView.addView(stub, index);
-                view = null;
-            }
-        } else if (enabled) {
-            // It's possible the stub was never inflated if the configuration changed
-            ViewStub stub = mView.findViewById(stubId);
-            view = stub.inflate();
-        }
-        return view;
-    }
-
     @VisibleForTesting
     void reInflateViews() {
         debugLog("reInflateViews");
@@ -1042,11 +987,6 @@
                 mStatusBarStateController.getInterpolatedDozeAmount());
     }
 
-    @VisibleForTesting
-    boolean isFlinging() {
-        return mIsFlinging;
-    }
-
     /** Sets a listener to be notified when the shade starts opening or finishes closing. */
     public void setOpenCloseListener(OpenCloseListener openCloseListener) {
         SceneContainerFlag.assertInLegacyMode();
@@ -1096,8 +1036,6 @@
      * @param forceClockUpdate Should the clock be updated even when not on keyguard
      */
     private void positionClockAndNotifications(boolean forceClockUpdate) {
-        boolean animate = !SceneContainerFlag.isEnabled()
-                && mNotificationStackScrollLayoutController.isAddOrRemoveAnimationPending();
         int stackScrollerPadding;
         boolean onKeyguard = isKeyguardShowing();
 
@@ -1120,14 +1058,14 @@
         mNotificationStackScrollLayoutController.setIntrinsicPadding(stackScrollerPadding);
 
         mStackScrollerMeasuringPass++;
-        requestScrollerTopPaddingUpdate(animate);
+        requestScrollerTopPaddingUpdate();
         mStackScrollerMeasuringPass = 0;
         mAnimateNextPositionUpdate = false;
     }
 
     private void updateClockAppearance() {
         mKeyguardClockInteractor.setClockSize(computeDesiredClockSize());
-        updateKeyguardStatusViewAlignment(/* animate= */true);
+        updateKeyguardStatusViewAlignment();
 
         float darkAmount =
                 mScreenOffAnimationController.shouldExpandNotifications()
@@ -1146,10 +1084,6 @@
     }
 
     private ClockSize computeDesiredClockSize() {
-        if (shouldForceSmallClock()) {
-            return ClockSize.SMALL;
-        }
-
         if (mSplitShadeEnabled) {
             return computeDesiredClockSizeForSplitShade();
         }
@@ -1174,17 +1108,9 @@
         return ClockSize.LARGE;
     }
 
-    private boolean shouldForceSmallClock() {
-        return mFeatureFlags.isEnabled(Flags.LOCKSCREEN_ENABLE_LANDSCAPE)
-                && !isOnAod()
-                // True on small landscape screens
-                && mResources.getBoolean(R.bool.force_small_clock_on_lockscreen);
-    }
-
-    private void updateKeyguardStatusViewAlignment(boolean animate) {
+    private void updateKeyguardStatusViewAlignment() {
         boolean shouldBeCentered = shouldKeyguardStatusViewBeCentered();
         mKeyguardUnfoldTransition.ifPresent(t -> t.setStatusViewCentered(shouldBeCentered));
-        mKeyguardInteractor.setClockShouldBeCentered(shouldBeCentered);
     }
 
     private boolean shouldKeyguardStatusViewBeCentered() {
@@ -1214,14 +1140,8 @@
     }
 
     private boolean hasVisibleNotifications() {
-        if (FooterViewRefactor.isEnabled()) {
-            return mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()
-                    || mMediaDataManager.hasActiveMediaOrRecommendation();
-        } else {
-            return mNotificationStackScrollLayoutController
-                    .getVisibleNotificationCount() != 0
-                    || mMediaDataManager.hasActiveMediaOrRecommendation();
-        }
+        return mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()
+                || mMediaDataManager.hasActiveMediaOrRecommendation();
     }
 
     @Override
@@ -1464,7 +1384,7 @@
                 }
             }
         });
-        if (!mScrimController.isScreenOn() && !mForceFlingAnimationForTest) {
+        if (!mScrimController.isScreenOn()) {
             animator.setDuration(1);
         }
         setAnimator(animator);
@@ -1472,16 +1392,11 @@
     }
 
     @VisibleForTesting
-    void setForceFlingAnimationForTest(boolean force) {
-        mForceFlingAnimationForTest = force;
-    }
-
-    @VisibleForTesting
     void onFlingEnd(boolean cancelled) {
         mIsFlinging = false;
         mExpectingSynthesizedDown = false;
         // No overshoot when the animation ends
-        setOverExpansionInternal(0, false /* isFromGesture */);
+        setOverExpansionInternal(0);
         setAnimator(null);
         mKeyguardStateController.notifyPanelFlingEnd();
         if (!cancelled) {
@@ -1572,7 +1487,7 @@
     }
 
     /** Return whether a touch is near the gesture handle at the bottom of screen */
-    boolean isInGestureNavHomeHandleArea(float x, float y) {
+    boolean isInGestureNavHomeHandleArea(float y) {
         return mIsGestureNavigation && y > mView.getHeight() - mNavigationBarBottomHeight;
     }
 
@@ -1605,7 +1520,7 @@
      * There are two scenarios behind this function call. First, input focus transfer has
      * successfully happened and this view already received synthetic DOWN event.
      * (mExpectingSynthesizedDown == false). Do nothing.
-     *
+     * <p>
      * Second, before input focus transfer finished, user may have lifted finger in previous window
      * and this window never received synthetic DOWN event. (mExpectingSynthesizedDown == true). In
      * this case, we use the velocity to trigger fling event.
@@ -1766,7 +1681,7 @@
         return mBarState == KEYGUARD;
     }
 
-    void requestScrollerTopPaddingUpdate(boolean animate) {
+    void requestScrollerTopPaddingUpdate() {
         if (!SceneContainerFlag.isEnabled()) {
             float padding = mQsController.calculateNotificationsTopPadding(mIsExpandingOrCollapsing,
                     getKeyguardNotificationStaticPadding(), mExpandedFraction);
@@ -2041,11 +1956,6 @@
     }
 
     @VisibleForTesting
-    void setTouchSlopExceeded(boolean isTouchSlopExceeded) {
-        mTouchSlopExceeded = isTouchSlopExceeded;
-    }
-
-    @VisibleForTesting
     void setOverExpansion(float overExpansion) {
         if (overExpansion == mOverExpansion) {
             return;
@@ -2218,9 +2128,6 @@
     @Override
     public void setBouncerShowing(boolean bouncerShowing) {
         mBouncerShowing = bouncerShowing;
-        if (!FooterViewRefactor.isEnabled()) {
-            mNotificationStackScrollLayoutController.updateShowEmptyShadeView();
-        }
         updateVisibility();
     }
 
@@ -2397,7 +2304,7 @@
         final float dozeAmount = dozing ? 1 : 0;
         mStatusBarStateController.setAndInstrumentDozeAmount(mView, dozeAmount, animate);
 
-        updateKeyguardStatusViewAlignment(animate);
+        updateKeyguardStatusViewAlignment();
     }
 
     @Override
@@ -2416,7 +2323,7 @@
         }
         mNotificationStackScrollLayoutController.setPulsing(pulsing, animatePulse);
 
-        updateKeyguardStatusViewAlignment(/* animate= */ true);
+        updateKeyguardStatusViewAlignment();
     }
 
     public void performHapticFeedback(int constant) {
@@ -2982,8 +2889,7 @@
         mIsSpringBackAnimation = true;
         ValueAnimator animator = ValueAnimator.ofFloat(mOverExpansion, 0);
         animator.addUpdateListener(
-                animation -> setOverExpansionInternal((float) animation.getAnimatedValue(),
-                        false /* isFromGesture */));
+                animation -> setOverExpansionInternal((float) animation.getAnimatedValue()));
         animator.setDuration(SHADE_OPEN_SPRING_BACK_DURATION);
         animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
         animator.addListener(new AnimatorListenerAdapter() {
@@ -3075,19 +2981,10 @@
      * Set the current overexpansion
      *
      * @param overExpansion the amount of overexpansion to apply
-     * @param isFromGesture is this amount from a gesture and needs to be rubberBanded?
      */
-    private void setOverExpansionInternal(float overExpansion, boolean isFromGesture) {
-        if (!isFromGesture) {
-            mLastGesturedOverExpansion = -1;
-            setOverExpansion(overExpansion);
-        } else if (mLastGesturedOverExpansion != overExpansion) {
-            mLastGesturedOverExpansion = overExpansion;
-            final float heightForFullOvershoot = mView.getHeight() / 3.0f;
-            float newExpansion = MathUtils.saturate(overExpansion / heightForFullOvershoot);
-            newExpansion = Interpolators.getOvershootInterpolation(newExpansion);
-            setOverExpansion(newExpansion * mPanelFlingOvershootAmount * 2.0f);
-        }
+    private void setOverExpansionInternal(float overExpansion) {
+        mLastGesturedOverExpansion = -1;
+        setOverExpansion(overExpansion);
     }
 
     /** Sets the expanded height relative to a number from 0 to 1. */
@@ -3183,29 +3080,6 @@
     }
 
     /**
-     * Phase 2: Bounce down.
-     */
-    private void startUnlockHintAnimationPhase2(final Runnable onAnimationFinished) {
-        ValueAnimator animator = createHeightAnimator(getMaxPanelHeight());
-        animator.setDuration(450);
-        animator.setInterpolator(mBounceInterpolator);
-        animator.addListener(new AnimatorListenerAdapter() {
-            @Override
-            public void onAnimationEnd(Animator animation) {
-                setAnimator(null);
-                onAnimationFinished.run();
-                updateExpansionAndVisibility();
-            }
-        });
-        animator.start();
-        setAnimator(animator);
-    }
-
-    private ValueAnimator createHeightAnimator(float targetHeight) {
-        return createHeightAnimator(targetHeight, 0.0f /* performOvershoot */);
-    }
-
-    /**
      * Create an animator that can also overshoot
      *
      * @param targetHeight    the target height
@@ -3225,7 +3099,7 @@
                                 mPanelFlingOvershootAmount * overshootAmount,
                                 Interpolators.FAST_OUT_SLOW_IN.getInterpolation(
                                         animator.getAnimatedFraction()));
-                        setOverExpansionInternal(expansion, false /* isFromGesture */);
+                        setOverExpansionInternal(expansion);
                     }
                     setExpandedHeightInternal((float) animation.getAnimatedValue());
                 });
@@ -3280,8 +3154,8 @@
         mFixedDuration = NO_FIXED_DURATION;
     }
 
-    boolean postToView(Runnable action) {
-        return mView.post(action);
+    void postToView(Runnable action) {
+        mView.post(action);
     }
 
     /** Sends an external (e.g. Status Bar) intercept touch event to the Shade touch handler. */
@@ -3360,7 +3234,7 @@
         return mShadeExpansionStateManager;
     }
 
-    void onQsExpansionChanged(boolean expanded) {
+    void onQsExpansionChanged() {
         updateExpandedHeightToMaxHeight();
         updateSystemUiStateFlags();
         NavigationBarView navigationBarView =
@@ -3372,7 +3246,7 @@
 
     @VisibleForTesting
     void onQsSetExpansionHeightCalled(boolean qsFullyExpanded) {
-        requestScrollerTopPaddingUpdate(false);
+        requestScrollerTopPaddingUpdate();
         mKeyguardStatusBarViewController.updateViewState();
         int barState = getBarState();
         if (barState == SHADE_LOCKED || barState == KEYGUARD) {
@@ -3413,7 +3287,7 @@
 
     private void onExpansionHeightSetToMax(boolean requestPaddingUpdate) {
         if (requestPaddingUpdate) {
-            requestScrollerTopPaddingUpdate(false /* animate */);
+            requestScrollerTopPaddingUpdate();
         }
         updateExpandedHeightToMaxHeight();
     }
@@ -3437,7 +3311,7 @@
                             ? (ExpandableNotificationRow) firstChildNotGone : null;
             if (firstRow != null && (view == firstRow || (firstRow.getNotificationParent()
                     == firstRow))) {
-                requestScrollerTopPaddingUpdate(false /* animate */);
+                requestScrollerTopPaddingUpdate();
             }
             updateExpandedHeightToMaxHeight();
         }
@@ -3517,7 +3391,6 @@
                 boolean animatingUnlockedShadeToKeyguardBypass
         ) {
             boolean goingToFullShade = mStatusBarStateController.goingToFullShade();
-            boolean keyguardFadingAway = mKeyguardStateController.isKeyguardFadingAway();
             int oldState = mBarState;
             boolean keyguardShowing = statusBarState == KEYGUARD;
 
@@ -3738,17 +3611,13 @@
         }
         if (state == STATE_CLOSED) {
             mQsController.setExpandImmediate(false);
-            // Close the status bar in the next frame so we can show the end of the
-            // animation.
-            if (!mIsAnyMultiShadeExpanded) {
-                mView.post(mMaybeHideExpandedRunnable);
-            }
+            // Close the status bar in the next frame so we can show the end of the animation.
+            mView.post(mMaybeHideExpandedRunnable);
         }
         mCurrentPanelState = state;
     }
 
-    private Consumer<Float> setDreamLockscreenTransitionAlpha(
-            NotificationStackScrollLayoutController stackScroller) {
+    private Consumer<Float> setDreamLockscreenTransitionAlpha() {
         return (Float alpha) -> {
             // Also animate the status bar's alpha during transitions between the lockscreen and
             // dreams.
@@ -4285,4 +4154,3 @@
         }
     }
 }
-
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
index c88e7b8..d058372 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsControllerImpl.java
@@ -86,7 +86,6 @@
 import com.android.systemui.statusbar.QsFrameTranslateController;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor;
-import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor;
 import com.android.systemui.statusbar.notification.stack.AmbientState;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController;
@@ -96,8 +95,8 @@
 import com.android.systemui.statusbar.phone.LightBarController;
 import com.android.systemui.statusbar.phone.LockscreenGestureLogger;
 import com.android.systemui.statusbar.phone.ScrimController;
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.statusbar.phone.ShadeTouchableRegionManager;
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.statusbar.policy.CastController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.statusbar.policy.SplitShadeStateController;
@@ -115,9 +114,7 @@
 import javax.inject.Inject;
 import javax.inject.Provider;
 
-/** Handles QuickSettings touch handling, expansion and animation state
- * TODO (b/264460656) make this dumpable
- */
+/** Handles QuickSettings touch handling, expansion and animation state. */
 @SysUISingleton
 public class QuickSettingsControllerImpl implements QuickSettingsController, Dumpable {
     public static final String TAG = "QuickSettingsController";
@@ -295,7 +292,6 @@
     private ValueAnimator mSizeChangeAnimator;
 
     private ExpansionHeightListener mExpansionHeightListener;
-    private QsStateUpdateListener mQsStateUpdateListener;
     private ApplyClippingImmediatelyListener mApplyClippingImmediatelyListener;
     private FlingQsWithoutClickListener mFlingQsWithoutClickListener;
     private ExpansionHeightSetToMaxListener mExpansionHeightSetToMaxListener;
@@ -402,10 +398,6 @@
         mExpansionHeightListener = listener;
     }
 
-    void setQsStateUpdateListener(QsStateUpdateListener listener) {
-        mQsStateUpdateListener = listener;
-    }
-
     void setApplyClippingImmediatelyListener(ApplyClippingImmediatelyListener listener) {
         mApplyClippingImmediatelyListener = listener;
     }
@@ -563,7 +555,7 @@
         }
         // TODO (b/265193930): remove dependency on NPVC
         // Let's reject anything at the very bottom around the home handle in gesture nav
-        if (mPanelViewControllerLazy.get().isInGestureNavHomeHandleArea(x, y)) {
+        if (mPanelViewControllerLazy.get().isInGestureNavHomeHandleArea(y)) {
             return false;
         }
         return y <= mNotificationStackScrollLayoutController.getBottomMostNotificationBottom()
@@ -805,7 +797,7 @@
         if (changed) {
             mShadeRepository.setLegacyIsQsExpanded(expanded);
             updateQsState();
-            mPanelViewControllerLazy.get().onQsExpansionChanged(expanded);
+            mPanelViewControllerLazy.get().onQsExpansionChanged();
             mShadeLog.logQsExpansionChanged("QS Expansion Changed.", expanded,
                     getMinExpansionHeight(), getMaxExpansionHeight(),
                     mStackScrollerOverscrolling, mAnimatorExpand, mAnimating);
@@ -1022,16 +1014,6 @@
     }
 
     void updateQsState() {
-        if (!FooterViewRefactor.isEnabled()) {
-            // Update full screen state; note that this will be true if the QS panel is only
-            // partially expanded, and that is fixed with the footer view refactor.
-            setQsFullScreen(/* qsFullScreen = */ getExpanded() && !mSplitShadeEnabled);
-        }
-
-        if (mQsStateUpdateListener != null) {
-            mQsStateUpdateListener.onQsStateUpdated(getExpanded(), mStackScrollerOverscrolling);
-        }
-
         if (mQs == null) return;
         mQs.setExpanded(getExpanded());
     }
@@ -1094,10 +1076,8 @@
         // Update the light bar
         mLightBarController.setQsExpanded(mFullyExpanded);
 
-        if (FooterViewRefactor.isEnabled()) {
-            // Update full screen state
-            setQsFullScreen(/* qsFullScreen = */ mFullyExpanded && !mSplitShadeEnabled);
-        }
+        // Update full screen state
+        setQsFullScreen(/* qsFullScreen = */ mFullyExpanded && !mSplitShadeEnabled);
     }
 
     float getLockscreenShadeDragProgress() {
@@ -1212,7 +1192,7 @@
     /**
      * Applies clipping to quick settings, notifications layout and
      * updates bounds of the notifications background (notifications scrim).
-     *
+     * <p>
      * The parameters are bounds of the notifications area rectangle, this function
      * calculates bounds for the QS clipping based on the notifications bounds.
      */
@@ -2268,10 +2248,8 @@
                     setExpansionHeight(qsHeight);
                 }
 
-                boolean hasNotifications = FooterViewRefactor.isEnabled()
-                        ? mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue()
-                        : mNotificationStackScrollLayoutController.getVisibleNotificationCount()
-                                != 0;
+                boolean hasNotifications =
+                        mActiveNotificationsInteractor.getAreAnyNotificationsPresentValue();
                 if (!hasNotifications && !mMediaDataManager.hasActiveMediaOrRecommendation()) {
                     // No notifications are visible, let's animate to the height of qs instead
                     if (isQsFragmentCreated()) {
@@ -2406,10 +2384,6 @@
         void onQsSetExpansionHeightCalled(boolean qsFullyExpanded);
     }
 
-    interface QsStateUpdateListener {
-        void onQsStateUpdated(boolean qsExpanded, boolean isStackScrollerOverscrolling);
-    }
-
     interface ApplyClippingImmediatelyListener {
         void onQsClippingImmediatelyApplied(boolean clipStatusView, Rect lastQsClipBounds,
                 int top, boolean qsFragmentCreated, boolean qsVisible);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayChangeLatencyTracker.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayChangeLatencyTracker.kt
index 4d35d0e..e358dce 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayChangeLatencyTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeDisplayChangeLatencyTracker.kt
@@ -24,7 +24,6 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.scene.ui.view.WindowRootView
-import com.android.systemui.shade.ShadeDisplayChangeLatencyTracker.Companion.TIMEOUT
 import com.android.systemui.shade.data.repository.ShadeDisplaysRepository
 import com.android.systemui.util.kotlin.getOrNull
 import java.util.Optional
@@ -33,7 +32,6 @@
 import kotlin.time.Duration.Companion.seconds
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Job
-import kotlinx.coroutines.cancel
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.filter
@@ -135,7 +133,7 @@
 
     private companion object {
         const val TAG = "ShadeDisplayLatency"
-        val t = TrackTracer(trackName = TAG)
+        val t = TrackTracer(trackName = TAG, trackGroup = "shade")
         val TIMEOUT = 3.seconds
         const val SHADE_MOVE_ACTION = LatencyTracker.ACTION_SHADE_WINDOW_DISPLAY_CHANGE
     }
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt
index 359ddd8..5fab889 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt
@@ -18,13 +18,16 @@
 
 import android.annotation.IntDef
 import android.os.Trace
+import android.os.Trace.TRACE_TAG_APP as TRACE_TAG
 import android.util.Log
 import androidx.annotation.FloatRange
+import com.android.app.tracing.TraceStateLogger
+import com.android.app.tracing.TrackGroupUtils.trackGroup
+import com.android.app.tracing.coroutines.TrackTracer
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.util.Compile
 import java.util.concurrent.CopyOnWriteArrayList
 import javax.inject.Inject
-import android.os.Trace.TRACE_TAG_APP as TRACE_TAG
 
 /**
  * A class responsible for managing the notification panel's current state.
@@ -38,6 +41,8 @@
     private val expansionListeners = CopyOnWriteArrayList<ShadeExpansionListener>()
     private val stateListeners = CopyOnWriteArrayList<ShadeStateListener>()
 
+    private val stateLogger = TraceStateLogger(trackGroup("shade", TRACK_NAME))
+
     @PanelState private var state: Int = STATE_CLOSED
     @FloatRange(from = 0.0, to = 1.0) private var fraction: Float = 0f
     private var expanded: Boolean = false
@@ -75,7 +80,7 @@
     fun onPanelExpansionChanged(
         @FloatRange(from = 0.0, to = 1.0) fraction: Float,
         expanded: Boolean,
-        tracking: Boolean
+        tracking: Boolean,
     ) {
         require(!fraction.isNaN()) { "fraction cannot be NaN" }
         val oldState = state
@@ -113,11 +118,8 @@
         )
 
         if (Trace.isTagEnabled(TRACE_TAG)) {
-            Trace.traceCounter(TRACE_TAG, "panel_expansion", (fraction * 100).toInt())
-            if (state != oldState) {
-                Trace.asyncTraceForTrackEnd(TRACE_TAG, TRACK_NAME, 0)
-                Trace.asyncTraceForTrackBegin(TRACE_TAG, TRACK_NAME, state.panelStateToString(), 0)
-            }
+            TrackTracer.instantForGroup("shade", "panel_expansion", fraction)
+            stateLogger.log(state.panelStateToString())
         }
 
         val expansionChangeEvent = ShadeExpansionChangeEvent(fraction, expanded, tracking)
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeStateTraceLogger.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeStateTraceLogger.kt
new file mode 100644
index 0000000..2705cda
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeStateTraceLogger.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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
+
+import com.android.app.tracing.TraceStateLogger
+import com.android.app.tracing.TrackGroupUtils.trackGroup
+import com.android.app.tracing.coroutines.TrackTracer.Companion.instantForGroup
+import com.android.app.tracing.coroutines.launchTraced
+import com.android.systemui.CoreStartable
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.shade.data.repository.ShadeDisplaysRepository
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+
+@SysUISingleton
+class ShadeStateTraceLogger
+@Inject
+constructor(
+    private val shadeInteractor: ShadeInteractor,
+    private val shadeDisplaysRepository: ShadeDisplaysRepository,
+    @Application private val scope: CoroutineScope,
+) : CoreStartable {
+    override fun start() {
+        scope.launchTraced("ShadeStateTraceLogger") {
+            launch {
+                val stateLogger = createTraceStateLogger("isShadeLayoutWide")
+                shadeInteractor.isShadeLayoutWide.collect { stateLogger.log(it.toString()) }
+            }
+            launch {
+                val stateLogger = createTraceStateLogger("shadeMode")
+                shadeInteractor.shadeMode.collect { stateLogger.log(it.toString()) }
+            }
+            launch {
+                shadeInteractor.shadeExpansion.collect {
+                    instantForGroup(TRACK_GROUP_NAME, "shadeExpansion", it)
+                }
+            }
+            launch {
+                shadeDisplaysRepository.displayId.collect {
+                    instantForGroup(TRACK_GROUP_NAME, "displayId", it)
+                }
+            }
+        }
+    }
+
+    private fun createTraceStateLogger(trackName: String): TraceStateLogger {
+        return TraceStateLogger(trackGroup(TRACK_GROUP_NAME, trackName))
+    }
+
+    private companion object {
+        const val TRACK_GROUP_NAME = "shade"
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeTraceLogger.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeTraceLogger.kt
index a36c56e..1180599 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeTraceLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeTraceLogger.kt
@@ -27,7 +27,7 @@
  * them across various threads' logs.
  */
 object ShadeTraceLogger {
-    private val t = TrackTracer(trackName = "ShadeTraceLogger")
+    private val t = TrackTracer(trackName = "ShadeTraceLogger", trackGroup = "shade")
 
     @JvmStatic
     fun logOnMovedToDisplay(displayId: Int, config: Configuration) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
index e191120..3449e81 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewProviderModule.kt
@@ -41,6 +41,7 @@
 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.SceneJankMonitor
 import com.android.systemui.scene.ui.view.SceneWindowRootView
 import com.android.systemui.scene.ui.view.WindowRootView
 import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel
@@ -89,6 +90,7 @@
             layoutInsetController: NotificationInsetsController,
             sceneDataSourceDelegator: Provider<SceneDataSourceDelegator>,
             qsSceneAdapter: Provider<QSSceneAdapter>,
+            sceneJankMonitorFactory: SceneJankMonitor.Factory,
         ): WindowRootView {
             return if (SceneContainerFlag.isEnabled) {
                 checkNoSceneDuplicates(scenesProvider.get())
@@ -104,6 +106,7 @@
                     layoutInsetController = layoutInsetController,
                     sceneDataSourceDelegator = sceneDataSourceDelegator.get(),
                     qsSceneAdapter = qsSceneAdapter,
+                    sceneJankMonitorFactory = sceneJankMonitorFactory,
                 )
                 sceneWindowRootView
             } else {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/StartShadeModule.kt b/packages/SystemUI/src/com/android/systemui/shade/StartShadeModule.kt
index c4de78b..570a785 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/StartShadeModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/StartShadeModule.kt
@@ -40,4 +40,9 @@
     @IntoMap
     @ClassKey(ShadeStartable::class)
     abstract fun provideShadeStartable(startable: ShadeStartable): CoreStartable
+
+    @Binds
+    @IntoMap
+    @ClassKey(ShadeStateTraceLogger::class)
+    abstract fun provideShadeStateTraceLogger(startable: ShadeStateTraceLogger): CoreStartable
 }
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 ef62d2d..a2edd3ab 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
@@ -15,11 +15,18 @@
  */
 package com.android.systemui.shade.data.repository
 
+import android.annotation.SuppressLint
 import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
 import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.BufferOverflow
+import kotlinx.coroutines.flow.MutableSharedFlow
 import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharedFlow
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.launch
 
 /** Data for the shade, mostly related to expansion of the shade and quick settings. */
 interface ShadeRepository {
@@ -36,7 +43,7 @@
      * Information about the currently running fling animation, or null if no fling animation is
      * running.
      */
-    val currentFling: StateFlow<FlingInfo?>
+    val currentFling: SharedFlow<FlingInfo?>
 
     /**
      * The amount the lockscreen shade has dragged down by the user, [0-1]. 0 means fully collapsed,
@@ -180,7 +187,8 @@
 
 /** Business logic for shade interactions */
 @SysUISingleton
-class ShadeRepositoryImpl @Inject constructor() : ShadeRepository {
+class ShadeRepositoryImpl @Inject constructor(@Background val backgroundScope: CoroutineScope) :
+    ShadeRepository {
     private val _qsExpansion = MutableStateFlow(0f)
     @Deprecated("Use ShadeInteractor.qsExpansion instead")
     override val qsExpansion: StateFlow<Float> = _qsExpansion.asStateFlow()
@@ -193,8 +201,13 @@
     override val udfpsTransitionToFullShadeProgress: StateFlow<Float> =
         _udfpsTransitionToFullShadeProgress.asStateFlow()
 
-    private val _currentFling: MutableStateFlow<FlingInfo?> = MutableStateFlow(null)
-    override val currentFling: StateFlow<FlingInfo?> = _currentFling.asStateFlow()
+    /**
+     * Must be a SharedFlow, since the fling is by definition an event and dropping it has extreme
+     * consequences in some cases (for example, keyguard uses this to decide when to unlock).
+     */
+    @SuppressLint("SharedFlowCreation")
+    override val currentFling: MutableSharedFlow<FlingInfo?> =
+        MutableSharedFlow(replay = 2, onBufferOverflow = BufferOverflow.DROP_OLDEST)
 
     private val _legacyShadeExpansion = MutableStateFlow(0f)
     @Deprecated("Use ShadeInteractor.shadeExpansion instead")
@@ -294,7 +307,7 @@
     }
 
     override fun setCurrentFling(info: FlingInfo?) {
-        _currentFling.value = info
+        backgroundScope.launch { currentFling.emit(info) }
     }
 
     @Deprecated("Should only be called by NPVC and tests")
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDialogContextInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDialogContextInteractor.kt
index 201dc03..4edba27 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDialogContextInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeDialogContextInteractor.kt
@@ -77,7 +77,17 @@
     private fun getContextOrDefault(displayId: Int): Context {
         return try {
             traceSection({ "Getting dialog context for displayId=$displayId" }) {
-                displayWindowPropertyRepository.get().get(displayId, DIALOG_WINDOW_TYPE).context
+                val displayWindowProperties =
+                    displayWindowPropertyRepository.get().get(displayId, DIALOG_WINDOW_TYPE)
+                if (displayWindowProperties == null) {
+                    Log.e(
+                        TAG,
+                        "DisplayWindowPropertiesRepository returned null for display $displayId. Returning default one",
+                    )
+                    defaultContext
+                } else {
+                    displayWindowProperties.context
+                }
             }
         } catch (e: Exception) {
             // This can happen if the display was disconnected in the meantime.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 862f33bb..c6a4d157 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -183,6 +183,7 @@
     private static final int MSG_ENTER_DESKTOP = 80 << MSG_SHIFT;
     private static final int MSG_SET_SPLITSCREEN_FOCUS = 81 << MSG_SHIFT;
     private static final int MSG_TOGGLE_QUICK_SETTINGS_PANEL = 82 << MSG_SHIFT;
+    private static final int MSG_WALLET_ACTION_LAUNCH_GESTURE = 83 << MSG_SHIFT;
     public static final int FLAG_EXCLUDE_NONE = 0;
     public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
     public static final int FLAG_EXCLUDE_RECENTS_PANEL = 1 << 1;
@@ -343,6 +344,11 @@
         default void onCameraLaunchGestureDetected(int source) { }
 
         /**
+         * Notifies SysUI that the wallet launch gesture was detected.
+         */
+        default void onWalletLaunchGestureDetected() {}
+
+        /**
          * Notifies SysUI that the emergency action gesture was detected.
          */
         default void onEmergencyActionLaunchGestureDetected() { }
@@ -953,6 +959,18 @@
     }
 
     @Override
+    public void onWalletLaunchGestureDetected() {
+        synchronized (mLock) {
+            if (mPowerInteractor != null) {
+                mPowerInteractor.get().onWalletLaunchGestureDetected();
+            }
+
+            mHandler.removeMessages(MSG_WALLET_ACTION_LAUNCH_GESTURE);
+            mHandler.obtainMessage(MSG_WALLET_ACTION_LAUNCH_GESTURE).sendToTarget();
+        }
+    }
+
+    @Override
     public void onEmergencyActionLaunchGestureDetected() {
         synchronized (mLock) {
             mHandler.removeMessages(MSG_EMERGENCY_ACTION_LAUNCH_GESTURE);
@@ -1642,6 +1660,11 @@
                         mCallbacks.get(i).onCameraLaunchGestureDetected(msg.arg1);
                     }
                     break;
+                case MSG_WALLET_ACTION_LAUNCH_GESTURE:
+                    for (int i = 0; i < mCallbacks.size(); i++) {
+                        mCallbacks.get(i).onWalletLaunchGestureDetected();
+                    }
+                    break;
                 case MSG_EMERGENCY_ACTION_LAUNCH_GESTURE:
                     for (int i = 0; i < mCallbacks.size(); i++) {
                         mCallbacks.get(i).onEmergencyActionLaunchGestureDetected();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
index 37989f5..2885ce8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LightRevealScrim.kt
@@ -11,13 +11,13 @@
 import android.graphics.PorterDuffXfermode
 import android.graphics.RadialGradient
 import android.graphics.Shader
-import android.os.Trace
 import android.util.AttributeSet
 import android.util.MathUtils.lerp
 import android.view.MotionEvent
 import android.view.View
 import android.view.animation.PathInterpolator
 import com.android.app.animation.Interpolators
+import com.android.app.tracing.coroutines.TrackTracer
 import com.android.keyguard.logging.ScrimLogger
 import com.android.systemui.shade.TouchLogger
 import com.android.systemui.statusbar.LightRevealEffect.Companion.getPercentPastThreshold
@@ -321,9 +321,8 @@
                 }
                 revealEffect.setRevealAmountOnScrim(value, this)
                 updateScrimOpaque()
-                Trace.traceCounter(
-                    Trace.TRACE_TAG_APP,
-                    "light_reveal_amount $logString",
+                TrackTracer.instantForGroup(
+                    "scrim", { "light_reveal_amount $logString" },
                     (field * 100).toInt()
                 )
                 invalidate()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index 239257d..2bcd3fc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -61,11 +61,13 @@
 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.deviceentry.domain.interactor.DeviceUnlockedInteractor;
 import com.android.systemui.dump.DumpManager;
 import com.android.systemui.flags.FeatureFlagsClassic;
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.plugins.statusbar.StatusBarStateController.StateListener;
 import com.android.systemui.recents.OverviewProxyService;
@@ -78,6 +80,7 @@
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.util.ListenerSet;
+import com.android.systemui.util.kotlin.JavaAdapterKt;
 import com.android.systemui.util.settings.SecureSettings;
 
 import dagger.Lazy;
@@ -88,9 +91,13 @@
 import java.util.List;
 import java.util.Objects;
 import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
 
 import javax.inject.Inject;
 
+import kotlinx.coroutines.CoroutineScope;
+
 /**
  * Handles keeping track of the current user, profiles, and various things related to hiding
  * contents, redacting notifications, and the lockscreen.
@@ -111,6 +118,9 @@
     private static final Uri SHOW_PRIVATE_LOCKSCREEN =
             Settings.Secure.getUriFor(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
 
+    private static final long LOCK_TIME_FOR_SENSITIVE_REDACTION_MS =
+            TimeUnit.MINUTES.toMillis(10);
+
     private final Lazy<NotificationVisibilityProvider> mVisibilityProviderLazy;
     private final Lazy<CommonNotifCollection> mCommonNotifCollectionLazy;
     private final DevicePolicyManager mDevicePolicyManager;
@@ -284,7 +294,12 @@
     protected final SparseArray<UserInfo> mCurrentProfiles = new SparseArray<>();
     protected final SparseArray<UserInfo> mCurrentManagedProfiles = new SparseArray<>();
 
+    // The last lock time. Uses currentTimeMillis
+    @VisibleForTesting
+    protected final AtomicLong mLastLockTime = new AtomicLong(-1);
+
     protected int mCurrentUserId = 0;
+
     protected NotificationPresenter mPresenter;
     protected ContentObserver mLockscreenSettingsObserver;
     protected ContentObserver mSettingsObserver;
@@ -311,7 +326,10 @@
             DumpManager dumpManager,
             LockPatternUtils lockPatternUtils,
             FeatureFlagsClassic featureFlags,
-            Lazy<DeviceUnlockedInteractor> deviceUnlockedInteractorLazy) {
+            Lazy<DeviceUnlockedInteractor> deviceUnlockedInteractorLazy,
+            Lazy<KeyguardInteractor> keyguardInteractor,
+            @Application CoroutineScope coroutineScope
+    ) {
         mContext = context;
         mMainExecutor = mainExecutor;
         mBackgroundExecutor = backgroundExecutor;
@@ -341,6 +359,18 @@
         if (keyguardPrivateNotifications()) {
             init();
         }
+
+        // To avoid dependency injection cycle, finish constructing this object before using the
+        // KeyguardInteractor. The CoroutineScope will only be null in tests.
+        if (LockscreenOtpRedaction.isEnabled() && coroutineScope != null) {
+            mMainExecutor.execute(() -> JavaAdapterKt.collectFlow(coroutineScope,
+                    keyguardInteractor.get().isKeyguardDismissible(),
+                    unlocked -> {
+                        if (!unlocked) {
+                            mLastLockTime.set(System.currentTimeMillis());
+                        }
+                    }));
+        }
     }
 
     public void setUpWithPresenter(NotificationPresenter presenter) {
@@ -443,7 +473,7 @@
         mCurrentUserId = mUserTracker.getUserId(); // in case we reg'd receiver too late
         updateCurrentProfilesCache();
 
-        // Set  up
+        // Set up
         mBackgroundExecutor.execute(() -> {
             @SuppressLint("MissingPermission") List<UserInfo> users = mUserManager.getUsers();
             for (int i = users.size() - 1; i >= 0; i--) {
@@ -667,8 +697,6 @@
                 !userAllowsPrivateNotificationsInPublic(mCurrentUserId);
         boolean isNotifForManagedProfile = mCurrentManagedProfiles.contains(userId);
         boolean isNotifUserRedacted = !userAllowsPrivateNotificationsInPublic(userId);
-        boolean isNotifSensitive = LockscreenOtpRedaction.isEnabled()
-                && ent.getRanking() != null && ent.getRanking().hasSensitiveContent();
 
         // redact notifications if the current user is redacting notifications or the notification
         // contains sensitive content. However if the notification is associated with a managed
@@ -689,12 +717,45 @@
         if (keyguardPrivateNotifications() && !mKeyguardAllowingNotifications) {
             return REDACTION_TYPE_PUBLIC;
         }
-        if (isNotifSensitive) {
+
+        if (shouldShowSensitiveContentRedactedView(ent)) {
             return REDACTION_TYPE_SENSITIVE_CONTENT;
         }
         return REDACTION_TYPE_NONE;
     }
 
+    /*
+     * We show the sensitive content redaction view if
+     * 1. The feature is enabled
+     * 2. The device is locked
+     * 3. The notification has the `hasSensitiveContent` ranking variable set to true
+     * 4. The device has been locked for at least LOCK_TIME_FOR_SENSITIVE_REDACTION_MS
+     * 5. The notification arrived since the last lock time
+     */
+    private boolean shouldShowSensitiveContentRedactedView(NotificationEntry ent) {
+        if (!LockscreenOtpRedaction.isEnabled()) {
+            return false;
+        }
+
+        if (!mKeyguardManager.isDeviceLocked()) {
+            return false;
+        }
+
+        if (ent.getRanking() == null || !ent.getRanking().hasSensitiveContent()) {
+            return false;
+        }
+
+        long lastLockedTime = mLastLockTime.get();
+        if (ent.getSbn().getPostTime() < lastLockedTime) {
+            return false;
+        }
+
+        if ((System.currentTimeMillis() - lastLockedTime) < LOCK_TIME_FOR_SENSITIVE_REDACTION_MS) {
+            return false;
+        }
+        return true;
+    }
+
     private boolean packageHasVisibilityOverride(String key) {
         if (mCommonNotifCollectionLazy.get() == null) {
             Log.wtf(TAG, "mEntryManager was null!", new Throwable());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
index e83cded..75117936 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt
@@ -22,7 +22,6 @@
 import android.content.Context
 import android.content.res.Configuration
 import android.os.SystemClock
-import android.os.Trace
 import android.util.IndentingPrintWriter
 import android.util.Log
 import android.util.MathUtils
@@ -33,6 +32,7 @@
 import androidx.dynamicanimation.animation.SpringAnimation
 import androidx.dynamicanimation.animation.SpringForce
 import com.android.app.animation.Interpolators
+import com.android.app.tracing.coroutines.TrackTracer
 import com.android.systemui.Dumpable
 import com.android.systemui.animation.ShadeInterpolation
 import com.android.systemui.dagger.SysUISingleton
@@ -263,7 +263,7 @@
             updateScheduled = false
             val (blur, zoomOutFromShadeRadius) = computeBlurAndZoomOut()
             val opaque = shouldBlurBeOpaque
-            Trace.traceCounter(Trace.TRACE_TAG_APP, "shade_blur_radius", blur)
+            TrackTracer.instantForGroup("shade", "shade_blur_radius", blur)
             blurUtils.applyBlur(root.viewRootImpl, blur, opaque)
             onBlurApplied(blur, zoomOutFromShadeRadius)
         }
@@ -384,7 +384,7 @@
             windowRootViewBlurInteractor.onBlurAppliedEvent.collect { appliedBlurRadius ->
                 if (updateScheduled) {
                     // Process the blur applied event only if we scheduled the update
-                    Trace.traceCounter(Trace.TRACE_TAG_APP, "shade_blur_radius", appliedBlurRadius)
+                    TrackTracer.instantForGroup("shade", "shade_blur_radius", appliedBlurRadius)
                     updateScheduled = false
                     onBlurApplied(appliedBlurRadius, zoomOutCalculatedFromShadeRadius)
                 } else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
index a7ad462..ead8f6a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
@@ -37,6 +37,7 @@
 import androidx.annotation.NonNull;
 
 import com.android.app.animation.Interpolators;
+import com.android.app.tracing.coroutines.TrackTracer;
 import com.android.compose.animation.scene.OverlayKey;
 import com.android.compose.animation.scene.SceneKey;
 import com.android.internal.annotations.GuardedBy;
@@ -671,7 +672,7 @@
     }
 
     private void recordHistoricalState(int newState, int lastState, boolean upcoming) {
-        Trace.traceCounter(Trace.TRACE_TAG_APP, "statusBarState", newState);
+        TrackTracer.instantForGroup("statusBar", "state", newState);
         mHistoryIndex = (mHistoryIndex + 1) % HISTORY_SIZE;
         HistoricalState state = mHistoricalRecords[mHistoryIndex];
         state.mNewState = newState;
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 6db610b..e86a991 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/StatusBarChipsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/StatusBarChipsModule.kt
@@ -20,10 +20,8 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.log.LogBuffer
 import com.android.systemui.log.LogBufferFactory
-import com.android.systemui.statusbar.chips.notification.demo.ui.viewmodel.DemoNotifChipViewModel
 import com.android.systemui.statusbar.chips.notification.domain.interactor.StatusBarNotificationChipsInteractor
 import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
-import dagger.Binds
 import dagger.Lazy
 import dagger.Module
 import dagger.Provides
@@ -32,11 +30,6 @@
 
 @Module
 abstract class StatusBarChipsModule {
-    @Binds
-    @IntoMap
-    @ClassKey(DemoNotifChipViewModel::class)
-    abstract fun binds(impl: DemoNotifChipViewModel): CoreStartable
-
     companion object {
         @Provides
         @SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModel.kt
deleted file mode 100644
index 5fa19dd..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModel.kt
+++ /dev/null
@@ -1,175 +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.chips.notification.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.notification.shared.StatusBarNotifChips
-import com.android.systemui.statusbar.chips.ui.model.ColorsModel
-import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
-import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipViewModel
-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 promoted ongoing notification chips from [chip] based on adb
- * commands sent by the user.
- *
- * Example adb commands:
- *
- * To show a chip with the SysUI icon and custom text and color:
- * ```
- * adb shell cmd statusbar demo-notif -p com.android.systemui -t 10min -c "\\#434343"
- * ```
- *
- * To hide the chip:
- * ```
- * adb shell cmd statusbar demo-notif --hide
- * ```
- *
- * See [DemoNotifCommand] for more information on the adb command spec.
- */
-@SysUISingleton
-class DemoNotifChipViewModel
-@Inject
-constructor(
-    private val commandRegistry: CommandRegistry,
-    private val packageManager: PackageManager,
-    private val systemClock: SystemClock,
-) : OngoingActivityChipViewModel, CoreStartable {
-    override fun start() {
-        commandRegistry.registerCommand(DEMO_COMMAND_NAME) { DemoNotifCommand() }
-    }
-
-    private val _chip =
-        MutableStateFlow<OngoingActivityChipModel>(OngoingActivityChipModel.Hidden())
-    override val chip: StateFlow<OngoingActivityChipModel> = _chip.asStateFlow()
-
-    private inner class DemoNotifCommand : ParseableCommand(DEMO_COMMAND_NAME) {
-        private val packageName: String? by
-            param(
-                longName = "packageName",
-                shortName = "p",
-                description = "The package name for app \"posting\" the demo notification",
-                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 notification chip")
-
-        override fun execute(pw: PrintWriter) {
-            if (!StatusBarNotifChips.isEnabled) {
-                pw.println(
-                    "Error: com.android.systemui.status_bar_notification_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)
-            )
-        }
-    }
-
-    companion object {
-        private const val DEMO_COMMAND_NAME = "demo-notif"
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractor.kt
index dff6f56..836cf49 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractor.kt
@@ -42,12 +42,15 @@
  *
  * [StatusBarNotificationChipsInteractor] will collect all the individual instances of this
  * interactor and send all the necessary information to the UI layer.
+ *
+ * @property creationTime the time when the notification first appeared as promoted.
  */
 @OptIn(ExperimentalCoroutinesApi::class)
 class SingleNotificationChipInteractor
 @AssistedInject
 constructor(
     @Assisted startingModel: ActiveNotificationModel,
+    @Assisted val creationTime: Long,
     private val activityManagerRepository: ActivityManagerRepository,
     @StatusBarChipsLog private val logBuffer: LogBuffer,
 ) {
@@ -142,6 +145,9 @@
 
     @AssistedFactory
     fun interface Factory {
-        fun create(startingModel: ActiveNotificationModel): SingleNotificationChipInteractor
+        fun create(
+            startingModel: ActiveNotificationModel,
+            creationTime: Long,
+        ): SingleNotificationChipInteractor
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractor.kt
index e8cb35b..2121f94 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractor.kt
@@ -29,6 +29,7 @@
 import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
 import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
 import com.android.systemui.util.kotlin.pairwise
+import com.android.systemui.util.time.SystemClock
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -48,6 +49,7 @@
 @Inject
 constructor(
     @Background private val backgroundScope: CoroutineScope,
+    private val systemClock: SystemClock,
     private val activeNotificationsInteractor: ActiveNotificationsInteractor,
     private val singleNotificationChipInteractorFactory: SingleNotificationChipInteractor.Factory,
     @StatusBarChipsLog private val logBuffer: LogBuffer,
@@ -95,21 +97,26 @@
             activeNotificationsInteractor.promotedOngoingNotifications
                 .pairwise(initialValue = emptyList())
                 .collect { (oldNotifs, currentNotifs) ->
-                    val removedNotifs = oldNotifs.minus(currentNotifs.toSet())
-                    removedNotifs.forEach { removedNotif ->
-                        val wasRemoved = promotedNotificationInteractorMap.remove(removedNotif.key)
+                    val removedNotifKeys =
+                        oldNotifs.map { it.key }.minus(currentNotifs.map { it.key }.toSet())
+                    removedNotifKeys.forEach { removedNotifKey ->
+                        val wasRemoved = promotedNotificationInteractorMap.remove(removedNotifKey)
                         if (wasRemoved == null) {
                             logger.w({
                                 "Attempted to remove $str1 from interactor map but it wasn't present"
                             }) {
-                                str1 = removedNotif.key
+                                str1 = removedNotifKey
                             }
                         }
                     }
+
                     currentNotifs.forEach { notif ->
                         val interactor =
                             promotedNotificationInteractorMap.computeIfAbsent(notif.key) {
-                                singleNotificationChipInteractorFactory.create(notif)
+                                singleNotificationChipInteractorFactory.create(
+                                    notif,
+                                    creationTime = systemClock.currentTimeMillis(),
+                                )
                             }
                         interactor.setNotification(notif)
                     }
@@ -130,7 +137,15 @@
     val notificationChips: Flow<List<NotificationChipModel>> =
         if (StatusBarNotifChips.isEnabled) {
             // For all our current interactors...
-            promotedNotificationInteractors.flatMapLatest { interactors ->
+            promotedNotificationInteractors.flatMapLatest { intrs ->
+                // Stable-sort the promoted notifications by when they first appeared so that:
+                // 1) The chips don't switch places if the older chip gets a notification update.
+                // 2) The chips don't switch places when the second chip is tapped. (Whichever
+                // notification is showing heads-up is considered to be the top notification, which
+                // means tapping the second chip would move it to be the first chip if we didn't
+                // sort by appearance time here.)
+                // 3) Older chips get hidden if there's not enough room for all chips.
+                val interactors = intrs.sortedByDescending { it.creationTime }
                 if (interactors.isNotEmpty()) {
                     // Combine each interactor's [notificationChip] flow...
                     val allNotificationChips: List<Flow<NotificationChipModel?>> =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt
index bcd8cfa..2f6431b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt
@@ -33,6 +33,7 @@
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.launch
 
 /** A view model for status bar chips for promoted ongoing notifications. */
@@ -50,11 +51,12 @@
      */
     val chips: Flow<List<OngoingActivityChipModel.Shown>> =
         combine(
-            notifChipsInteractor.notificationChips,
-            headsUpNotificationInteractor.statusBarHeadsUpState,
-        ) { notifications, headsUpState ->
-            notifications.map { it.toActivityChipModel(headsUpState) }
-        }
+                notifChipsInteractor.notificationChips,
+                headsUpNotificationInteractor.statusBarHeadsUpState,
+            ) { notifications, headsUpState ->
+                notifications.map { it.toActivityChipModel(headsUpState) }
+            }
+            .distinctUntilChanged()
 
     /** Converts the notification to the [OngoingActivityChipModel] object. */
     private fun NotificationChipModel.toActivityChipModel(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt
index c40df98..69ef09d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/binder/OngoingActivityChipBinder.kt
@@ -104,12 +104,6 @@
                 defaultIconView.visibility = View.VISIBLE
                 defaultIconView.tintView(iconTint)
             }
-            is OngoingActivityChipModel.ChipIcon.FullColorAppIcon -> {
-                StatusBarNotifChips.assertInNewMode()
-                IconViewBinder.bind(icon.impl, defaultIconView)
-                defaultIconView.visibility = View.VISIBLE
-                defaultIconView.untintView()
-            }
             is OngoingActivityChipModel.ChipIcon.StatusBarView -> {
                 StatusBarConnectedDisplays.assertInLegacyMode()
                 setStatusBarIconView(defaultIconView, icon.impl, iconTint, backgroundView)
@@ -176,10 +170,6 @@
         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(
@@ -252,8 +242,9 @@
         chipTimeView: ChipChronometer,
         chipShortTimeDeltaView: DateTimeView,
     ) {
-        if (chipModel.icon != null) {
-            if (chipModel.icon is OngoingActivityChipModel.ChipIcon.StatusBarView) {
+        val icon = chipModel.icon
+        if (icon != null) {
+            if (iconRequiresEmbeddedPadding(icon)) {
                 // 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 :)
@@ -275,6 +266,10 @@
         }
     }
 
+    private fun iconRequiresEmbeddedPadding(icon: OngoingActivityChipModel.ChipIcon) =
+        icon is OngoingActivityChipModel.ChipIcon.StatusBarView ||
+            icon is OngoingActivityChipModel.ChipIcon.StatusBarNotificationIcon
+
     private fun View.setTextPaddingForEmbeddedPaddingIcon() {
         val newPaddingEnd =
             context.resources.getDimensionPixelSize(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/ChipText.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/ChipText.kt
new file mode 100644
index 0000000..3d768d2
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/ChipText.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.statusbar.chips.ui.compose
+
+import androidx.compose.foundation.layout.sizeIn
+import androidx.compose.material3.LocalTextStyle
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.drawWithContent
+import androidx.compose.ui.geometry.Offset
+import androidx.compose.ui.geometry.Size
+import androidx.compose.ui.graphics.Brush
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.platform.LocalDensity
+import androidx.compose.ui.res.dimensionResource
+import androidx.compose.ui.text.TextLayoutResult
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.rememberTextMeasurer
+import com.android.systemui.res.R
+
+/**
+ * Renders text within a status bar chip. The text is only displayed if more than 50% of its width
+ * can fit inside the bounds of the chip. If there is any overflow,
+ * [R.dimen.ongoing_activity_chip_text_fading_edge_length] is used to fade out the edge of the text.
+ */
+@Composable
+fun ChipText(
+    text: String,
+    backgroundColor: Color,
+    modifier: Modifier = Modifier,
+    color: Color = Color.Unspecified,
+    style: TextStyle = LocalTextStyle.current,
+    minimumVisibleRatio: Float = 0.5f,
+) {
+    val density = LocalDensity.current
+    val textMeasurer = rememberTextMeasurer()
+
+    val textFadeLength =
+        dimensionResource(id = R.dimen.ongoing_activity_chip_text_fading_edge_length)
+    val maxTextWidthDp = dimensionResource(id = R.dimen.ongoing_activity_chip_max_text_width)
+    val maxTextWidthPx = with(density) { maxTextWidthDp.toPx() }
+
+    val textLayoutResult = remember(text, style) { textMeasurer.measure(text, style) }
+    val willOverflowWidth = textLayoutResult.size.width > maxTextWidthPx
+
+    if (isSufficientlyVisible(maxTextWidthPx, minimumVisibleRatio, textLayoutResult)) {
+        Text(
+            text = text,
+            style = style,
+            softWrap = false,
+            color = color,
+            modifier =
+                modifier
+                    .sizeIn(maxWidth = maxTextWidthDp)
+                    .then(
+                        if (willOverflowWidth) {
+                            Modifier.overflowFadeOut(
+                                with(density) { textFadeLength.roundToPx() },
+                                backgroundColor,
+                            )
+                        } else {
+                            Modifier
+                        }
+                    ),
+        )
+    }
+}
+
+private fun Modifier.overflowFadeOut(fadeLength: Int, color: Color): Modifier = drawWithContent {
+    drawContent()
+
+    val brush =
+        Brush.horizontalGradient(
+            colors = listOf(Color.Transparent, color),
+            startX = size.width - fadeLength,
+            endX = size.width,
+        )
+    drawRect(
+        brush = brush,
+        topLeft = Offset(size.width - fadeLength, 0f),
+        size = Size(fadeLength.toFloat(), size.height),
+    )
+}
+
+/**
+ * Returns `true` if at least [minimumVisibleRatio] of the text width fits within the given
+ * [maxAvailableWidthPx].
+ */
+@Composable
+private fun isSufficientlyVisible(
+    maxAvailableWidthPx: Float,
+    minimumVisibleRatio: Float,
+    textLayoutResult: TextLayoutResult,
+): Boolean {
+    val widthPx = textLayoutResult.size.width
+
+    return (maxAvailableWidthPx / widthPx) > minimumVisibleRatio
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/ChronometerText.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/ChronometerText.kt
new file mode 100644
index 0000000..1c14d33
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/ChronometerText.kt
@@ -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.systemui.statusbar.chips.ui.compose
+
+import android.os.SystemClock
+import android.text.format.DateUtils.formatElapsedTime
+import androidx.compose.material3.LocalTextStyle
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableLongStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.text.TextStyle
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.compose.LocalLifecycleOwner
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.statusbar.chips.ui.compose.modifiers.neverDecreaseWidth
+import kotlinx.coroutines.delay
+
+/** Platform-optimized interface for getting current time */
+fun interface TimeSource {
+    fun getCurrentTime(): Long
+}
+
+/** Holds and manages the state for a Chronometer */
+class ChronometerState(private val timeSource: TimeSource, private val startTimeMillis: Long) {
+    private var currentTimeMillis by mutableLongStateOf(0L)
+    private val elapsedTimeMillis: Long
+        get() = maxOf(0L, currentTimeMillis - startTimeMillis)
+
+    val currentTimeText: String by derivedStateOf { formatElapsedTime(elapsedTimeMillis / 1000) }
+
+    suspend fun run() {
+        while (true) {
+            currentTimeMillis = timeSource.getCurrentTime()
+            val delaySkewMillis = (currentTimeMillis - startTimeMillis) % 1000L
+            delay(1000L - delaySkewMillis)
+        }
+    }
+}
+
+/** Remember and manage the ChronometerState */
+@Composable
+fun rememberChronometerState(timeSource: TimeSource, startTimeMillis: Long): ChronometerState {
+    val state =
+        remember(timeSource, startTimeMillis) { ChronometerState(timeSource, startTimeMillis) }
+    val lifecycleOwner = LocalLifecycleOwner.current
+    LaunchedEffect(lifecycleOwner, timeSource, startTimeMillis) {
+        lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { state.run() }
+    }
+
+    return state
+}
+
+/**
+ * A composable chronometer that displays elapsed time with constrained width. The width of the text
+ * is only allowed to increase. This ensures there is no visual jitter when individual digits in the
+ * text change due to the timer ticking.
+ */
+@Composable
+fun ChronometerText(
+    startTimeMillis: Long,
+    modifier: Modifier = Modifier,
+    color: Color = Color.Unspecified,
+    style: TextStyle = LocalTextStyle.current,
+    timeSource: TimeSource = remember { TimeSource { SystemClock.elapsedRealtime() } },
+) {
+    val state = rememberChronometerState(timeSource, startTimeMillis)
+    Text(
+        text = state.currentTimeText,
+        style = style,
+        color = color,
+        modifier = modifier.neverDecreaseWidth(),
+    )
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/modifiers/NeverDecreaseWidth.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/modifiers/NeverDecreaseWidth.kt
new file mode 100644
index 0000000..505a5fc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/modifiers/NeverDecreaseWidth.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.compose.modifiers
+
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.layout.Measurable
+import androidx.compose.ui.layout.MeasureResult
+import androidx.compose.ui.layout.MeasureScope
+import androidx.compose.ui.node.LayoutModifierNode
+import androidx.compose.ui.node.ModifierNodeElement
+import androidx.compose.ui.unit.Constraints
+import androidx.compose.ui.unit.constrain
+
+/** A modifier that ensures the width of the content only increases and never decreases. */
+fun Modifier.neverDecreaseWidth(): Modifier {
+    return this.then(neverDecreaseWidthElement)
+}
+
+private data object neverDecreaseWidthElement : ModifierNodeElement<NeverDecreaseWidthNode>() {
+    override fun create(): NeverDecreaseWidthNode {
+        return NeverDecreaseWidthNode()
+    }
+
+    override fun update(node: NeverDecreaseWidthNode) {
+        error("This should never be called")
+    }
+}
+
+private class NeverDecreaseWidthNode : Modifier.Node(), LayoutModifierNode {
+    private var minWidth = 0
+
+    override fun MeasureScope.measure(
+        measurable: Measurable,
+        constraints: Constraints,
+    ): MeasureResult {
+        val placeable = measurable.measure(Constraints(minWidth = minWidth).constrain(constraints))
+        val width = placeable.width
+        val height = placeable.height
+
+        minWidth = maxOf(minWidth, width)
+
+        return layout(width, height) { placeable.place(0, 0) }
+    }
+}
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 efedf41..cac25d0 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,26 +39,12 @@
             Utils.getColorAttrDefaultColor(context, com.android.internal.R.attr.colorPrimary)
     }
 
-    /**
-     * The chip should have the given background color and primary text color.
-     *
-     * If [primaryTextColorInt] is null, the text color will match the current UI mode (light/dark).
-     */
-    data class Custom(val backgroundColorInt: Int, val primaryTextColorInt: Int? = null) :
-        ColorsModel {
+    /** The chip should have the given background color and primary text color. */
+    data class Custom(val backgroundColorInt: Int, val primaryTextColorInt: Int) : ColorsModel {
         override fun background(context: Context): ColorStateList =
             ColorStateList.valueOf(backgroundColorInt)
 
-        // TODO(b/361346412): When UI mode 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 UI mode changes.
-        override fun text(context: Context): Int {
-            return primaryTextColorInt
-                ?: Utils.getColorAttrDefaultColor(
-                    context,
-                    com.android.internal.R.color.materialColorOnSurface,
-                )
-        }
+        override fun text(context: Context): Int = primaryTextColorInt
     }
 
     /** The chip should have a red background with white text. */
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 18217d7..c81e8e2 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
@@ -153,8 +153,5 @@
          * 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 45efc57..baa9d8b 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
@@ -24,20 +24,19 @@
 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.notification.demo.ui.viewmodel.DemoNotifChipViewModel
 import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
 import com.android.systemui.statusbar.chips.notification.ui.viewmodel.NotifChipsViewModel
 import com.android.systemui.statusbar.chips.screenrecord.ui.viewmodel.ScreenRecordChipViewModel
 import com.android.systemui.statusbar.chips.sharetoapp.ui.viewmodel.ShareToAppChipViewModel
 import com.android.systemui.statusbar.chips.ui.model.MultipleOngoingActivityChipsModel
 import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel
-import com.android.systemui.util.kotlin.combine
 import com.android.systemui.util.kotlin.pairwise
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.stateIn
 
@@ -57,7 +56,6 @@
     castToOtherDeviceChipViewModel: CastToOtherDeviceChipViewModel,
     callChipViewModel: CallChipViewModel,
     notifChipsViewModel: NotifChipsViewModel,
-    demoNotifChipViewModel: DemoNotifChipViewModel,
     @StatusBarChipsLog private val logger: LogBuffer,
 ) {
     private enum class ChipType {
@@ -66,8 +64,6 @@
         CastToOtherDevice,
         Call,
         Notification,
-        /** A demo of a notification chip, used just for testing. */
-        DemoNotification,
     }
 
     /** Model that helps us internally track the various chip states from each of the types. */
@@ -89,7 +85,6 @@
             val castToOtherDevice: OngoingActivityChipModel.Hidden,
             val call: OngoingActivityChipModel.Hidden,
             val notifs: OngoingActivityChipModel.Hidden,
-            val demoNotif: OngoingActivityChipModel.Hidden,
         ) : InternalChipModel
     }
 
@@ -99,7 +94,6 @@
         val castToOtherDevice: OngoingActivityChipModel = OngoingActivityChipModel.Hidden(),
         val call: OngoingActivityChipModel = OngoingActivityChipModel.Hidden(),
         val notifs: List<OngoingActivityChipModel.Shown> = emptyList(),
-        val demoNotif: OngoingActivityChipModel = OngoingActivityChipModel.Hidden(),
     )
 
     /** Bundles all the incoming chips into one object to easily pass to various flows. */
@@ -110,8 +104,7 @@
                 castToOtherDeviceChipViewModel.chip,
                 callChipViewModel.chip,
                 notifChipsViewModel.chips,
-                demoNotifChipViewModel.chip,
-            ) { screenRecord, shareToApp, castToOtherDevice, call, notifs, demoNotif ->
+            ) { screenRecord, shareToApp, castToOtherDevice, call, notifs ->
                 logger.log(
                     TAG,
                     LogLevel.INFO,
@@ -129,9 +122,8 @@
                         str1 = call.logName
                         // TODO(b/364653005): Log other information for notification chips.
                         str2 = notifs.map { it.logName }.toString()
-                        str3 = demoNotif.logName
                     },
-                    { "... > Call=$str1 > Notifs=$str2 > DemoNotif=$str3" },
+                    { "... > Call=$str1 > Notifs=$str2" },
                 )
                 ChipBundle(
                     screenRecord = screenRecord,
@@ -139,7 +131,6 @@
                     castToOtherDevice = castToOtherDevice,
                     call = call,
                     notifs = notifs,
-                    demoNotif = demoNotif,
                 )
             }
             // Some of the chips could have timers in them and we don't want the start time
@@ -282,14 +273,6 @@
                     remainingChips =
                         bundle.copy(notifs = bundle.notifs.subList(1, bundle.notifs.size)),
                 )
-            bundle.demoNotif is OngoingActivityChipModel.Shown -> {
-                StatusBarNotifChips.assertInNewMode()
-                MostImportantChipResult(
-                    mostImportantChip =
-                        InternalChipModel.Shown(ChipType.DemoNotification, bundle.demoNotif),
-                    remainingChips = bundle.copy(demoNotif = OngoingActivityChipModel.Hidden()),
-                )
-            }
             else -> {
                 // We should only get here if all chip types are hidden
                 check(bundle.screenRecord is OngoingActivityChipModel.Hidden)
@@ -297,7 +280,6 @@
                 check(bundle.castToOtherDevice is OngoingActivityChipModel.Hidden)
                 check(bundle.call is OngoingActivityChipModel.Hidden)
                 check(bundle.notifs.isEmpty())
-                check(bundle.demoNotif is OngoingActivityChipModel.Hidden)
                 MostImportantChipResult(
                     mostImportantChip =
                         InternalChipModel.Hidden(
@@ -306,7 +288,6 @@
                             castToOtherDevice = bundle.castToOtherDevice,
                             call = bundle.call,
                             notifs = OngoingActivityChipModel.Hidden(),
-                            demoNotif = bundle.demoNotif,
                         ),
                     // All the chips are already hidden, so no need to filter anything out of the
                     // bundle.
@@ -335,7 +316,6 @@
                 ChipType.CastToOtherDevice -> new.castToOtherDevice
                 ChipType.Call -> new.call
                 ChipType.Notification -> new.notifs
-                ChipType.DemoNotification -> new.demoNotif
             }
         } else if (new is InternalChipModel.Shown) {
             // If we have a chip to show, always show it.
@@ -357,7 +337,6 @@
                 castToOtherDevice = OngoingActivityChipModel.Hidden(),
                 call = OngoingActivityChipModel.Hidden(),
                 notifs = OngoingActivityChipModel.Hidden(),
-                demoNotif = OngoingActivityChipModel.Hidden(),
             )
 
         private val DEFAULT_MULTIPLE_INTERNAL_HIDDEN_MODEL =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/CallbackHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/CallbackHandler.java
index 5391992..03d6494 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/CallbackHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/CallbackHandler.java
@@ -171,28 +171,6 @@
     }
 
     @Override
-    public void setCallIndicator(IconState statusIcon, int subId) {
-        String currentCallback = new StringBuilder()
-                .append("setCallIndicator: ")
-                .append("statusIcon=").append(statusIcon).append(",")
-                .append("subId=").append(subId)
-                .toString();
-        if (!currentCallback.equals(mLastCallback)) {
-            mLastCallback = currentCallback;
-            String log = new StringBuilder()
-                    .append(SSDF.format(System.currentTimeMillis())).append(",")
-                    .append(currentCallback).append(",")
-                    .toString();
-            recordLastCallback(log);
-        }
-        post(() -> {
-            for (SignalCallback signalCluster : mSignalCallbacks) {
-                signalCluster.setCallIndicator(statusIcon, subId);
-            }
-        });
-    }
-
-    @Override
     public void setSubs(List<SubscriptionInfo> subs) {
         String currentCallback = new StringBuilder()
                 .append("setSubs: ")
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/EthernetSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/EthernetSignalController.java
index acd9779..70f7135 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/EthernetSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/EthernetSignalController.java
@@ -20,10 +20,12 @@
 
 import com.android.settingslib.AccessibilityContentDescriptions;
 import com.android.settingslib.SignalIcon.IconGroup;
+import com.android.systemui.statusbar.pipeline.ethernet.domain.EthernetInteractor;
 
 import java.util.BitSet;
 
-/** */
+/** @deprecated use {@link EthernetInteractor} instead. */
+@Deprecated
 public class EthernetSignalController extends
         SignalController<ConnectivityState, IconGroup> {
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java
index d6df987..a6ac372 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/MobileSignalController.java
@@ -44,6 +44,7 @@
 import com.android.settingslib.mobile.TelephonyIcons;
 import com.android.settingslib.net.SignalStrengthUtil;
 import com.android.systemui.res.R;
+import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor;
 import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy;
 import com.android.systemui.util.CarrierConfigTracker;
 
@@ -55,7 +56,10 @@
 
 /**
  * Monitors the mobile signal changes and update the SysUI icons.
+ *
+ * @deprecated Use {@link MobileIconsInteractor} instead.
  */
+@Deprecated
 public class MobileSignalController extends SignalController<MobileState, MobileIconGroup> {
     private static final SimpleDateFormat SSDF = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
     private static final int STATUS_HISTORY_SIZE = 64;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/SignalCallback.kt b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/SignalCallback.kt
index 6be407a..cf8240d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/SignalCallback.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/SignalCallback.kt
@@ -22,7 +22,13 @@
  * SignalCallback contains all of the connectivity updates from [NetworkController]. Implement this
  * interface to be able to draw iconography for Wi-Fi, mobile data, ethernet, call strength
  * indicators, etc.
+ *
+ * @deprecated
  */
+@Deprecated(
+    "Use Recommended Architecture classes instead: MobileIconsInteractor, WifiInteractor, " +
+        "AirplaneModeInteractor, and EthernetInteractor"
+)
 interface SignalCallback {
     /**
      * Called when the Wi-Fi iconography has been updated. Implement this method to draw Wi-Fi icons
@@ -35,8 +41,8 @@
      * Called when the mobile iconography has been updated. Implement this method to draw mobile
      * indicators
      *
-     * @param mobileDataIndicators a box type containing enough information to properly draw
-     * mobile data icons
+     * @param mobileDataIndicators a box type containing enough information to properly draw mobile
+     *   data icons
      *
      * NOTE: phones can have multiple subscriptions, so this [mobileDataIndicators] object should be
      * indexed based on its [subId][MobileDataIndicators.subId]
@@ -44,8 +50,8 @@
     fun setMobileDataIndicators(mobileDataIndicators: MobileDataIndicators) {}
 
     /**
-     * Called when the list of mobile data subscriptions has changed. Use this method as a chance
-     * to remove views that are no longer needed, or to make room for new icons to come in
+     * Called when the list of mobile data subscriptions has changed. Use this method as a chance to
+     * remove views that are no longer needed, or to make room for new icons to come in
      *
      * @param subs a [SubscriptionInfo] for each subscription that we know about
      */
@@ -53,8 +59,7 @@
 
     /**
      * Called when:
-     * 1. The number of [MobileSignalController]s goes to 0 while mobile data is enabled
-     * OR
+     * 1. The number of [MobileSignalController]s goes to 0 while mobile data is enabled OR
      * 2. The presence of any SIM changes
      *
      * @param show whether or not to show a "no sim" view
@@ -86,6 +91,7 @@
 
     /**
      * Callback for listeners to be able to update the connectivity status
+     *
      * @param noDefaultNetwork whether there is any default network.
      * @param noValidatedNetwork whether there is any validated network.
      * @param noNetworksAvailable whether there is any WiFi networks available.
@@ -93,15 +99,8 @@
     fun setConnectivityStatus(
         noDefaultNetwork: Boolean,
         noValidatedNetwork: Boolean,
-        noNetworksAvailable: Boolean
-    ) { }
-
-    /**
-     * Callback for listeners to be able to update the call indicator
-     * @param statusIcon the icon for the call indicator
-     * @param subId subscription ID for which to update the UI
-     */
-    fun setCallIndicator(statusIcon: IconState, subId: Int) {}
+        noNetworksAvailable: Boolean,
+    ) {}
 }
 
 /** Box type for [SignalCallback.setWifiIndicators] */
@@ -113,19 +112,28 @@
     @JvmField val activityOut: Boolean,
     @JvmField val description: String?,
     @JvmField val isTransient: Boolean,
-    @JvmField val statusLabel: String?
+    @JvmField val statusLabel: String?,
 ) {
     override fun toString(): String {
         return StringBuilder("WifiIndicators[")
-                .append("enabled=").append(enabled)
-                .append(",statusIcon=").append(statusIcon?.toString() ?: "")
-                .append(",qsIcon=").append(qsIcon?.toString() ?: "")
-                .append(",activityIn=").append(activityIn)
-                .append(",activityOut=").append(activityOut)
-                .append(",qsDescription=").append(description)
-                .append(",isTransient=").append(isTransient)
-                .append(",statusLabel=").append(statusLabel)
-                .append(']').toString()
+            .append("enabled=")
+            .append(enabled)
+            .append(",statusIcon=")
+            .append(statusIcon?.toString() ?: "")
+            .append(",qsIcon=")
+            .append(qsIcon?.toString() ?: "")
+            .append(",activityIn=")
+            .append(activityIn)
+            .append(",activityOut=")
+            .append(activityOut)
+            .append(",qsDescription=")
+            .append(description)
+            .append(",isTransient=")
+            .append(isTransient)
+            .append(",statusLabel=")
+            .append(statusLabel)
+            .append(']')
+            .toString()
     }
 }
 
@@ -142,23 +150,37 @@
     @JvmField val qsDescription: CharSequence?,
     @JvmField val subId: Int,
     @JvmField val roaming: Boolean,
-    @JvmField val showTriangle: Boolean
+    @JvmField val showTriangle: Boolean,
 ) {
     override fun toString(): String {
-        return java.lang.StringBuilder("MobileDataIndicators[")
-                .append("statusIcon=").append(statusIcon?.toString() ?: "")
-                .append(",qsIcon=").append(qsIcon?.toString() ?: "")
-                .append(",statusType=").append(statusType)
-                .append(",qsType=").append(qsType)
-                .append(",activityIn=").append(activityIn)
-                .append(",activityOut=").append(activityOut)
-                .append(",typeContentDescription=").append(typeContentDescription)
-                .append(",typeContentDescriptionHtml=").append(typeContentDescriptionHtml)
-                .append(",description=").append(qsDescription)
-                .append(",subId=").append(subId)
-                .append(",roaming=").append(roaming)
-                .append(",showTriangle=").append(showTriangle)
-                .append(']').toString()
+        return java.lang
+            .StringBuilder("MobileDataIndicators[")
+            .append("statusIcon=")
+            .append(statusIcon?.toString() ?: "")
+            .append(",qsIcon=")
+            .append(qsIcon?.toString() ?: "")
+            .append(",statusType=")
+            .append(statusType)
+            .append(",qsType=")
+            .append(qsType)
+            .append(",activityIn=")
+            .append(activityIn)
+            .append(",activityOut=")
+            .append(activityOut)
+            .append(",typeContentDescription=")
+            .append(typeContentDescription)
+            .append(",typeContentDescriptionHtml=")
+            .append(typeContentDescriptionHtml)
+            .append(",description=")
+            .append(qsDescription)
+            .append(",subId=")
+            .append(subId)
+            .append(",roaming=")
+            .append(roaming)
+            .append(",showTriangle=")
+            .append(showTriangle)
+            .append(']')
+            .toString()
     }
 }
 
@@ -166,13 +188,20 @@
 data class IconState(
     @JvmField val visible: Boolean,
     @JvmField val icon: Int,
-    @JvmField val contentDescription: String
+    @JvmField val contentDescription: String,
 ) {
     override fun toString(): String {
         val builder = java.lang.StringBuilder()
-        return builder.append("[visible=").append(visible).append(',')
-                .append("icon=").append(icon).append(',')
-                .append("contentDescription=").append(contentDescription).append(']')
-                .toString()
+        return builder
+            .append("[visible=")
+            .append(visible)
+            .append(',')
+            .append("icon=")
+            .append(icon)
+            .append(',')
+            .append("contentDescription=")
+            .append(contentDescription)
+            .append(']')
+            .toString()
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/SignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/SignalController.java
index 2d2ee4a..8cb3121 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/SignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/SignalController.java
@@ -34,7 +34,11 @@
  *
  * @param <T> State of the SysUI controller.
  * @param <I> Icon groups of the SysUI controller for a given State.
+ *
+ * @deprecated "Use Recommended Architecture classes instead: MobileIconsInteractor, WifiInteractor,
+ * AirplaneModeInteractor, and EthernetInteractor
  */
+@Deprecated
 public abstract class SignalController<T extends ConnectivityState, I extends IconGroup> {
     // Save the previous SignalController.States of all SignalControllers for dumps.
     static final boolean RECORD_HISTORY = true;
@@ -167,10 +171,6 @@
         }
     }
 
-    protected final void notifyCallStateChange(IconState statusIcon, int subId) {
-        mCallbackHandler.setCallIndicator(statusIcon, subId);
-    }
-
     /**
      * Returns the resource if resId is not 0, and an empty string otherwise.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java
index 0e572be..9854e27 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiSignalController.java
@@ -36,11 +36,13 @@
 import com.android.settingslib.wifi.WifiStatusTracker;
 import com.android.systemui.dagger.qualifiers.Background;
 import com.android.systemui.res.R;
+import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor;
 
 import java.io.PrintWriter;
 import java.util.BitSet;
 
-/** */
+/** @deprecated use {@link WifiInteractor} instead. */
+@Deprecated
 public class WifiSignalController extends SignalController<WifiState, IconGroup> {
     private final boolean mHasMobileDataFeature;
     private final WifiStatusTracker mWifiTracker;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/CommandQueueInitializer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/CommandQueueInitializer.kt
index d24edda..d25ca28 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/core/CommandQueueInitializer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/CommandQueueInitializer.kt
@@ -72,7 +72,7 @@
 
     private fun initializeStatusBarForDisplay(displayId: Int, result: RegisterStatusBarResult) {
         if ((result.mTransientBarTypes and WindowInsets.Type.statusBars()) != 0) {
-            statusBarModeRepository.forDisplay(displayId).showTransient()
+            statusBarModeRepository.forDisplay(displayId)?.showTransient()
         }
         val commandQueueCallbacks = commandQueueCallbacksLazy.get()
         commandQueueCallbacks.onSystemBarAttributesChanged(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarter.kt
index 9e9a38e..b057fb0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarter.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarStarter.kt
@@ -89,21 +89,26 @@
     }
 
     private fun createAndStartOrchestratorForDisplay(displayId: Int) {
+        val statusBarModeRepository = statusBarModeRepositoryStore.forDisplay(displayId) ?: return
+        val statusBarInitializer = initializerStore.forDisplay(displayId) ?: return
+        val statusBarWindowController =
+            statusBarWindowControllerStore.forDisplay(displayId) ?: return
+        val autoHideController = autoHideControllerStore.forDisplay(displayId) ?: return
         statusBarOrchestratorFactory
             .create(
                 displayId,
                 displayScopeRepository.scopeForDisplay(displayId),
                 statusBarWindowStateRepositoryStore.forDisplay(displayId),
-                statusBarModeRepositoryStore.forDisplay(displayId),
-                initializerStore.forDisplay(displayId),
-                statusBarWindowControllerStore.forDisplay(displayId),
-                autoHideControllerStore.forDisplay(displayId),
+                statusBarModeRepository,
+                statusBarInitializer,
+                statusBarWindowController,
+                autoHideController,
             )
             .start()
     }
 
     private fun createAndStartInitializerForDisplay(displayId: Int) {
-        statusBarInitializerStore.forDisplay(displayId).start()
+        statusBarInitializerStore.forDisplay(displayId)?.start()
     }
 
     private fun startPrivacyDotForDisplay(displayId: Int) {
@@ -111,6 +116,6 @@
             // For the default display, privacy dot is started via ScreenDecorations
             return
         }
-        privacyDotWindowControllerStore.forDisplay(displayId).start()
+        privacyDotWindowControllerStore.forDisplay(displayId)?.start()
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt
index 4c54fc4..1e127ee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializer.kt
@@ -20,9 +20,11 @@
 import androidx.annotation.VisibleForTesting
 import com.android.systemui.CoreStartable
 import com.android.systemui.fragments.FragmentHostManager
+import com.android.systemui.plugins.DarkIconDispatcher
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.core.StatusBarInitializer.OnStatusBarViewInitializedListener
 import com.android.systemui.statusbar.core.StatusBarInitializer.OnStatusBarViewUpdatedListener
+import com.android.systemui.statusbar.data.repository.StatusBarConfigurationController
 import com.android.systemui.statusbar.data.repository.StatusBarModePerDisplayRepository
 import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions
 import com.android.systemui.statusbar.phone.PhoneStatusBarView
@@ -34,7 +36,6 @@
 import dagger.assisted.Assisted
 import dagger.assisted.AssistedFactory
 import dagger.assisted.AssistedInject
-import java.lang.IllegalStateException
 import javax.inject.Provider
 
 /**
@@ -75,6 +76,8 @@
         fun create(
             statusBarWindowController: StatusBarWindowController,
             statusBarModePerDisplayRepository: StatusBarModePerDisplayRepository,
+            statusBarConfigurationController: StatusBarConfigurationController,
+            darkIconDispatcher: DarkIconDispatcher,
         ): StatusBarInitializer
     }
 }
@@ -84,6 +87,8 @@
 constructor(
     @Assisted private val statusBarWindowController: StatusBarWindowController,
     @Assisted private val statusBarModePerDisplayRepository: StatusBarModePerDisplayRepository,
+    @Assisted private val statusBarConfigurationController: StatusBarConfigurationController,
+    @Assisted private val darkIconDispatcher: DarkIconDispatcher,
     private val collapsedStatusBarFragmentProvider: Provider<CollapsedStatusBarFragment>,
     private val statusBarRootFactory: StatusBarRootFactory,
     private val componentFactory: HomeStatusBarComponent.Factory,
@@ -131,23 +136,32 @@
                 ->
                 val phoneStatusBarView = cv.findViewById<PhoneStatusBarView>(R.id.status_bar)
                 component =
-                    componentFactory.create(phoneStatusBarView).also { component ->
-                        // CollapsedStatusBarFragment used to be responsible initializing
-                        component.init()
-
-                        statusBarViewUpdatedListener?.onStatusBarViewUpdated(
-                            component.phoneStatusBarViewController,
-                            component.phoneStatusBarTransitions,
+                    componentFactory
+                        .create(
+                            phoneStatusBarView,
+                            statusBarConfigurationController,
+                            statusBarWindowController,
+                            darkIconDispatcher,
                         )
+                        .also { component ->
+                            // CollapsedStatusBarFragment used to be responsible initializing
+                            component.init()
 
-                        if (StatusBarConnectedDisplays.isEnabled) {
-                            statusBarModePerDisplayRepository.onStatusBarViewInitialized(component)
-                        } else {
-                            creationListeners.forEach { listener ->
-                                listener.onStatusBarViewInitialized(component)
+                            statusBarViewUpdatedListener?.onStatusBarViewUpdated(
+                                component.phoneStatusBarViewController,
+                                component.phoneStatusBarTransitions,
+                            )
+
+                            if (StatusBarConnectedDisplays.isEnabled) {
+                                statusBarModePerDisplayRepository.onStatusBarViewInitialized(
+                                    component
+                                )
+                            } else {
+                                creationListeners.forEach { listener ->
+                                    listener.onStatusBarViewInitialized(component)
+                                }
                             }
                         }
-                    }
             }
 
         // Add the new compose view to the hierarchy because we don't use fragment transactions
@@ -163,9 +177,11 @@
                 CollapsedStatusBarFragment.TAG,
                 object : FragmentHostManager.FragmentListener {
                     override fun onFragmentViewCreated(tag: String, fragment: Fragment) {
-                        component =
-                            (fragment as CollapsedStatusBarFragment).homeStatusBarComponent
-                                ?: throw IllegalStateException()
+                        val statusBarFragment = fragment as CollapsedStatusBarFragment
+                        if (statusBarFragment.homeStatusBarComponent == null) {
+                            return
+                        }
+                        component = fragment.homeStatusBarComponent
                         statusBarViewUpdatedListener?.onStatusBarViewUpdated(
                             component!!.phoneStatusBarViewController,
                             component!!.phoneStatusBarTransitions,
@@ -195,6 +211,8 @@
         override fun create(
             statusBarWindowController: StatusBarWindowController,
             statusBarModePerDisplayRepository: StatusBarModePerDisplayRepository,
+            statusBarConfigurationController: StatusBarConfigurationController,
+            darkIconDispatcher: DarkIconDispatcher,
         ): StatusBarInitializerImpl
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializerStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializerStore.kt
index 4f815c1..de6cd07 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializerStore.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarInitializerStore.kt
@@ -22,6 +22,8 @@
 import com.android.systemui.display.data.repository.PerDisplayStore
 import com.android.systemui.display.data.repository.PerDisplayStoreImpl
 import com.android.systemui.display.data.repository.SingleDisplayStore
+import com.android.systemui.statusbar.data.repository.DarkIconDispatcherStore
+import com.android.systemui.statusbar.data.repository.StatusBarConfigurationControllerStore
 import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore
 import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
 import javax.inject.Inject
@@ -39,6 +41,8 @@
     private val factory: StatusBarInitializer.Factory,
     private val statusBarWindowControllerStore: StatusBarWindowControllerStore,
     private val statusBarModeRepositoryStore: StatusBarModeRepositoryStore,
+    private val statusBarConfigurationControllerStore: StatusBarConfigurationControllerStore,
+    private val darkIconDispatcherStore: DarkIconDispatcherStore,
 ) :
     StatusBarInitializerStore,
     PerDisplayStoreImpl<StatusBarInitializer>(backgroundApplicationScope, displayRepository) {
@@ -47,10 +51,19 @@
         StatusBarConnectedDisplays.assertInNewMode()
     }
 
-    override fun createInstanceForDisplay(displayId: Int): StatusBarInitializer {
+    override fun createInstanceForDisplay(displayId: Int): StatusBarInitializer? {
+        val statusBarWindowController =
+            statusBarWindowControllerStore.forDisplay(displayId) ?: return null
+        val statusBarModePerDisplayRepository =
+            statusBarModeRepositoryStore.forDisplay(displayId) ?: return null
+        val statusBarConfigurationController =
+            statusBarConfigurationControllerStore.forDisplay(displayId) ?: return null
+        val darkIconDispatcher = darkIconDispatcherStore.forDisplay(displayId) ?: return null
         return factory.create(
-            statusBarWindowController = statusBarWindowControllerStore.forDisplay(displayId),
-            statusBarModePerDisplayRepository = statusBarModeRepositoryStore.forDisplay(displayId),
+            statusBarWindowController,
+            statusBarModePerDisplayRepository,
+            statusBarConfigurationController,
+            darkIconDispatcher,
         )
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt
index f91c5dd..9d55f6c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/StatusBarOrchestrator.kt
@@ -39,8 +39,8 @@
 import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions
 import com.android.systemui.statusbar.phone.PhoneStatusBarViewController
 import com.android.systemui.statusbar.window.StatusBarWindowController
-import com.android.systemui.statusbar.window.data.model.StatusBarWindowState
 import com.android.systemui.statusbar.window.data.repository.StatusBarWindowStatePerDisplayRepository
+import com.android.systemui.statusbar.window.shared.model.StatusBarWindowState
 import com.android.wm.shell.bubbles.Bubbles
 import dagger.Lazy
 import dagger.assisted.Assisted
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
index 254b792..d327fc2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
@@ -16,8 +16,6 @@
 
 package com.android.systemui.statusbar.dagger;
 
-import static com.android.systemui.Flags.predictiveBackAnimateDialogs;
-
 import android.content.Context;
 import android.os.Handler;
 import android.os.RemoteException;
@@ -28,7 +26,6 @@
 import com.android.internal.statusbar.IStatusBarService;
 import com.android.systemui.CoreStartable;
 import com.android.systemui.animation.ActivityTransitionAnimator;
-import com.android.systemui.animation.AnimationFeatureFlags;
 import com.android.systemui.animation.DialogTransitionAnimator;
 import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
 import com.android.systemui.dagger.SysUISingleton;
@@ -226,8 +223,7 @@
             IDreamManager dreamManager,
             KeyguardStateController keyguardStateController,
             Lazy<AlternateBouncerInteractor> alternateBouncerInteractor,
-            InteractionJankMonitor interactionJankMonitor,
-            AnimationFeatureFlags animationFeatureFlags) {
+            InteractionJankMonitor interactionJankMonitor) {
         DialogTransitionAnimator.Callback callback = new DialogTransitionAnimator.Callback() {
             @Override
             public boolean isDreaming() {
@@ -249,19 +245,6 @@
                 return alternateBouncerInteractor.get().canShowAlternateBouncerForFingerprint();
             }
         };
-        return new DialogTransitionAnimator(
-                mainExecutor, callback, interactionJankMonitor, animationFeatureFlags);
-    }
-
-    /** */
-    @Provides
-    @SysUISingleton
-    static AnimationFeatureFlags provideAnimationFeatureFlags() {
-        return new AnimationFeatureFlags() {
-            @Override
-            public boolean isPredictiveBackQsDialogAnim() {
-                return predictiveBackAnimateDialogs();
-            }
-        };
+        return new DialogTransitionAnimator(mainExecutor, callback, interactionJankMonitor);
     }
 }
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 46c84fbc..eff959d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/StatusBarModule.kt
@@ -27,12 +27,12 @@
 import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
 import com.android.systemui.statusbar.data.StatusBarDataLayerModule
 import com.android.systemui.statusbar.data.repository.LightBarControllerStore
+import com.android.systemui.statusbar.layout.StatusBarContentInsetsProvider
+import com.android.systemui.statusbar.layout.StatusBarContentInsetsProviderImpl
 import com.android.systemui.statusbar.phone.AutoHideController
 import com.android.systemui.statusbar.phone.AutoHideControllerImpl
 import com.android.systemui.statusbar.phone.LightBarController
 import com.android.systemui.statusbar.phone.LightBarControllerImpl
-import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
-import com.android.systemui.statusbar.phone.StatusBarContentInsetsProviderImpl
 import com.android.systemui.statusbar.phone.StatusBarSignalPolicy
 import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallController
 import com.android.systemui.statusbar.phone.ongoingcall.OngoingCallLog
@@ -91,9 +91,7 @@
         @SysUISingleton
         @IntoMap
         @ClassKey(OngoingCallController::class)
-        fun ongoingCallController(
-            controller: OngoingCallController
-        ): CoreStartable =
+        fun ongoingCallController(controller: OngoingCallController): CoreStartable =
             if (StatusBarChipsModernization.isEnabled) {
                 CoreStartable.NOP
             } else {
@@ -104,9 +102,7 @@
         @SysUISingleton
         @IntoMap
         @ClassKey(OngoingCallInteractor::class)
-        fun ongoingCallInteractor(
-            interactor: OngoingCallInteractor
-        ): CoreStartable =
+        fun ongoingCallInteractor(interactor: OngoingCallInteractor): CoreStartable =
             if (StatusBarChipsModernization.isEnabled) {
                 interactor
             } else {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/model/StatusBarAppearance.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/model/StatusBarAppearance.kt
index 0cd31d0..b7b91fa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/data/model/StatusBarAppearance.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/model/StatusBarAppearance.kt
@@ -17,7 +17,7 @@
 package com.android.systemui.statusbar.data.model
 
 import com.android.internal.view.AppearanceRegion
-import com.android.systemui.statusbar.phone.BoundsPair
+import com.android.systemui.statusbar.layout.BoundsPair
 
 /** Keeps track of various parameters coordinating the appearance of the status bar. */
 data class StatusBarAppearance(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/DarkIconDispatcherStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/DarkIconDispatcherStore.kt
index 8183a48..041f3c8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/DarkIconDispatcherStore.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/DarkIconDispatcherStore.kt
@@ -65,8 +65,9 @@
         StatusBarConnectedDisplays.assertInNewMode()
     }
 
-    override fun createInstanceForDisplay(displayId: Int): SysuiDarkIconDispatcher {
-        val properties = displayWindowPropertiesRepository.get(displayId, TYPE_STATUS_BAR)
+    override fun createInstanceForDisplay(displayId: Int): SysuiDarkIconDispatcher? {
+        val properties =
+            displayWindowPropertiesRepository.get(displayId, TYPE_STATUS_BAR) ?: return null
         return factory.create(displayId, properties.context)
     }
 
@@ -103,7 +104,7 @@
     override val defaultDisplay: DarkIconDispatcher
         get() = store.defaultDisplay
 
-    override fun forDisplay(displayId: Int): DarkIconDispatcher = store.forDisplay(displayId)
+    override fun forDisplay(displayId: Int): DarkIconDispatcher? = store.forDisplay(displayId)
 }
 
 @Module
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/LightBarControllerStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/LightBarControllerStore.kt
index e498755..c629d10 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/LightBarControllerStore.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/LightBarControllerStore.kt
@@ -49,13 +49,16 @@
     LightBarControllerStore,
     PerDisplayStoreImpl<LightBarController>(backgroundApplicationScope, displayRepository) {
 
-    override fun createInstanceForDisplay(displayId: Int): LightBarController {
+    override fun createInstanceForDisplay(displayId: Int): LightBarController? {
+        val darkIconDispatcher = darkIconDispatcherStore.forDisplay(displayId) ?: return null
+        val statusBarModePerDisplayRepository =
+            statusBarModeRepositoryStore.forDisplay(displayId) ?: return null
         return factory
             .create(
                 displayId,
                 displayScopeRepository.scopeForDisplay(displayId),
-                darkIconDispatcherStore.forDisplay(displayId),
-                statusBarModeRepositoryStore.forDisplay(displayId),
+                darkIconDispatcher,
+                statusBarModePerDisplayRepository,
             )
             .also { it.start() }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/PrivacyDotViewControllerStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/PrivacyDotViewControllerStore.kt
index bd61c44..d48c94b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/PrivacyDotViewControllerStore.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/PrivacyDotViewControllerStore.kt
@@ -52,11 +52,14 @@
     PrivacyDotViewControllerStore,
     PerDisplayStoreImpl<PrivacyDotViewController>(backgroundApplicationScope, displayRepository) {
 
-    override fun createInstanceForDisplay(displayId: Int): PrivacyDotViewController {
+    override fun createInstanceForDisplay(displayId: Int): PrivacyDotViewController? {
+        val configurationController =
+            statusBarConfigurationControllerStore.forDisplay(displayId) ?: return null
+        val contentInsetsProvider = contentInsetsProviderStore.forDisplay(displayId) ?: return null
         return factory.create(
             displayScopeRepository.scopeForDisplay(displayId),
-            statusBarConfigurationControllerStore.forDisplay(displayId),
-            contentInsetsProviderStore.forDisplay(displayId),
+            configurationController,
+            contentInsetsProvider,
         )
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/PrivacyDotWindowControllerStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/PrivacyDotWindowControllerStore.kt
index a1f5655..086cc99 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/PrivacyDotWindowControllerStore.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/PrivacyDotWindowControllerStore.kt
@@ -58,15 +58,18 @@
         StatusBarConnectedDisplays.assertInNewMode()
     }
 
-    override fun createInstanceForDisplay(displayId: Int): PrivacyDotWindowController {
+    override fun createInstanceForDisplay(displayId: Int): PrivacyDotWindowController? {
         if (displayId == Display.DEFAULT_DISPLAY) {
             throw IllegalArgumentException("This class should only be used for connected displays")
         }
         val displayWindowProperties =
             displayWindowPropertiesRepository.get(displayId, TYPE_NAVIGATION_BAR_PANEL)
+                ?: return null
+        val privacyDotViewController =
+            privacyDotViewControllerStore.forDisplay(displayId) ?: return null
         return windowControllerFactory.create(
             displayId = displayId,
-            privacyDotViewController = privacyDotViewControllerStore.forDisplay(displayId),
+            privacyDotViewController = privacyDotViewController,
             viewCaptureAwareWindowManager =
                 viewCaptureAwareWindowManagerFactory.create(displayWindowProperties.windowManager),
             inflater = displayWindowProperties.layoutInflater,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarConfigurationControllerStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarConfigurationControllerStore.kt
index 6cf2c73..38cea83 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarConfigurationControllerStore.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarConfigurationControllerStore.kt
@@ -62,9 +62,9 @@
         StatusBarConnectedDisplays.assertInNewMode()
     }
 
-    override fun createInstanceForDisplay(displayId: Int): StatusBarConfigurationController {
+    override fun createInstanceForDisplay(displayId: Int): StatusBarConfigurationController? {
         val displayWindowProperties =
-            displayWindowPropertiesRepository.get(displayId, TYPE_STATUS_BAR)
+            displayWindowPropertiesRepository.get(displayId, TYPE_STATUS_BAR) ?: return null
         return configurationControllerFactory.create(displayWindowProperties.context)
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarContentInsetsProviderStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarContentInsetsProviderStore.kt
index e471b12..5ea1211 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarContentInsetsProviderStore.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarContentInsetsProviderStore.kt
@@ -28,8 +28,8 @@
 import com.android.systemui.display.data.repository.PerDisplayStoreImpl
 import com.android.systemui.display.data.repository.SingleDisplayStore
 import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
-import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
-import com.android.systemui.statusbar.phone.StatusBarContentInsetsProviderImpl
+import com.android.systemui.statusbar.layout.StatusBarContentInsetsProvider
+import com.android.systemui.statusbar.layout.StatusBarContentInsetsProviderImpl
 import dagger.Lazy
 import dagger.Module
 import dagger.Provides
@@ -59,13 +59,17 @@
         displayRepository,
     ) {
 
-    override fun createInstanceForDisplay(displayId: Int): StatusBarContentInsetsProvider {
-        val context = displayWindowPropertiesRepository.get(displayId, TYPE_STATUS_BAR).context
+    override fun createInstanceForDisplay(displayId: Int): StatusBarContentInsetsProvider? {
+        val displayWindowProperties =
+            displayWindowPropertiesRepository.get(displayId, TYPE_STATUS_BAR) ?: return null
+        val context = displayWindowProperties.context
+        val configurationController =
+            statusBarConfigurationControllerStore.forDisplay(displayId) ?: return null
         val cameraProtectionLoader = cameraProtectionLoaderFactory.create(context)
         return factory
             .create(
                 context,
-                statusBarConfigurationControllerStore.forDisplay(displayId),
+                configurationController,
                 sysUICutoutProviderFactory.create(context, cameraProtectionLoader),
             )
             .also { it.start() }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModePerDisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModePerDisplayRepository.kt
index 22c37df..7fa9f0e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModePerDisplayRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModePerDisplayRepository.kt
@@ -33,9 +33,9 @@
 import com.android.systemui.statusbar.core.StatusBarInitializer.OnStatusBarViewInitializedListener
 import com.android.systemui.statusbar.data.model.StatusBarAppearance
 import com.android.systemui.statusbar.data.model.StatusBarMode
-import com.android.systemui.statusbar.phone.BoundsPair
-import com.android.systemui.statusbar.phone.LetterboxAppearanceCalculator
-import com.android.systemui.statusbar.phone.StatusBarBoundsProvider
+import com.android.systemui.statusbar.layout.BoundsPair
+import com.android.systemui.statusbar.layout.LetterboxAppearanceCalculator
+import com.android.systemui.statusbar.layout.StatusBarBoundsProvider
 import com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarComponent
 import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization
 import com.android.systemui.statusbar.phone.ongoingcall.data.repository.OngoingCallRepository
@@ -209,9 +209,7 @@
     override val ongoingProcessRequiresStatusBarVisible =
         _ongoingProcessRequiresStatusBarVisible.asStateFlow()
 
-    override fun setOngoingProcessRequiresStatusBarVisible(
-        requiredVisible: Boolean
-    ) {
+    override fun setOngoingProcessRequiresStatusBarVisible(requiredVisible: Boolean) {
         _ongoingProcessRequiresStatusBarVisible.value = requiredVisible
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/SystemEventChipAnimationControllerStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/SystemEventChipAnimationControllerStore.kt
index 7760f58..ffc1255 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/SystemEventChipAnimationControllerStore.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/SystemEventChipAnimationControllerStore.kt
@@ -62,11 +62,17 @@
         StatusBarConnectedDisplays.assertInNewMode()
     }
 
-    override fun createInstanceForDisplay(displayId: Int): SystemEventChipAnimationController {
+    override fun createInstanceForDisplay(displayId: Int): SystemEventChipAnimationController? {
+        val displayWindowProperties =
+            displayWindowPropertiesRepository.get(displayId, TYPE_STATUS_BAR) ?: return null
+        val statusBarWindowController =
+            statusBarWindowControllerStore.forDisplay(displayId) ?: return null
+        val contentInsetsProvider =
+            statusBarContentInsetsProviderStore.forDisplay(displayId) ?: return null
         return factory.create(
-            displayWindowPropertiesRepository.get(displayId, TYPE_STATUS_BAR).context,
-            statusBarWindowControllerStore.forDisplay(displayId),
-            statusBarContentInsetsProviderStore.forDisplay(displayId),
+            displayWindowProperties.context,
+            statusBarWindowController,
+            contentInsetsProvider,
         )
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/MultiDisplaySystemEventChipAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/MultiDisplaySystemEventChipAnimationController.kt
index f2bb7b1..4b9721e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/MultiDisplaySystemEventChipAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/MultiDisplaySystemEventChipAnimationController.kt
@@ -72,5 +72,5 @@
     }
 
     private fun controllersForAllDisplays() =
-        displayRepository.displays.value.map { controllerStore.forDisplay(it.displayId) }
+        displayRepository.displays.value.mapNotNull { controllerStore.forDisplay(it.displayId) }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
index f7bc23c..63410d746 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotViewController.kt
@@ -41,8 +41,8 @@
 import com.android.systemui.statusbar.events.PrivacyDotCorner.BottomRight
 import com.android.systemui.statusbar.events.PrivacyDotCorner.TopLeft
 import com.android.systemui.statusbar.events.PrivacyDotCorner.TopRight
-import com.android.systemui.statusbar.phone.StatusBarContentInsetsChangedListener
-import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
+import com.android.systemui.statusbar.layout.StatusBarContentInsetsChangedListener
+import com.android.systemui.statusbar.layout.StatusBarContentInsetsProvider
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.util.concurrency.DelayableExecutor
 import com.android.systemui.util.leak.RotationUtils
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotWindowController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotWindowController.kt
index 9928ac6..f7799bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotWindowController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/PrivacyDotWindowController.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.events
 
+import android.util.Log
 import android.view.Display
 import android.view.DisplayCutout.BOUNDS_POSITION_BOTTOM
 import android.view.DisplayCutout.BOUNDS_POSITION_LEFT
@@ -23,6 +24,7 @@
 import android.view.DisplayCutout.BOUNDS_POSITION_TOP
 import android.view.LayoutInflater
 import android.view.View
+import android.view.WindowManager.InvalidDisplayException
 import android.view.WindowManager.LayoutParams.WRAP_CONTENT
 import android.widget.FrameLayout
 import com.android.app.viewcapture.ViewCaptureAwareWindowManager
@@ -97,7 +99,17 @@
         // PrivacyDotViewController expects the dot view to have a FrameLayout parent.
         val rootView = FrameLayout(context)
         rootView.addView(this)
-        viewCaptureAwareWindowManager.addView(rootView, params)
+        try {
+            // Wrapping this in a try/catch to avoid crashes when a display is instantly removed
+            // after being added, and initialization hasn't finished yet.
+            viewCaptureAwareWindowManager.addView(rootView, params)
+        } catch (e: InvalidDisplayException) {
+            Log.e(
+                TAG,
+                "Unable to add view to WM. Display with id $displayId does not exist anymore",
+                e,
+            )
+        }
     }
 
     @AssistedFactory
@@ -109,4 +121,8 @@
             inflater: LayoutInflater,
         ): PrivacyDotWindowController
     }
+
+    private companion object {
+        const val TAG = "PrivacyDotWindowController"
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt
index 1038ad4..70632b3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemEventChipAnimationController.kt
@@ -36,8 +36,8 @@
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
 import com.android.systemui.statusbar.data.repository.StatusBarContentInsetsProviderStore
-import com.android.systemui.statusbar.phone.StatusBarContentInsetsChangedListener
-import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
+import com.android.systemui.statusbar.layout.StatusBarContentInsetsChangedListener
+import com.android.systemui.statusbar.layout.StatusBarContentInsetsProvider
 import com.android.systemui.statusbar.window.StatusBarWindowController
 import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
 import com.android.systemui.util.animation.AnimationUtil.Companion.frames
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/media/domain/interactor/MediaControlChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/media/domain/interactor/MediaControlChipInteractor.kt
index 4e68bee..e3e77e1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/media/domain/interactor/MediaControlChipInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/media/domain/interactor/MediaControlChipInteractor.kt
@@ -21,6 +21,7 @@
 import com.android.systemui.media.controls.data.repository.MediaFilterRepository
 import com.android.systemui.media.controls.shared.model.MediaCommonModel
 import com.android.systemui.media.controls.shared.model.MediaData
+import com.android.systemui.res.R
 import com.android.systemui.statusbar.featurepods.media.shared.model.MediaControlChipModel
 import javax.inject.Inject
 import kotlinx.coroutines.CoroutineScope
@@ -71,5 +72,10 @@
 }
 
 private fun MediaData.toMediaControlChipModel(): MediaControlChipModel {
-    return MediaControlChipModel(appIcon = this.appIcon, appName = this.app, songName = this.song)
+    return MediaControlChipModel(
+        appIcon = this.appIcon,
+        appName = this.app,
+        songName = this.song,
+        playOrPause = this.semanticActions?.getActionById(R.id.actionPlayPause),
+    )
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/media/shared/model/MediaControlChipModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/media/shared/model/MediaControlChipModel.kt
index 4035667..2e47c1e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/media/shared/model/MediaControlChipModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/media/shared/model/MediaControlChipModel.kt
@@ -17,10 +17,12 @@
 package com.android.systemui.statusbar.featurepods.media.shared.model
 
 import android.graphics.drawable.Icon
+import com.android.systemui.media.controls.shared.model.MediaAction
 
 /** Model used to display a media control chip in the status bar. */
 data class MediaControlChipModel(
     val appIcon: Icon?,
     val appName: String?,
     val songName: CharSequence?,
+    val playOrPause: MediaAction?,
 )
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/media/ui/viewmodel/MediaControlChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/media/ui/viewmodel/MediaControlChipViewModel.kt
index 3e854b4..19acb2e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/media/ui/viewmodel/MediaControlChipViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/media/ui/viewmodel/MediaControlChipViewModel.kt
@@ -24,6 +24,7 @@
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.statusbar.featurepods.media.domain.interactor.MediaControlChipInteractor
 import com.android.systemui.statusbar.featurepods.media.shared.model.MediaControlChipModel
+import com.android.systemui.statusbar.featurepods.popups.shared.model.HoverBehavior
 import com.android.systemui.statusbar.featurepods.popups.shared.model.PopupChipId
 import com.android.systemui.statusbar.featurepods.popups.shared.model.PopupChipModel
 import com.android.systemui.statusbar.featurepods.popups.ui.viewmodel.StatusBarPopupChipViewModel
@@ -33,6 +34,7 @@
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.stateIn
+import kotlinx.coroutines.launch
 
 /**
  * [StatusBarPopupChipViewModel] for a media control chip in the status bar. This view model is
@@ -54,34 +56,51 @@
      */
     override val chip: StateFlow<PopupChipModel> =
         mediaControlChipInteractor.mediaControlModel
-            .map { mediaControlModel -> toPopupChipModel(mediaControlModel, applicationContext) }
+            .map { mediaControlModel -> toPopupChipModel(mediaControlModel) }
             .stateIn(
                 backgroundScope,
                 SharingStarted.WhileSubscribed(),
                 PopupChipModel.Hidden(PopupChipId.MediaControl),
             )
-}
 
-private fun toPopupChipModel(model: MediaControlChipModel?, context: Context): PopupChipModel {
-    if (model == null || model.songName.isNullOrEmpty()) {
-        return PopupChipModel.Hidden(PopupChipId.MediaControl)
-    }
+    private fun toPopupChipModel(model: MediaControlChipModel?): PopupChipModel {
+        if (model == null || model.songName.isNullOrEmpty()) {
+            return PopupChipModel.Hidden(PopupChipId.MediaControl)
+        }
 
-    val contentDescription = model.appName?.let { ContentDescription.Loaded(description = it) }
-    return PopupChipModel.Shown(
-        chipId = PopupChipId.MediaControl,
-        icon =
-            model.appIcon?.loadDrawable(context)?.let {
+        val contentDescription = model.appName?.let { ContentDescription.Loaded(description = it) }
+
+        val defaultIcon =
+            model.appIcon?.loadDrawable(applicationContext)?.let {
                 Icon.Loaded(drawable = it, contentDescription = contentDescription)
             }
                 ?: Icon.Resource(
                     res = com.android.internal.R.drawable.ic_audio_media,
                     contentDescription = contentDescription,
-                ),
-        chipText = model.songName.toString(),
-        // TODO(b/385202114): Show a popup containing the media carousal when the chip is toggled.
-        onToggle = {},
-        // TODO(b/385202193): Add support for clicking on the icon on a media chip.
-        onIconPressed = {},
-    )
+                )
+        return PopupChipModel.Shown(
+            chipId = PopupChipId.MediaControl,
+            icon = defaultIcon,
+            chipText = model.songName.toString(),
+            isToggled = false,
+            // TODO(b/385202114): Show a popup containing the media carousal when the chip is
+            // toggled.
+            onToggle = {},
+            hoverBehavior = createHoverBehavior(model),
+        )
+    }
+
+    private fun createHoverBehavior(model: MediaControlChipModel): HoverBehavior {
+        val playOrPause = model.playOrPause ?: return HoverBehavior.None
+        val icon = playOrPause.icon ?: return HoverBehavior.None
+        val action = playOrPause.action ?: return HoverBehavior.None
+
+        val contentDescription =
+            ContentDescription.Loaded(description = playOrPause.contentDescription.toString())
+
+        return HoverBehavior.Button(
+            icon = Icon.Loaded(drawable = icon, contentDescription = contentDescription),
+            onIconPressed = { backgroundScope.launch { action.run() } },
+        )
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/shared/model/PopupChipModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/shared/model/PopupChipModel.kt
index 0a6c4d0..683b9716 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/shared/model/PopupChipModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/shared/model/PopupChipModel.kt
@@ -26,6 +26,18 @@
     data object MediaControl : PopupChipId("MediaControl")
 }
 
+/** Defines the behavior of the chip when hovered over. */
+sealed interface HoverBehavior {
+    /** No specific hover behavior. The default icon will be shown. */
+    data object None : HoverBehavior
+
+    /**
+     * Shows a button on hover with the given [icon] and executes [onIconPressed] when the icon is
+     * pressed.
+     */
+    data class Button(val icon: Icon, val onIconPressed: () -> Unit) : HoverBehavior
+}
+
 /** Model for individual status bar popup chips. */
 sealed class PopupChipModel {
     abstract val logName: String
@@ -38,11 +50,12 @@
 
     data class Shown(
         override val chipId: PopupChipId,
+        /** Default icon displayed on the chip */
         val icon: Icon,
         val chipText: String,
         val isToggled: Boolean = false,
         val onToggle: () -> Unit,
-        val onIconPressed: () -> Unit,
+        val hoverBehavior: HoverBehavior = HoverBehavior.None,
     ) : PopupChipModel() {
         override val logName = "Shown(id=$chipId, toggled=$isToggled)"
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/compose/StatusBarPopupChip.kt b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/compose/StatusBarPopupChip.kt
new file mode 100644
index 0000000..34bef9d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/compose/StatusBarPopupChip.kt
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.featurepods.popups.ui.compose
+
+import androidx.compose.animation.animateContentSize
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.hoverable
+import androidx.compose.foundation.interaction.MutableInteractionSource
+import androidx.compose.foundation.interaction.collectIsHoveredAsState
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.widthIn
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
+import androidx.compose.material3.contentColorFor
+import androidx.compose.material3.ripple
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.semantics.Role
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.unit.dp
+import com.android.compose.modifiers.thenIf
+import com.android.systemui.common.ui.compose.Icon
+import com.android.systemui.statusbar.featurepods.popups.shared.model.HoverBehavior
+import com.android.systemui.statusbar.featurepods.popups.shared.model.PopupChipModel
+
+/**
+ * A clickable chip that can show an anchored popup containing relevant system controls. The chip
+ * can show an icon that can have its own separate action distinct from its parent chip. Moreover,
+ * the chip can show text containing contextual information.
+ */
+@Composable
+fun StatusBarPopupChip(model: PopupChipModel.Shown, modifier: Modifier = Modifier) {
+    val hasHoverBehavior = model.hoverBehavior !is HoverBehavior.None
+    val hoverInteractionSource = remember { MutableInteractionSource() }
+    val isHovered by hoverInteractionSource.collectIsHoveredAsState()
+    val isToggled = model.isToggled
+
+    val chipBackgroundColor =
+        if (isToggled) {
+            MaterialTheme.colorScheme.primaryContainer
+        } else {
+            MaterialTheme.colorScheme.surfaceContainerHighest
+        }
+    Surface(
+        shape = RoundedCornerShape(16.dp),
+        modifier =
+            modifier
+                .widthIn(max = 120.dp)
+                .padding(vertical = 4.dp)
+                .animateContentSize()
+                .thenIf(hasHoverBehavior) { Modifier.hoverable(hoverInteractionSource) }
+                .clickable { model.onToggle() },
+        color = chipBackgroundColor,
+    ) {
+        Row(
+            modifier = Modifier.padding(start = 4.dp, end = 8.dp),
+            verticalAlignment = Alignment.CenterVertically,
+            horizontalArrangement = Arrangement.spacedBy(4.dp),
+        ) {
+            val iconColor =
+                if (isHovered) chipBackgroundColor else contentColorFor(chipBackgroundColor)
+            val hoverBehavior = model.hoverBehavior
+            val iconBackgroundColor = contentColorFor(chipBackgroundColor)
+            val iconInteractionSource = remember { MutableInteractionSource() }
+            Icon(
+                icon =
+                    when {
+                        isHovered && hoverBehavior is HoverBehavior.Button -> hoverBehavior.icon
+                        else -> model.icon
+                    },
+                modifier =
+                    Modifier.thenIf(isHovered) {
+                            Modifier.padding(3.dp)
+                                .background(color = iconBackgroundColor, shape = CircleShape)
+                        }
+                        .thenIf(hoverBehavior is HoverBehavior.Button) {
+                            Modifier.clickable(
+                                role = Role.Button,
+                                onClick = (hoverBehavior as HoverBehavior.Button).onIconPressed,
+                                indication = ripple(),
+                                interactionSource = iconInteractionSource,
+                            )
+                        }
+                        .padding(3.dp),
+                tint = iconColor,
+            )
+
+            Text(
+                text = model.chipText,
+                style = MaterialTheme.typography.labelLarge,
+                softWrap = false,
+                overflow = TextOverflow.Ellipsis,
+            )
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/compose/StatusBarPopupChipsContainer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/compose/StatusBarPopupChipsContainer.kt
index 56bbd74..d35674d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/compose/StatusBarPopupChipsContainer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/featurepods/popups/ui/compose/StatusBarPopupChipsContainer.kt
@@ -18,10 +18,11 @@
 
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Row
-import androidx.compose.material3.Text
+import androidx.compose.foundation.layout.padding
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
 import com.android.systemui.statusbar.featurepods.popups.shared.model.PopupChipModel
 
 /** Container view that holds all right hand side chips in the status bar. */
@@ -29,9 +30,11 @@
 fun StatusBarPopupChipsContainer(chips: List<PopupChipModel.Shown>, modifier: Modifier = Modifier) {
     //    TODO(b/385353140): Add padding and spacing for this container according to UX specs.
     Box {
-        Row(verticalAlignment = Alignment.CenterVertically) {
-            // TODO(b/385352859): Show `StatusBarPopupChip` here instead of `Text` once it is ready.
-            chips.forEach { chip -> Text(text = chip.chipText) }
+        Row(
+            modifier = Modifier.padding(horizontal = 8.dp),
+            verticalAlignment = Alignment.CenterVertically,
+        ) {
+            chips.forEach { chip -> StatusBarPopupChip(chip) }
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LetterboxAppearanceCalculator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/layout/LetterboxAppearanceCalculator.kt
similarity index 87%
rename from packages/SystemUI/src/com/android/systemui/statusbar/phone/LetterboxAppearanceCalculator.kt
rename to packages/SystemUI/src/com/android/systemui/statusbar/layout/LetterboxAppearanceCalculator.kt
index 231a8c6..1469fe7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LetterboxAppearanceCalculator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/layout/LetterboxAppearanceCalculator.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.statusbar.phone
+package com.android.systemui.statusbar.layout
 
 import android.annotation.ColorInt
 import android.content.Context
@@ -39,7 +39,7 @@
 ) {
     override fun toString(): String {
         val appearanceString =
-                ViewDebug.flagsToString(InsetsFlags::class.java, "appearance", appearance)
+            ViewDebug.flagsToString(InsetsFlags::class.java, "appearance", appearance)
         return "LetterboxAppearance{$appearanceString, $appearanceRegions}"
     }
 }
@@ -57,14 +57,16 @@
     private val letterboxBackgroundProvider: LetterboxBackgroundProvider,
 ) : Dumpable {
 
-    private val darkAppearanceIconColor = context.getColor(
-        // For a dark background status bar, use a *light* icon color.
-        com.android.settingslib.R.color.light_mode_icon_color_single_tone
-    )
-    private val lightAppearanceIconColor = context.getColor(
-        // For a light background status bar, use a *dark* icon color.
-        com.android.settingslib.R.color.dark_mode_icon_color_single_tone
-    )
+    private val darkAppearanceIconColor =
+        context.getColor(
+            // For a dark background status bar, use a *light* icon color.
+            com.android.settingslib.R.color.light_mode_icon_color_single_tone
+        )
+    private val lightAppearanceIconColor =
+        context.getColor(
+            // For a light background status bar, use a *dark* icon color.
+            com.android.settingslib.R.color.dark_mode_icon_color_single_tone
+        )
 
     init {
         dumpManager.registerCriticalDumpable(this)
@@ -85,7 +87,11 @@
         lastAppearanceRegions = originalAppearanceRegions
         lastLetterboxes = letterboxes
         return getLetterboxAppearanceInternal(
-                letterboxes, originalAppearance, originalAppearanceRegions, statusBarBounds)
+                letterboxes,
+                originalAppearance,
+                originalAppearanceRegions,
+                statusBarBounds,
+            )
             .also { lastLetterboxAppearance = it }
     }
 
@@ -118,7 +124,7 @@
 
     private fun getAppearanceRegions(
         originalAppearanceRegions: List<AppearanceRegion>,
-        letterboxes: List<LetterboxDetails>
+        letterboxes: List<LetterboxDetails>,
     ): List<AppearanceRegion> {
         return sanitizeAppearanceRegions(originalAppearanceRegions, letterboxes) +
             getAllOuterAppearanceRegions(letterboxes)
@@ -126,7 +132,7 @@
 
     private fun sanitizeAppearanceRegions(
         originalAppearanceRegions: List<AppearanceRegion>,
-        letterboxes: List<LetterboxDetails>
+        letterboxes: List<LetterboxDetails>,
     ): List<AppearanceRegion> =
         originalAppearanceRegions.map { appearanceRegion ->
             val matchingLetterbox =
@@ -138,17 +144,20 @@
                 // full bounds of its window.
                 // Here we want the bounds to be only for the inner bounds of the letterboxed app.
                 AppearanceRegion(
-                    appearanceRegion.appearance, matchingLetterbox.letterboxInnerBounds)
+                    appearanceRegion.appearance,
+                    matchingLetterbox.letterboxInnerBounds,
+                )
             }
         }
 
     private fun originalAppearanceWithScrim(
         @Appearance originalAppearance: Int,
-        originalAppearanceRegions: List<AppearanceRegion>
+        originalAppearanceRegions: List<AppearanceRegion>,
     ): LetterboxAppearance {
         return LetterboxAppearance(
             originalAppearance or APPEARANCE_SEMI_TRANSPARENT_STATUS_BARS,
-            originalAppearanceRegions)
+            originalAppearanceRegions,
+        )
     }
 
     @Appearance
@@ -215,7 +224,9 @@
            lastAppearanceRegion: $lastAppearanceRegions,
            lastLetterboxes: $lastLetterboxes,
            lastLetterboxAppearance: $lastLetterboxAppearance
-       """.trimIndent())
+       """
+                .trimIndent()
+        )
     }
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LetterboxBackgroundProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/layout/LetterboxBackgroundProvider.kt
similarity index 93%
rename from packages/SystemUI/src/com/android/systemui/statusbar/phone/LetterboxBackgroundProvider.kt
rename to packages/SystemUI/src/com/android/systemui/statusbar/layout/LetterboxBackgroundProvider.kt
index 34c7059e..3d8ced1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LetterboxBackgroundProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/layout/LetterboxBackgroundProvider.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.statusbar.phone
+package com.android.systemui.statusbar.layout
 
 import android.annotation.ColorInt
 import android.app.WallpaperManager
@@ -49,9 +49,7 @@
         private set
 
     private val wallpaperColorsListener =
-        WallpaperManager.OnColorsChangedListener { _, _ ->
-            fetchBackgroundColorInfo()
-        }
+        WallpaperManager.OnColorsChangedListener { _, _ -> fetchBackgroundColorInfo() }
 
     override fun start() {
         fetchBackgroundColorInfo()
@@ -75,6 +73,8 @@
             """
            letterboxBackgroundColor: ${Color.valueOf(letterboxBackgroundColor)}
            isLetterboxBackgroundMultiColored: $isLetterboxBackgroundMultiColored
-       """.trimIndent())
+       """
+                .trimIndent()
+        )
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarBoundsProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/layout/StatusBarBoundsProvider.kt
similarity index 98%
rename from packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarBoundsProvider.kt
rename to packages/SystemUI/src/com/android/systemui/statusbar/layout/StatusBarBoundsProvider.kt
index 3ac0bac..ac5b037 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarBoundsProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/layout/StatusBarBoundsProvider.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.statusbar.phone
+package com.android.systemui.statusbar.layout
 
 import android.graphics.Rect
 import android.view.View
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/layout/StatusBarContentInsetsProvider.kt
similarity index 99%
rename from packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
rename to packages/SystemUI/src/com/android/systemui/statusbar/layout/StatusBarContentInsetsProvider.kt
index 41db5f4..f7a9094 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/layout/StatusBarContentInsetsProvider.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.statusbar.phone
+package com.android.systemui.statusbar.layout
 
 import android.annotation.Px
 import android.content.Context
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DataStoreCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DataStoreCoordinator.kt
index 90212ed..034a4fd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DataStoreCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DataStoreCoordinator.kt
@@ -36,7 +36,7 @@
 internal constructor(private val notifLiveDataStoreImpl: NotifLiveDataStoreImpl) : CoreCoordinator {
 
     override fun attach(pipeline: NotifPipeline) {
-        pipeline.addOnAfterRenderListListener { entries, _ -> onAfterRenderList(entries) }
+        pipeline.addOnAfterRenderListListener { entries -> onAfterRenderList(entries) }
     }
 
     override fun dumpPipeline(d: PipelineDumper) {
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 eb6ec9f..c7535ec 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
@@ -141,9 +141,6 @@
                 wasAdded = false,
                 wasUpdated = false,
                 // Force-set this notification to show heads-up.
-                // TODO(b/364653005): This means that if you tap on the second notification chip,
-                // then it moves to become the first chip because whatever notification is showing
-                // heads-up is considered to be the top notification.
                 shouldHeadsUpEver = true,
                 shouldHeadsUpAgain = true,
                 isPinnedByUser = true,
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 32de65b..1cb2366 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
@@ -23,11 +23,9 @@
 import com.android.systemui.statusbar.notification.collection.NotifPipeline
 import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
 import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManagerImpl
-import com.android.systemui.statusbar.notification.collection.render.NotifStackController
-import com.android.systemui.statusbar.notification.collection.render.NotifStats
+import com.android.systemui.statusbar.notification.data.model.NotifStats
 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.stack.BUCKET_SILENT
 import com.android.systemui.statusbar.policy.SensitiveNotificationProtectionController
 import javax.inject.Inject
@@ -43,7 +41,8 @@
     private val groupExpansionManagerImpl: GroupExpansionManagerImpl,
     private val renderListInteractor: RenderNotificationListInteractor,
     private val activeNotificationsInteractor: ActiveNotificationsInteractor,
-    private val sensitiveNotificationProtectionController: SensitiveNotificationProtectionController,
+    private val sensitiveNotificationProtectionController:
+        SensitiveNotificationProtectionController,
 ) : Coordinator {
 
     override fun attach(pipeline: NotifPipeline) {
@@ -51,14 +50,10 @@
         groupExpansionManagerImpl.attach(pipeline)
     }
 
-    private fun onAfterRenderList(entries: List<ListEntry>, controller: NotifStackController) =
+    private fun onAfterRenderList(entries: List<ListEntry>) =
         traceSection("StackCoordinator.onAfterRenderList") {
             val notifStats = calculateNotifStats(entries)
-            if (FooterViewRefactor.isEnabled) {
-                activeNotificationsInteractor.setNotifStats(notifStats)
-            } else {
-                controller.setNotifStats(notifStats)
-            }
+            activeNotificationsInteractor.setNotifStats(notifStats)
             renderListInteractor.setRenderedList(entries)
         }
 
@@ -87,7 +82,6 @@
             }
         }
         return NotifStats(
-            numActiveNotifs = entries.size,
             hasNonClearableAlertingNotifs = hasNonClearableAlertingNotifs,
             hasClearableAlertingNotifs = hasClearableAlertingNotifs,
             hasNonClearableSilentNotifs = hasNonClearableSilentNotifs,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java
index a34d033..c58b3fe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NotifPipelineInitializer.java
@@ -33,7 +33,6 @@
 import com.android.systemui.statusbar.notification.collection.coalescer.GroupCoalescer;
 import com.android.systemui.statusbar.notification.collection.coordinator.NotifCoordinators;
 import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
-import com.android.systemui.statusbar.notification.collection.render.NotifStackController;
 import com.android.systemui.statusbar.notification.collection.render.RenderStageManager;
 import com.android.systemui.statusbar.notification.collection.render.ShadeViewManager;
 import com.android.systemui.statusbar.notification.collection.render.ShadeViewManagerFactory;
@@ -89,8 +88,7 @@
     public void initialize(
             NotificationListener notificationService,
             NotificationRowBinderImpl rowBinder,
-            NotificationListContainer listContainer,
-            NotifStackController stackController) {
+            NotificationListContainer listContainer) {
         mDumpManager.registerDumpable("NotifPipeline", this);
 
         mNotificationService = notificationService;
@@ -102,7 +100,7 @@
         mNotifPluggableCoordinators.attach(mPipelineWrapper);
 
         // Wire up pipeline
-        mShadeViewManager = mShadeViewManagerFactory.create(listContainer, stackController);
+        mShadeViewManager = mShadeViewManagerFactory.create(listContainer);
         mShadeViewManager.attach(mRenderStageManager);
         mRenderStageManager.attach(mListBuilder);
         mListBuilder.attach(mNotifCollection);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnAfterRenderListListener.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnAfterRenderListListener.java
index b5a0f7a..ac450c0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnAfterRenderListListener.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/OnAfterRenderListListener.java
@@ -20,7 +20,6 @@
 
 import com.android.systemui.statusbar.notification.collection.ListEntry;
 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
-import com.android.systemui.statusbar.notification.collection.render.NotifStackController;
 
 import java.util.List;
 
@@ -31,9 +30,6 @@
      *
      * @param entries The current list of top-level entries. Note that this is a live view into the
      * current list and will change whenever the pipeline is rerun.
-     * @param controller An object for setting state on the shade.
      */
-    void onAfterRenderList(
-            @NonNull List<ListEntry> entries,
-            @NonNull NotifStackController controller);
+    void onAfterRenderList(@NonNull List<ListEntry> entries);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifStackController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifStackController.kt
deleted file mode 100644
index a37937a..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifStackController.kt
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.notification.collection.render
-
-import javax.inject.Inject
-
-/** An interface by which the pipeline can make updates to the notification root view. */
-interface NotifStackController {
-    /** Provides stats about the list of notifications attached to the shade */
-    fun setNotifStats(stats: NotifStats)
-}
-
-/** Data provided to the NotificationRootController whenever the pipeline runs */
-data class NotifStats(
-    // TODO(b/293167744): The count can be removed from here when we remove the FooterView flag.
-    val numActiveNotifs: Int,
-    val hasNonClearableAlertingNotifs: Boolean,
-    val hasClearableAlertingNotifs: Boolean,
-    val hasNonClearableSilentNotifs: Boolean,
-    val hasClearableSilentNotifs: Boolean
-) {
-    companion object {
-        @JvmStatic val empty = NotifStats(0, false, false, false, false)
-    }
-}
-
-/**
- * An implementation of NotifStackController which provides default, no-op implementations of each
- * method. This is used by ArcSystemUI so that that implementation can opt-in to overriding methods,
- * rather than forcing us to add no-op implementations in their implementation every time a method
- * is added.
- */
-open class DefaultNotifStackController @Inject constructor() : NotifStackController {
-    override fun setNotifStats(stats: NotifStats) {}
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifViewRenderer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifViewRenderer.kt
index 410b78b..8284022 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifViewRenderer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NotifViewRenderer.kt
@@ -37,12 +37,6 @@
     fun onRenderList(notifList: List<ListEntry>)
 
     /**
-     * Provides an interface for the pipeline to update the overall shade. This will be called at
-     * most once for each time [onRenderList] is called.
-     */
-    fun getStackController(): NotifStackController
-
-    /**
      * Provides an interface for the pipeline to update individual groups. This will be called at
      * most once for each group in the most recent call to [onRenderList].
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManager.kt
index 9d3b098..21e6837 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RenderStageManager.kt
@@ -50,7 +50,7 @@
         traceSection("RenderStageManager.onRenderList") {
             val viewRenderer = viewRenderer ?: return
             viewRenderer.onRenderList(notifList)
-            dispatchOnAfterRenderList(viewRenderer, notifList)
+            dispatchOnAfterRenderList(notifList)
             dispatchOnAfterRenderGroups(viewRenderer, notifList)
             dispatchOnAfterRenderEntries(viewRenderer, notifList)
             viewRenderer.onDispatchComplete()
@@ -85,15 +85,9 @@
             dump("onAfterRenderEntryListeners", onAfterRenderEntryListeners)
         }
 
-    private fun dispatchOnAfterRenderList(
-        viewRenderer: NotifViewRenderer,
-        entries: List<ListEntry>,
-    ) {
+    private fun dispatchOnAfterRenderList(entries: List<ListEntry>) {
         traceSection("RenderStageManager.dispatchOnAfterRenderList") {
-            val stackController = viewRenderer.getStackController()
-            onAfterRenderListListeners.forEach { listener ->
-                listener.onAfterRenderList(entries, stackController)
-            }
+            onAfterRenderListListeners.forEach { listener -> listener.onAfterRenderList(entries) }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
index 3c838e5..72316bf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewManager.kt
@@ -41,7 +41,6 @@
 constructor(
     @ShadeDisplayAware context: Context,
     @Assisted listContainer: NotificationListContainer,
-    @Assisted private val stackController: NotifStackController,
     mediaContainerController: MediaContainerController,
     featureManager: NotificationSectionsFeatureManager,
     sectionHeaderVisibilityProvider: SectionHeaderVisibilityProvider,
@@ -83,8 +82,6 @@
                 }
             }
 
-            override fun getStackController(): NotifStackController = stackController
-
             override fun getGroupController(group: GroupEntry): NotifGroupController =
                 viewBarn.requireGroupController(group.requireSummary)
 
@@ -95,8 +92,5 @@
 
 @AssistedFactory
 interface ShadeViewManagerFactory {
-    fun create(
-        listContainer: NotificationListContainer,
-        stackController: NotifStackController,
-    ): ShadeViewManager
+    fun create(listContainer: NotificationListContainer): ShadeViewManager
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/model/NotifStats.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/model/NotifStats.kt
new file mode 100644
index 0000000..d7fd702
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/model/NotifStats.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.statusbar.notification.data.model
+
+/** Information about the current list of notifications. */
+data class NotifStats(
+    val hasNonClearableAlertingNotifs: Boolean,
+    val hasClearableAlertingNotifs: Boolean,
+    val hasNonClearableSilentNotifs: Boolean,
+    val hasClearableSilentNotifs: Boolean,
+) {
+    companion object {
+        @JvmStatic
+        val empty =
+            NotifStats(
+                hasNonClearableAlertingNotifs = false,
+                hasClearableAlertingNotifs = false,
+                hasNonClearableSilentNotifs = false,
+                hasClearableSilentNotifs = false,
+            )
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepository.kt
index 2b9e493..70f06eb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/data/repository/ActiveNotificationListRepository.kt
@@ -16,7 +16,7 @@
 package com.android.systemui.statusbar.notification.data.repository
 
 import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.statusbar.notification.collection.render.NotifStats
+import com.android.systemui.statusbar.notification.data.model.NotifStats
 import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore.Key
 import com.android.systemui.statusbar.notification.shared.ActiveNotificationEntryModel
 import com.android.systemui.statusbar.notification.shared.ActiveNotificationGroupModel
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 6b93ee1..0c040c8 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
@@ -18,7 +18,7 @@
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips
-import com.android.systemui.statusbar.notification.collection.render.NotifStats
+import com.android.systemui.statusbar.notification.data.model.NotifStats
 import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository
 import com.android.systemui.statusbar.notification.shared.ActiveNotificationGroupModel
 import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModel.kt
index fbec640..7e2361f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/emptyshade/ui/viewmodel/EmptyShadeViewModel.kt
@@ -26,7 +26,6 @@
 import com.android.systemui.statusbar.notification.NotificationActivityStarter.SettingsIntent
 import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor
 import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyShadeFix
-import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
 import com.android.systemui.statusbar.notification.footer.ui.viewmodel.FooterMessageViewModel
 import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor
 import com.android.systemui.util.kotlin.FlowDumperImpl
@@ -35,7 +34,6 @@
 import java.util.Locale
 import kotlinx.coroutines.CoroutineDispatcher
 import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.flowOf
@@ -57,9 +55,7 @@
     dumpManager: DumpManager,
 ) : FlowDumperImpl(dumpManager) {
     val areNotificationsHiddenInShade: Flow<Boolean> by lazy {
-        if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) {
-            flowOf(false)
-        } else if (ModesEmptyShadeFix.isEnabled) {
+        if (ModesEmptyShadeFix.isEnabled) {
             zenModeInteractor.areNotificationsHiddenInShade
                 .dumpWhileCollecting("areNotificationsHiddenInShade")
                 .flowOn(bgDispatcher)
@@ -70,15 +66,10 @@
         }
     }
 
-    val hasFilteredOutSeenNotifications: StateFlow<Boolean> by lazy {
-        if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) {
-            MutableStateFlow(false)
-        } else {
-            seenNotificationsInteractor.hasFilteredOutSeenNotifications.dumpValue(
-                "hasFilteredOutSeenNotifications"
-            )
-        }
-    }
+    val hasFilteredOutSeenNotifications: StateFlow<Boolean> =
+        seenNotificationsInteractor.hasFilteredOutSeenNotifications.dumpValue(
+            "hasFilteredOutSeenNotifications"
+        )
 
     val text: Flow<String> by lazy {
         if (ModesEmptyShadeFix.isUnexpectedlyInLegacyMode()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/shared/FooterViewRefactor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/shared/FooterViewRefactor.kt
deleted file mode 100644
index 7e6044e..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/shared/FooterViewRefactor.kt
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.notification.footer.shared
-
-import com.android.systemui.Flags
-import com.android.systemui.flags.FlagToken
-import com.android.systemui.flags.RefactorFlagUtils
-
-/** Helper for reading or using the FooterView refactor flag state. */
-@Suppress("NOTHING_TO_INLINE")
-object FooterViewRefactor {
-    /** The aconfig flag name */
-    const val FLAG_NAME = Flags.FLAG_NOTIFICATIONS_FOOTER_VIEW_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.notificationsFooterViewRefactor()
-
-    /**
-     * 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/footer/ui/view/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
index d258898..a670f69 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
@@ -41,7 +41,6 @@
 
 import com.android.systemui.res.R;
 import com.android.systemui.statusbar.notification.ColorUpdateLogger;
-import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor;
 import com.android.systemui.statusbar.notification.footer.shared.NotifRedesignFooter;
 import com.android.systemui.statusbar.notification.row.FooterViewButton;
 import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
@@ -63,16 +62,9 @@
     private FooterViewButton mSettingsButton;
     private FooterViewButton mHistoryButton;
     private boolean mShouldBeHidden;
-    private boolean mShowHistory;
-    // String cache, for performance reasons.
-    // Reading them from a Resources object can be quite slow sometimes.
-    private String mManageNotificationText;
-    private String mManageNotificationHistoryText;
 
     // Footer label
     private TextView mSeenNotifsFooterTextView;
-    private String mSeenNotifsFilteredText;
-    private Drawable mSeenNotifsFilteredIcon;
 
     private @StringRes int mClearAllButtonTextId;
     private @StringRes int mClearAllButtonDescriptionId;
@@ -159,8 +151,8 @@
         IndentingPrintWriter pw = DumpUtilsKt.asIndenting(pwOriginal);
         super.dump(pw, args);
         DumpUtilsKt.withIncreasedIndent(pw, () -> {
+            // TODO: b/375010573 - update dumps for redesign
             pw.println("visibility: " + DumpUtilsKt.visibilityString(getVisibility()));
-            pw.println("manageButton showHistory: " + mShowHistory);
             pw.println("manageButton visibility: "
                     + DumpUtilsKt.visibilityString(mClearAllButton.getVisibility()));
             pw.println("dismissButton visibility: "
@@ -170,7 +162,6 @@
 
     /** Set the text label for the "Clear all" button. */
     public void setClearAllButtonText(@StringRes int textId) {
-        if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) return;
         if (mClearAllButtonTextId == textId) {
             return; // nothing changed
         }
@@ -187,9 +178,6 @@
 
     /** Set the accessibility content description for the "Clear all" button. */
     public void setClearAllButtonDescription(@StringRes int contentDescriptionId) {
-        if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) {
-            return;
-        }
         if (mClearAllButtonDescriptionId == contentDescriptionId) {
             return; // nothing changed
         }
@@ -207,7 +195,6 @@
     /** Set the text label for the "Manage"/"History" button. */
     public void setManageOrHistoryButtonText(@StringRes int textId) {
         NotifRedesignFooter.assertInLegacyMode();
-        if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) return;
         if (mManageOrHistoryButtonTextId == textId) {
             return; // nothing changed
         }
@@ -226,9 +213,6 @@
     /** Set the accessibility content description for the "Clear all" button. */
     public void setManageOrHistoryButtonDescription(@StringRes int contentDescriptionId) {
         NotifRedesignFooter.assertInLegacyMode();
-        if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) {
-            return;
-        }
         if (mManageOrHistoryButtonDescriptionId == contentDescriptionId) {
             return; // nothing changed
         }
@@ -247,7 +231,6 @@
 
     /** Set the string for a message to be shown instead of the buttons. */
     public void setMessageString(@StringRes int messageId) {
-        if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) return;
         if (mMessageStringId == messageId) {
             return; // nothing changed
         }
@@ -265,7 +248,6 @@
 
     /** Set the icon to be shown before the message (see {@link #setMessageString(int)}). */
     public void setMessageIcon(@DrawableRes int iconId) {
-        if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) return;
         if (mMessageIconId == iconId) {
             return; // nothing changed
         }
@@ -303,32 +285,17 @@
             mManageOrHistoryButton = findViewById(R.id.manage_text);
         }
         mSeenNotifsFooterTextView = findViewById(R.id.unlock_prompt_footer);
-        if (!FooterViewRefactor.isEnabled()) {
-            updateResources();
-        }
         updateContent();
         updateColors();
     }
 
     /** Show a message instead of the footer buttons. */
     public void setFooterLabelVisible(boolean isVisible) {
-        // In the refactored code, hiding the buttons is handled in the FooterViewModel
-        if (FooterViewRefactor.isEnabled()) {
-            if (isVisible) {
-                mSeenNotifsFooterTextView.setVisibility(View.VISIBLE);
-            } else {
-                mSeenNotifsFooterTextView.setVisibility(View.GONE);
-            }
+        // Note: hiding the buttons is handled in the FooterViewModel
+        if (isVisible) {
+            mSeenNotifsFooterTextView.setVisibility(View.VISIBLE);
         } else {
-            if (isVisible) {
-                mManageOrHistoryButton.setVisibility(View.GONE);
-                mClearAllButton.setVisibility(View.GONE);
-                mSeenNotifsFooterTextView.setVisibility(View.VISIBLE);
-            } else {
-                mManageOrHistoryButton.setVisibility(View.VISIBLE);
-                mClearAllButton.setVisibility(View.VISIBLE);
-                mSeenNotifsFooterTextView.setVisibility(View.GONE);
-            }
+            mSeenNotifsFooterTextView.setVisibility(View.GONE);
         }
     }
 
@@ -359,10 +326,8 @@
 
     /** Set onClickListener for the clear all (end) button. */
     public void setClearAllButtonClickListener(OnClickListener listener) {
-        if (FooterViewRefactor.isEnabled()) {
-            if (mClearAllButtonClickListener == listener) return;
-            mClearAllButtonClickListener = listener;
-        }
+        if (mClearAllButtonClickListener == listener) return;
+        mClearAllButtonClickListener = listener;
         mClearAllButton.setOnClickListener(listener);
     }
 
@@ -379,62 +344,17 @@
                 || touchY > mContent.getY() + mContent.getHeight();
     }
 
-    /** Show "History" instead of "Manage" on the start button. */
-    public void showHistory(boolean showHistory) {
-        FooterViewRefactor.assertInLegacyMode();
-        if (mShowHistory == showHistory) {
-            return;
-        }
-        mShowHistory = showHistory;
-        updateContent();
-    }
-
     private void updateContent() {
-        if (FooterViewRefactor.isEnabled()) {
-            updateClearAllButtonText();
-            updateClearAllButtonDescription();
+        updateClearAllButtonText();
+        updateClearAllButtonDescription();
 
-            if (!NotifRedesignFooter.isEnabled()) {
-                updateManageOrHistoryButtonText();
-                updateManageOrHistoryButtonDescription();
-            }
-
-            updateMessageString();
-            updateMessageIcon();
-        } else {
-            // NOTE: Prior to the refactor, `updateResources` set the class properties to the right
-            // string values. It was always being called together with `updateContent`, which
-            // deals with actually associating those string values with the correct views
-            // (buttons or text).
-            // In the new code, the resource IDs are being set in the view binder (through
-            // setMessageString and similar setters). The setters themselves now deal with
-            // updating both the resource IDs and the views where appropriate (as in, calling
-            // `updateMessageString` when the resource ID changes). This eliminates the need for
-            // `updateResources`, which will eventually be removed. There are, however, still
-            // situations in which we want to update the views even if the resource IDs didn't
-            // change, such as configuration changes.
-            if (mShowHistory) {
-                mManageOrHistoryButton.setText(mManageNotificationHistoryText);
-                mManageOrHistoryButton.setContentDescription(mManageNotificationHistoryText);
-            } else {
-                mManageOrHistoryButton.setText(mManageNotificationText);
-                mManageOrHistoryButton.setContentDescription(mManageNotificationText);
-            }
-
-            mClearAllButton.setText(R.string.clear_all_notifications_text);
-            mClearAllButton.setContentDescription(
-                    mContext.getString(R.string.accessibility_clear_all));
-
-            mSeenNotifsFooterTextView.setText(mSeenNotifsFilteredText);
-            mSeenNotifsFooterTextView
-                    .setCompoundDrawablesRelative(mSeenNotifsFilteredIcon, null, null, null);
+        if (!NotifRedesignFooter.isEnabled()) {
+            updateManageOrHistoryButtonText();
+            updateManageOrHistoryButtonDescription();
         }
-    }
 
-    /** Whether the start button shows "History" (true) or "Manage" (false). */
-    public boolean isHistoryShown() {
-        FooterViewRefactor.assertInLegacyMode();
-        return mShowHistory;
+        updateMessageString();
+        updateMessageIcon();
     }
 
     @Override
@@ -445,9 +365,6 @@
         }
         super.onConfigurationChanged(newConfig);
         updateColors();
-        if (!FooterViewRefactor.isEnabled()) {
-            updateResources();
-        }
         updateContent();
     }
 
@@ -502,18 +419,6 @@
         }
     }
 
-    private void updateResources() {
-        FooterViewRefactor.assertInLegacyMode();
-        mManageNotificationText = getContext().getString(R.string.manage_notifications_text);
-        mManageNotificationHistoryText = getContext()
-                .getString(R.string.manage_notifications_history_text);
-        int unlockIconSize = getResources()
-                .getDimensionPixelSize(R.dimen.notifications_unseen_footer_icon_size);
-        mSeenNotifsFilteredText = getContext().getString(R.string.unlock_to_see_notif_text);
-        mSeenNotifsFilteredIcon = getContext().getDrawable(R.drawable.ic_friction_lock_closed);
-        mSeenNotifsFilteredIcon.setBounds(0, 0, unlockIconSize, unlockIconSize);
-    }
-
     @Override
     @NonNull
     public ExpandableViewState createExpandableViewState() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt
index e724935..5696e9f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt
@@ -27,7 +27,6 @@
 import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
 import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor
 import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyShadeFix
-import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
 import com.android.systemui.statusbar.notification.footer.ui.view.FooterView
 import com.android.systemui.util.kotlin.sample
 import com.android.systemui.util.ui.AnimatableEvent
@@ -144,6 +143,7 @@
         )
 }
 
+// TODO: b/293167744 - remove this, use new viewmodel style
 @Module
 object FooterViewModelModule {
     @Provides
@@ -153,18 +153,13 @@
         notificationSettingsInteractor: Provider<NotificationSettingsInteractor>,
         seenNotificationsInteractor: Provider<SeenNotificationsInteractor>,
         shadeInteractor: Provider<ShadeInteractor>,
-    ): Optional<FooterViewModel> {
-        return if (FooterViewRefactor.isEnabled) {
-            Optional.of(
-                FooterViewModel(
-                    activeNotificationsInteractor.get(),
-                    notificationSettingsInteractor.get(),
-                    seenNotificationsInteractor.get(),
-                    shadeInteractor.get(),
-                )
+    ): Optional<FooterViewModel> =
+        Optional.of(
+            FooterViewModel(
+                activeNotificationsInteractor.get(),
+                notificationSettingsInteractor.get(),
+                seenNotificationsInteractor.get(),
+                shadeInteractor.get(),
             )
-        } else {
-            Optional.empty()
-        }
-    }
+        )
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/ConnectedDisplaysStatusBarNotificationIconViewStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/ConnectedDisplaysStatusBarNotificationIconViewStore.kt
index 227a1fe..eb55856 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/ConnectedDisplaysStatusBarNotificationIconViewStore.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/ConnectedDisplaysStatusBarNotificationIconViewStore.kt
@@ -62,8 +62,10 @@
 
     override fun iconView(key: String): StatusBarIconView? {
         val entry = notifCollection.getEntry(key) ?: return null
+        val displayWindowProperties =
+            displayWindowPropertiesInteractor.getForStatusBar(displayId) ?: return null
         return cachedIcons.computeIfAbsent(key) {
-            val context = displayWindowPropertiesInteractor.getForStatusBar(displayId).context
+            val context = displayWindowProperties.context
             iconManager.createSbIconView(context, entry)
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt
index 2ba28a6..e103282 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewmodel/NotificationIconContainerStatusBarViewModel.kt
@@ -68,13 +68,14 @@
             .distinctUntilChanged()
 
     /** The colors with which to display the notification icons. */
-    fun iconColors(displayId: Int): Flow<NotificationIconColors> =
-        darkIconInteractor
+    fun iconColors(displayId: Int): Flow<NotificationIconColors> {
+        return darkIconInteractor
             .darkState(displayId)
             .map { (areas: Collection<Rect>, tint: Int) -> IconColorsImpl(tint, areas) }
             .flowOn(bgContext)
             .conflate()
             .distinctUntilChanged()
+    }
 
     /** [NotificationIconsViewData] indicating which icons to display in the view. */
     val icons: Flow<NotificationIconsViewData> =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsController.kt
index 2c5d9c2..3c2051f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsController.kt
@@ -20,7 +20,6 @@
 import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption
 import com.android.systemui.statusbar.NotificationPresenter
 import com.android.systemui.statusbar.notification.NotificationActivityStarter
-import com.android.systemui.statusbar.notification.collection.render.NotifStackController
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer
 
 /**
@@ -33,7 +32,6 @@
     fun initialize(
         presenter: NotificationPresenter,
         listContainer: NotificationListContainer,
-        stackController: NotifStackController,
         notificationActivityStarter: NotificationActivityStarter,
     )
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
index ea6a60b..0a9899e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
@@ -34,7 +34,6 @@
 import com.android.systemui.statusbar.notification.collection.init.NotifPipelineInitializer
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
-import com.android.systemui.statusbar.notification.collection.render.NotifStackController
 import com.android.systemui.statusbar.notification.interruption.HeadsUpViewBinder
 import com.android.systemui.statusbar.notification.logging.NotificationLogger
 import com.android.systemui.statusbar.notification.row.NotifBindPipelineInitializer
@@ -76,7 +75,6 @@
     override fun initialize(
         presenter: NotificationPresenter,
         listContainer: NotificationListContainer,
-        stackController: NotifStackController,
         notificationActivityStarter: NotificationActivityStarter,
     ) {
         notificationListener.registerAsSystemService()
@@ -101,7 +99,7 @@
 
         notifPipelineInitializer
             .get()
-            .initialize(notificationListener, notificationRowBinder, listContainer, stackController)
+            .initialize(notificationListener, notificationRowBinder, listContainer)
 
         targetSdkResolver.initialize(notifPipeline.get())
         notificationsMediaManager.setUpWithPresenter(presenter)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerStub.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerStub.kt
index 148b3f0..92d96f9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerStub.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerStub.kt
@@ -21,7 +21,6 @@
 import com.android.systemui.statusbar.NotificationListener
 import com.android.systemui.statusbar.NotificationPresenter
 import com.android.systemui.statusbar.notification.NotificationActivityStarter
-import com.android.systemui.statusbar.notification.collection.render.NotifStackController
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer
 import javax.inject.Inject
 
@@ -35,7 +34,6 @@
     override fun initialize(
         presenter: NotificationPresenter,
         listContainer: NotificationListContainer,
-        stackController: NotifStackController,
         notificationActivityStarter: NotificationActivityStarter,
     ) {
         // Always connect the listener even if notification-handling is disabled. Being a listener
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
index c6832bc..cc4be57 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationLogger.java
@@ -20,7 +20,6 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemClock;
-import android.os.Trace;
 import android.service.notification.NotificationListenerService;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -29,6 +28,7 @@
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 
+import com.android.app.tracing.coroutines.TrackTracer;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.statusbar.IStatusBarService;
@@ -152,8 +152,8 @@
 
             mExpansionStateLogger.onVisibilityChanged(
                     mTmpCurrentlyVisibleNotifications, mTmpCurrentlyVisibleNotifications);
-            Trace.traceCounter(Trace.TRACE_TAG_APP, "Notifications [Active]", N);
-            Trace.traceCounter(Trace.TRACE_TAG_APP, "Notifications [Visible]",
+            TrackTracer.instantForGroup("Notifications", "Active", N);
+            TrackTracer.instantForGroup("Notifications", "Visible",
                     mCurrentlyVisibleNotifications.size());
 
             recycleAllVisibilityObjects(mTmpNoLongerVisibleNotifications);
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 5a52c37..95604c1 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
@@ -1267,6 +1267,9 @@
         }
         if (mExpandedWhenPinned) {
             return Math.max(getMaxExpandHeight(), getHeadsUpHeight());
+        } else if (android.app.Flags.compactHeadsUpNotification()
+                && getShowingLayout().isHUNCompact()) {
+            return getHeadsUpHeight();
         } else if (atLeastMinHeight) {
             return Math.max(getCollapsedHeight(), getHeadsUpHeight());
         } else {
@@ -3680,11 +3683,18 @@
         return super.disallowSingleClick(event);
     }
 
+    // TODO: b/388470175 - Although this does get triggered when a notification
+    // is expanded by the system (e.g. the first notication in the shade), it
+    // will not be when a notification is collapsed by the system (such as when
+    // the shade is closed).
     private void onExpansionChanged(boolean userAction, boolean wasExpanded) {
         boolean nowExpanded = isExpanded();
         if (mIsSummaryWithChildren && (!mIsMinimized || wasExpanded)) {
             nowExpanded = mGroupExpansionManager.isGroupExpanded(mEntry);
         }
+        // Note: nowExpanded is going to be true here on the first expansion of minimized groups,
+        // even though the group itself is not expanded. Use mGroupExpansionManager to get the real
+        // group expansion if needed.
         if (nowExpanded != wasExpanded) {
             updateShelfIconColor();
             if (mLogger != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
index 70e27a9..7c44eae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar.notification.row;
 
+import static android.app.Flags.notificationsRedesignTemplates;
+
 import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
 import static com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_SENSITIVE_CONTENT;
 import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_CONTRACTED;
@@ -479,15 +481,16 @@
                     logger.logAsyncTaskProgress(entryForLogging,
                             "creating low-priority group summary remote view");
                     result.mNewMinimizedGroupHeaderView =
-                            builder.makeLowPriorityContentView(true /* useRegularSubtext */);
+                            builder.makeLowPriorityContentView(/* useRegularSubtext = */ true,
+                                    /* highlightExpander = */ notificationsRedesignTemplates());
                 }
             }
             setNotifsViewsInflaterFactory(result, row, notifLayoutInflaterFactoryProvider);
             result.packageContext = packageContext;
             result.headsUpStatusBarText = builder.getHeadsUpStatusBarText(
-                    false /* showingPublic */);
+                    /* showingPublic = */ false);
             result.headsUpStatusBarTextPublic = builder.getHeadsUpStatusBarText(
-                    true /* showingPublic */);
+                    /* showingPublic = */ true);
 
             return result;
         });
@@ -1136,7 +1139,8 @@
     private static RemoteViews createContentView(Notification.Builder builder,
             boolean isMinimized, boolean useLarge) {
         if (isMinimized) {
-            return builder.makeLowPriorityContentView(false /* useRegularSubtext */);
+            return builder.makeLowPriorityContentView(/* useRegularSubtext = */ false,
+                    /* highlightExpander = */ false);
         }
         return builder.createContentView(useLarge);
     }
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 786d7d9..0d29981 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
@@ -207,6 +207,8 @@
     private boolean mContentAnimating;
     private UiEventLogger mUiEventLogger;
 
+    private boolean mIsHUNCompact;
+
     public NotificationContentView(Context context, AttributeSet attrs) {
         super(context, attrs);
         mHybridGroupManager = new HybridGroupManager(getContext());
@@ -543,6 +545,7 @@
         if (child == null) {
             mHeadsUpChild = null;
             mHeadsUpWrapper = null;
+            mIsHUNCompact = false;
             if (mTransformationStartVisibleType == VISIBLE_TYPE_HEADSUP) {
                 mTransformationStartVisibleType = VISIBLE_TYPE_NONE;
             }
@@ -556,8 +559,9 @@
         mHeadsUpWrapper = NotificationViewWrapper.wrap(getContext(), child,
                 mContainingNotification);
 
-        if (Flags.compactHeadsUpNotification()
-                && mHeadsUpWrapper instanceof NotificationCompactHeadsUpTemplateViewWrapper) {
+        mIsHUNCompact = Flags.compactHeadsUpNotification()
+                && mHeadsUpWrapper instanceof NotificationCompactHeadsUpTemplateViewWrapper;
+        if (mIsHUNCompact) {
             logCompactHUNShownEvent();
         }
 
@@ -902,6 +906,10 @@
         }
     }
 
+    public boolean isHUNCompact() {
+        return mIsHUNCompact;
+    }
+
     private boolean isGroupExpanded() {
         return mContainingNotification.isGroupExpanded();
     }
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 c619b17..ae9b69c 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
@@ -16,6 +16,7 @@
 package com.android.systemui.statusbar.notification.row
 
 import android.annotation.SuppressLint
+import android.app.Flags.notificationsRedesignTemplates
 import android.app.Notification
 import android.app.Notification.MessagingStyle
 import android.content.Context
@@ -887,7 +888,10 @@
                             entryForLogging,
                             "creating low-priority group summary remote view",
                         )
-                        builder.makeLowPriorityContentView(true /* useRegularSubtext */)
+                        builder.makeLowPriorityContentView(
+                            /* useRegularSubtext = */ true,
+                            /* highlightExpander = */ notificationsRedesignTemplates(),
+                        )
                     } else null
                 NewRemoteViews(
                         contracted = contracted,
@@ -1657,7 +1661,10 @@
             useLarge: Boolean,
         ): RemoteViews {
             return if (isMinimized) {
-                builder.makeLowPriorityContentView(false /* useRegularSubtext */)
+                builder.makeLowPriorityContentView(
+                    /* useRegularSubtext = */ false,
+                    /* highlightExpander = */ false,
+                )
             } else builder.createContentView(useLarge)
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/NotificationShelfIconContainer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/NotificationShelfIconContainer.kt
index 64d1654..5106ccc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/NotificationShelfIconContainer.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shelf/NotificationShelfIconContainer.kt
@@ -19,6 +19,7 @@
 import android.content.Context
 import android.util.AttributeSet
 import android.view.View
+import com.android.internal.annotations.VisibleForTesting
 import com.android.systemui.statusbar.notification.shared.NotificationMinimalism
 import com.android.systemui.statusbar.phone.NotificationIconContainer
 import kotlin.math.max
@@ -35,7 +36,8 @@
     /**
      * @return The left boundary (not the RTL compatible start) of the area that icons can be added.
      */
-    override fun getLeftBound(): Float {
+    @VisibleForTesting
+    public override fun getLeftBound(): Float {
         if (!NotificationMinimalism.isEnabled) {
             return super.getLeftBound()
         }
@@ -49,7 +51,8 @@
     /**
      * @return The right boundary (not the RTL compatible end) of the area that icons can be added.
      */
-    override fun getRightBound(): Float {
+    @VisibleForTesting
+    public override fun getRightBound(): Float {
         if (!NotificationMinimalism.isEnabled) {
             return super.getRightBound()
         }
@@ -80,7 +83,8 @@
         return actualWidth - iconState.xTranslation - iconView.width
     }
 
-    private val isAlignedToRight: Boolean
+    @VisibleForTesting
+    val isAlignedToRight: Boolean
         get() {
             if (!NotificationMinimalism.isEnabled) {
                 return isLayoutRtl
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index 9fb7fad..e477c74 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -30,7 +30,6 @@
 import android.service.notification.StatusBarNotification;
 import android.util.AttributeSet;
 import android.util.Log;
-import android.view.ContextThemeWrapper;
 import android.view.LayoutInflater;
 import android.view.NotificationHeaderView;
 import android.view.View;
@@ -93,9 +92,16 @@
     private int mChildPadding;
     private int mDividerHeight;
     private float mDividerAlpha;
-    private int mNotificationHeaderMargin;
 
-    private int mNotificationTopPadding;
+    private int mHeaderHeight;
+    /** Margin needed at the top in the collapsed group, to allow space for the header text. */
+    private int mCollapsedHeaderMargin;
+    /**
+     * Spacing needed in addition to {@link this#mCollapsedHeaderMargin} when the group is expanded,
+     * to accommodate the full header. It doesn't include the spacing needed for the first divider.
+     */
+    private int mAdditionalExpandedHeaderMargin;
+
     private float mCollapsedBottomPadding;
     private boolean mChildrenExpanded;
     private ExpandableNotificationRow mContainingNotification;
@@ -105,7 +111,6 @@
     private boolean mUserLocked;
     private int mActualHeight;
     private boolean mNeverAppliedGroupState;
-    private int mHeaderHeight;
 
     /**
      * Whether or not individual notifications that are part of this container will have shadows.
@@ -168,15 +173,18 @@
         mDividerHeight = res.getDimensionPixelOffset(
                 R.dimen.notification_children_container_divider_height);
         mDividerAlpha = res.getFloat(R.dimen.notification_divider_alpha);
-        mNotificationHeaderMargin = notificationsRedesignTemplates()
-                ? Notification.Builder.getContentMarginTop(getContext(),
-                    R.dimen.notification_2025_children_container_margin_top)
-                : res.getDimensionPixelOffset(R.dimen.notification_children_container_margin_top);
-        mNotificationTopPadding = res.getDimensionPixelOffset(
-                R.dimen.notification_children_container_top_padding);
-        mHeaderHeight = notificationsRedesignTemplates()
-                ? res.getDimensionPixelSize(R.dimen.notification_2025_header_height)
-                : mNotificationHeaderMargin + mNotificationTopPadding;
+        if (notificationsRedesignTemplates()) {
+            mHeaderHeight = res.getDimensionPixelSize(R.dimen.notification_2025_header_height);
+            mCollapsedHeaderMargin = Notification.Builder.getContentMarginTop(getContext(),
+                    R.dimen.notification_2025_children_container_margin_top);
+            mAdditionalExpandedHeaderMargin = mHeaderHeight - mCollapsedHeaderMargin;
+        } else {
+            mCollapsedHeaderMargin = res.getDimensionPixelOffset(
+                    R.dimen.notification_children_container_margin_top);
+            mAdditionalExpandedHeaderMargin = res.getDimensionPixelOffset(
+                    R.dimen.notification_children_container_top_padding);
+            mHeaderHeight = mCollapsedHeaderMargin + mAdditionalExpandedHeaderMargin;
+        }
         mCollapsedBottomPadding = res.getDimensionPixelOffset(
                 R.dimen.notification_children_collapsed_bottom_padding);
         mEnableShadowOnChildNotifications =
@@ -189,7 +197,7 @@
                 res.getBoolean(R.bool.config_hideDividersDuringExpand);
         mTranslationForHeader = res.getDimensionPixelOffset(
                 com.android.internal.R.dimen.notification_content_margin)
-                - mNotificationHeaderMargin;
+                - mCollapsedHeaderMargin;
         mHybridGroupManager.initDimens();
         mMinSingleLineHeight = getResources().getDimensionPixelSize(
                 R.dimen.conversation_single_line_face_pile_size);
@@ -251,7 +259,7 @@
                     newHeightSpec);
         }
         int dividerHeightSpec = MeasureSpec.makeMeasureSpec(mDividerHeight, MeasureSpec.EXACTLY);
-        int height = mNotificationHeaderMargin + mNotificationTopPadding;
+        int height = mCollapsedHeaderMargin + mAdditionalExpandedHeaderMargin;
         int childCount =
                 Math.min(mAttachedChildren.size(), NUMBER_OF_CHILDREN_WHEN_CHILDREN_EXPANDED);
         int collapsedChildren = getMaxAllowedVisibleChildren(true /* likeCollapsed */);
@@ -560,7 +568,8 @@
                 builder = Notification.Builder.recoverBuilder(getContext(),
                         notification.getNotification());
             }
-            header = builder.makeLowPriorityContentView(true /* useRegularSubtext */);
+            header = builder.makeLowPriorityContentView(true /* useRegularSubtext */,
+                    notificationsRedesignTemplates() /* highlightExpander */);
             if (mMinimizedGroupHeader == null) {
                 mMinimizedGroupHeader = (NotificationHeaderView) header.apply(getContext(),
                         this);
@@ -704,7 +713,7 @@
                 return mMinimizedGroupHeader.getHeight();
             }
         }
-        int intrinsicHeight = mNotificationHeaderMargin + mCurrentHeaderTranslation;
+        int intrinsicHeight = mCollapsedHeaderMargin + mCurrentHeaderTranslation;
         int visibleChildren = 0;
         int childCount = mAttachedChildren.size();
         boolean firstChild = true;
@@ -728,11 +737,11 @@
                 if (mUserLocked) {
                     intrinsicHeight += NotificationUtils.interpolate(
                             0,
-                            mNotificationTopPadding + mDividerHeight,
+                            mAdditionalExpandedHeaderMargin + mDividerHeight,
                             expandFactor);
                 } else {
                     intrinsicHeight += childrenExpanded
-                            ? mNotificationTopPadding + mDividerHeight
+                            ? mAdditionalExpandedHeaderMargin + mDividerHeight
                             : 0;
                 }
                 firstChild = false;
@@ -757,7 +766,7 @@
      */
     public void updateState(ExpandableViewState parentState) {
         int childCount = mAttachedChildren.size();
-        int yPosition = mNotificationHeaderMargin + mCurrentHeaderTranslation;
+        int yPosition = mCollapsedHeaderMargin + mCurrentHeaderTranslation;
         boolean firstChild = true;
         int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren();
         int lastVisibleIndex = maxAllowedVisibleChildren - 1;
@@ -785,10 +794,12 @@
                 if (expandingToExpandedGroup) {
                     yPosition += NotificationUtils.interpolate(
                             0,
-                            mNotificationTopPadding + mDividerHeight,
+                            mAdditionalExpandedHeaderMargin + mDividerHeight,
                             expandFactor);
                 } else {
-                    yPosition += mChildrenExpanded ? mNotificationTopPadding + mDividerHeight : 0;
+                    yPosition += mChildrenExpanded
+                            ? mAdditionalExpandedHeaderMargin + mDividerHeight
+                            : 0;
                 }
                 firstChild = false;
             }
@@ -851,7 +862,7 @@
                 }
             } else {
                 mGroupOverFlowState.setYTranslation(
-                        mGroupOverFlowState.getYTranslation() + mNotificationHeaderMargin);
+                        mGroupOverFlowState.getYTranslation() + mCollapsedHeaderMargin);
                 mGroupOverFlowState.setAlpha(0.0f);
             }
         }
@@ -1147,10 +1158,12 @@
         return mContainingNotification;
     }
 
+    @Nullable
     public NotificationViewWrapper getNotificationViewWrapper() {
         return mGroupHeaderWrapper;
     }
 
+    @Nullable
     public NotificationViewWrapper getMinimizedGroupHeaderWrapper() {
         return mMinimizedGroupHeaderWrapper;
     }
@@ -1160,10 +1173,12 @@
         return mCurrentHeader;
     }
 
+    @Nullable
     public NotificationHeaderView getGroupHeader() {
         return mGroupHeader;
     }
 
+    @Nullable
     public NotificationHeaderView getMinimizedNotificationHeader() {
         return mMinimizedGroupHeader;
     }
@@ -1300,8 +1315,8 @@
             return getMinHeight(NUMBER_OF_CHILDREN_WHEN_SYSTEM_EXPANDED, true
                     /* likeHighPriority */);
         }
-        int maxContentHeight = mNotificationHeaderMargin + mCurrentHeaderTranslation
-                + mNotificationTopPadding;
+        int maxContentHeight = mCollapsedHeaderMargin + mCurrentHeaderTranslation
+                + mAdditionalExpandedHeaderMargin;
         int visibleChildren = 0;
         int childCount = mAttachedChildren.size();
         for (int i = 0; i < childCount; i++) {
@@ -1363,8 +1378,8 @@
     }
 
     private int getVisibleChildrenExpandHeight() {
-        int intrinsicHeight = mNotificationHeaderMargin + mCurrentHeaderTranslation
-                + mNotificationTopPadding + mDividerHeight;
+        int intrinsicHeight = mCollapsedHeaderMargin + mCurrentHeaderTranslation
+                + mAdditionalExpandedHeaderMargin + mDividerHeight;
         int visibleChildren = 0;
         int childCount = mAttachedChildren.size();
         int maxAllowedVisibleChildren = getMaxAllowedVisibleChildren(true /* forceCollapsed */);
@@ -1429,7 +1444,7 @@
             }
             return mMinimizedGroupHeader.getHeight();
         }
-        int minExpandHeight = mNotificationHeaderMargin + headerTranslation;
+        int minExpandHeight = mCollapsedHeaderMargin + headerTranslation;
         int visibleChildren = 0;
         boolean firstChild = true;
         int childCount = mAttachedChildren.size();
@@ -1517,18 +1532,13 @@
             // The overflow number is not used, so its color is irrelevant; skip this
             return;
         }
-        int color = mContainingNotification.getNotificationColor();
-        Resources.Theme theme = new ContextThemeWrapper(mContext,
-                com.android.internal.R.style.Theme_DeviceDefault_DayNight).getTheme();
-
-        color = mContext.getColor(com.android.internal.R.color.materialColorPrimary);
-
+        int color = mContext.getColor(com.android.internal.R.color.materialColorPrimary);
         mHybridGroupManager.setOverflowNumberColor(mOverflowNumber, color);
     }
 
     public int getPositionInLinearLayout(View childInGroup) {
-        int position = mNotificationHeaderMargin + mCurrentHeaderTranslation
-                + mNotificationTopPadding;
+        int position = mCollapsedHeaderMargin + mCurrentHeaderTranslation
+                + mAdditionalExpandedHeaderMargin;
 
         for (int i = 0; i < mAttachedChildren.size(); i++) {
             ExpandableNotificationRow child = mAttachedChildren.get(i);
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 50e5a23..76591ac 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
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.notification.stack;
 
+import static android.app.Flags.notificationsRedesignTemplates;
 import static android.os.Trace.TRACE_TAG_APP;
 import static android.view.MotionEvent.ACTION_CANCEL;
 import static android.view.MotionEvent.ACTION_UP;
@@ -62,6 +63,7 @@
 import android.view.InputDevice;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
+import android.view.NotificationHeaderView;
 import android.view.VelocityTracker;
 import android.view.View;
 import android.view.ViewConfiguration;
@@ -106,7 +108,6 @@
 import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
 import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyShadeFix;
 import com.android.systemui.statusbar.notification.emptyshade.ui.view.EmptyShadeView;
-import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor;
 import com.android.systemui.statusbar.notification.footer.ui.view.FooterView;
 import com.android.systemui.statusbar.notification.headsup.HeadsUpTouchHelper;
 import com.android.systemui.statusbar.notification.headsup.HeadsUpUtil;
@@ -701,9 +702,6 @@
         if (!ModesEmptyShadeFix.isEnabled()) {
             inflateEmptyShadeView();
         }
-        if (!FooterViewRefactor.isEnabled()) {
-            inflateFooterView();
-        }
     }
 
     /**
@@ -739,22 +737,12 @@
     }
 
     void reinflateViews() {
-        if (!FooterViewRefactor.isEnabled()) {
-            inflateFooterView();
-            updateFooter();
-        }
         if (!ModesEmptyShadeFix.isEnabled()) {
             inflateEmptyShadeView();
         }
         mSectionsManager.reinflateViews();
     }
 
-    public void setIsRemoteInputActive(boolean isActive) {
-        FooterViewRefactor.assertInLegacyMode();
-        mIsRemoteInputActive = isActive;
-        updateFooter();
-    }
-
     void sendRemoteInputRowBottomBound(Float bottom) {
         if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
         if (bottom != null) {
@@ -764,43 +752,6 @@
         mScrollViewFields.sendRemoteInputRowBottomBound(bottom);
     }
 
-    /** Setter for filtered notifs, to be removed with the FooterViewRefactor flag. */
-    public void setHasFilteredOutSeenNotifications(boolean hasFilteredOutSeenNotifications) {
-        FooterViewRefactor.assertInLegacyMode();
-        mHasFilteredOutSeenNotifications = hasFilteredOutSeenNotifications;
-    }
-
-    @VisibleForTesting
-    public void updateFooter() {
-        FooterViewRefactor.assertInLegacyMode();
-        if (mFooterView == null || mController == null) {
-            return;
-        }
-        final boolean showHistory = mController.isHistoryEnabled();
-        final boolean showDismissView = shouldShowDismissView();
-
-        updateFooterView(shouldShowFooterView(showDismissView)/* visible */,
-                showDismissView /* showDismissView */,
-                showHistory/* showHistory */);
-    }
-
-    private boolean shouldShowDismissView() {
-        FooterViewRefactor.assertInLegacyMode();
-        return mController.hasActiveClearableNotifications(ROWS_ALL);
-    }
-
-    private boolean shouldShowFooterView(boolean showDismissView) {
-        FooterViewRefactor.assertInLegacyMode();
-        return (showDismissView || mController.getVisibleNotificationCount() > 0)
-                && mIsCurrentUserSetup // see: b/193149550
-                && !onKeyguard()
-                && mUpcomingStatusBarState != StatusBarState.KEYGUARD
-                // quick settings don't affect notifications when not in full screen
-                && (getQsExpansionFraction() != 1 || !mQsFullScreen)
-                && !mScreenOffAnimationController.shouldHideNotificationsFooter()
-                && !mIsRemoteInputActive;
-    }
-
     void updateBgColor() {
         for (int i = 0; i < getChildCount(); i++) {
             View child = getChildAt(i);
@@ -1859,9 +1810,6 @@
      */
     private float getAppearEndPosition() {
         SceneContainerFlag.assertInLegacyMode();
-        if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) {
-            return getAppearEndPositionLegacy();
-        }
 
         int appearPosition = mAmbientState.getStackTopMargin();
         if (mEmptyShadeView.getVisibility() == GONE) {
@@ -1881,32 +1829,6 @@
         return appearPosition + (onKeyguard() ? getTopPadding() : getIntrinsicPadding());
     }
 
-    /**
-     * The version of {@code getAppearEndPosition} that uses the notif count. The view shouldn't
-     * need to know about that, so we want to phase this out with the footer view refactor.
-     */
-    private float getAppearEndPositionLegacy() {
-        FooterViewRefactor.assertInLegacyMode();
-
-        int appearPosition = mAmbientState.getStackTopMargin();
-        int visibleNotifCount = mController.getVisibleNotificationCount();
-        if (mEmptyShadeView.getVisibility() == GONE && visibleNotifCount > 0) {
-            if (isHeadsUpTransition()
-                    || (mInHeadsUpPinnedMode && !mAmbientState.isDozing())) {
-                if (mShelf.getVisibility() != GONE && visibleNotifCount > 1) {
-                    appearPosition += mShelf.getIntrinsicHeight() + mPaddingBetweenElements;
-                }
-                appearPosition += getTopHeadsUpPinnedHeight()
-                        + getPositionInLinearLayout(mAmbientState.getTrackedHeadsUpRow());
-            } else if (mShelf.getVisibility() != GONE) {
-                appearPosition += mShelf.getIntrinsicHeight();
-            }
-        } else {
-            appearPosition = mEmptyShadeView.getHeight();
-        }
-        return appearPosition + (onKeyguard() ? getTopPadding() : getIntrinsicPadding());
-    }
-
     private boolean isHeadsUpTransition() {
         return mAmbientState.getTrackedHeadsUpRow() != null;
     }
@@ -1926,8 +1848,7 @@
             // This can't use expansion fraction as that goes only from 0 to 1. Also when
             // appear fraction for HUN is 0, expansion fraction will be already around 0.2-0.3
             // and that makes translation jump immediately.
-            float appearEndPosition = FooterViewRefactor.isEnabled() ? getAppearEndPosition()
-                    : getAppearEndPositionLegacy();
+            float appearEndPosition = getAppearEndPosition();
             float appearStartPosition = getAppearStartPosition();
             float hunAppearFraction = (height - appearStartPosition)
                     / (appearEndPosition - appearStartPosition);
@@ -2661,12 +2582,6 @@
     }
 
     @Override
-    public int getHeadsUpInset() {
-        if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return 0;
-        return mHeadsUpInset;
-    }
-
-    @Override
     public int getStackBottomInset() {
         return mPaddingBetweenElements + mShelf.getIntrinsicHeight();
     }
@@ -4852,15 +4767,6 @@
         }
     }
 
-    /**
-     * Returns whether or not a History button is shown in the footer. If there is no footer, then
-     * this will return false.
-     **/
-    public boolean isHistoryShown() {
-        FooterViewRefactor.assertInLegacyMode();
-        return mFooterView != null && mFooterView.isHistoryShown();
-    }
-
     /** Bind the {@link FooterView} to the NSSL. */
     public void setFooterView(@NonNull FooterView footerView) {
         int index = -1;
@@ -4870,18 +4776,6 @@
         }
         mFooterView = footerView;
         addView(mFooterView, index);
-        if (!FooterViewRefactor.isEnabled()) {
-            if (mManageButtonClickListener != null) {
-                mFooterView.setManageButtonClickListener(mManageButtonClickListener);
-            }
-            mFooterView.setClearAllButtonClickListener(v -> {
-                if (mFooterClearAllListener != null) {
-                    mFooterClearAllListener.onClearAll();
-                }
-                clearNotifications(ROWS_ALL, true /* closeShade */);
-                footerView.setClearAllButtonVisible(false /* visible */, true /* animate */);
-            });
-        }
     }
 
     public void setEmptyShadeView(EmptyShadeView emptyShadeView) {
@@ -4894,13 +4788,6 @@
         addView(mEmptyShadeView, index);
     }
 
-    /** Legacy version, should be removed with the footer refactor flag. */
-    public void updateEmptyShadeView(boolean visible, boolean areNotificationsHiddenInShade) {
-        FooterViewRefactor.assertInLegacyMode();
-        updateEmptyShadeView(visible, areNotificationsHiddenInShade,
-                mHasFilteredOutSeenNotifications);
-    }
-
     /** Trigger an update for the empty shade resources and visibility. */
     public void updateEmptyShadeView(boolean visible, boolean areNotificationsHiddenInShade,
             boolean hasFilteredOutSeenNotifications) {
@@ -4953,18 +4840,6 @@
         return mEmptyShadeView.isVisible();
     }
 
-    public void updateFooterView(boolean visible, boolean showDismissView, boolean showHistory) {
-        FooterViewRefactor.assertInLegacyMode();
-        if (mFooterView == null || mNotificationStackSizeCalculator == null) {
-            return;
-        }
-        boolean animate = mIsExpanded && mAnimationsEnabled;
-        mFooterView.setVisible(visible, animate);
-        mFooterView.showHistory(showHistory);
-        mFooterView.setClearAllButtonVisible(showDismissView, animate);
-        mFooterView.setFooterLabelVisible(mHasFilteredOutSeenNotifications);
-    }
-
     @VisibleForTesting
     public void setClearAllInProgress(boolean clearAllInProgress) {
         mClearAllInProgress = clearAllInProgress;
@@ -5248,10 +5123,8 @@
 
     public void setQsFullScreen(boolean qsFullScreen) {
         SceneContainerFlag.assertInLegacyMode();
-        if (FooterViewRefactor.isEnabled()) {
-            if (qsFullScreen == mQsFullScreen) {
-                return;  // no change
-            }
+        if (qsFullScreen == mQsFullScreen) {
+            return;  // no change
         }
         mQsFullScreen = qsFullScreen;
         updateAlgorithmLayoutMinHeight();
@@ -5270,8 +5143,6 @@
 
     public void setQsExpansionFraction(float qsExpansionFraction) {
         SceneContainerFlag.assertInLegacyMode();
-        boolean footerAffected = getQsExpansionFraction() != qsExpansionFraction
-                && (getQsExpansionFraction() == 1 || qsExpansionFraction == 1);
         mQsExpansionFraction = qsExpansionFraction;
         updateUseRoundedRectClipping();
 
@@ -5280,9 +5151,6 @@
         if (getOwnScrollY() > 0) {
             setOwnScrollY((int) MathUtils.lerp(getOwnScrollY(), 0, getQsExpansionFraction()));
         }
-        if (!FooterViewRefactor.isEnabled() && footerAffected) {
-            updateFooter();
-        }
     }
 
     @VisibleForTesting
@@ -5460,14 +5328,6 @@
         requestChildrenUpdate();
     }
 
-    void setUpcomingStatusBarState(int upcomingStatusBarState) {
-        FooterViewRefactor.assertInLegacyMode();
-        mUpcomingStatusBarState = upcomingStatusBarState;
-        if (mUpcomingStatusBarState != mStatusBarState) {
-            updateFooter();
-        }
-    }
-
     void onStatePostChange(boolean fromShadeLocked) {
         boolean onKeyguard = onKeyguard();
 
@@ -5476,9 +5336,6 @@
         }
 
         setExpandingEnabled(!onKeyguard);
-        if (!FooterViewRefactor.isEnabled()) {
-            updateFooter();
-        }
         requestChildrenUpdate();
         onUpdateRowStates();
         updateVisibility();
@@ -5494,8 +5351,7 @@
         if (mEmptyShadeView == null || mEmptyShadeView.getVisibility() == GONE) {
             return getMinExpansionHeight();
         } else {
-            return FooterViewRefactor.isEnabled() ? getAppearEndPosition()
-                    : getAppearEndPositionLegacy();
+            return getAppearEndPosition();
         }
     }
 
@@ -5587,12 +5443,6 @@
                     for (int i = 0; i < childCount; i++) {
                         ExpandableView child = getChildAtIndex(i);
                         child.dump(pw, args);
-                        if (!FooterViewRefactor.isEnabled()) {
-                            if (child instanceof FooterView) {
-                                DumpUtilsKt.withIncreasedIndent(pw,
-                                        () -> dumpFooterViewVisibility(pw));
-                            }
-                        }
                         pw.println();
                     }
                     int transientViewCount = getTransientViewCount();
@@ -5619,45 +5469,6 @@
         pw.append(" bottomRadius=").println(mBgCornerRadii[4]);
     }
 
-    private void dumpFooterViewVisibility(IndentingPrintWriter pw) {
-        FooterViewRefactor.assertInLegacyMode();
-        final boolean showDismissView = shouldShowDismissView();
-
-        pw.println("showFooterView: " + shouldShowFooterView(showDismissView));
-        DumpUtilsKt.withIncreasedIndent(
-                pw,
-                () -> {
-                    pw.println("showDismissView: " + showDismissView);
-                    DumpUtilsKt.withIncreasedIndent(
-                            pw,
-                            () -> {
-                                pw.println(
-                                        "hasActiveClearableNotifications: "
-                                                + mController.hasActiveClearableNotifications(
-                                                        ROWS_ALL));
-                            });
-                    pw.println();
-                    pw.println("showHistory: " + mController.isHistoryEnabled());
-                    pw.println();
-                    pw.println(
-                            "visibleNotificationCount: "
-                                    + mController.getVisibleNotificationCount());
-                    pw.println("mIsCurrentUserSetup: " + mIsCurrentUserSetup);
-                    pw.println("onKeyguard: " + onKeyguard());
-                    pw.println("mUpcomingStatusBarState: " + mUpcomingStatusBarState);
-                    if (!SceneContainerFlag.isEnabled()) {
-                        pw.println("QsExpansionFraction: " + getQsExpansionFraction());
-                    }
-                    pw.println("mQsFullScreen: " + mQsFullScreen);
-                    pw.println(
-                            "mScreenOffAnimationController"
-                                    + ".shouldHideNotificationsFooter: "
-                                    + mScreenOffAnimationController
-                                            .shouldHideNotificationsFooter());
-                    pw.println("mIsRemoteInputActive: " + mIsRemoteInputActive);
-                });
-    }
-
     public boolean isFullyHidden() {
         return mAmbientState.isFullyHidden();
     }
@@ -5768,14 +5579,6 @@
         clearNotifications(ROWS_GENTLE, closeShade, hideSilentSection);
     }
 
-    /** Legacy version of clearNotifications below. Uses the old data source for notif stats. */
-    void clearNotifications(@SelectedRows int selection, boolean closeShade) {
-        FooterViewRefactor.assertInLegacyMode();
-        final boolean hideSilentSection = !mController.hasNotifications(
-                ROWS_GENTLE, false /* clearable */);
-        clearNotifications(selection, closeShade, hideSilentSection);
-    }
-
     /**
      * Collects a list of visible rows, and animates them away in a staggered fashion as if they
      * were dismissed. Notifications are dismissed in the backend via onClearAllAnimationsEnd.
@@ -5830,25 +5633,6 @@
         return canChildBeCleared(row) && matchesSelection(row, selection);
     }
 
-    /**
-     * Register a {@link View.OnClickListener} to be invoked when the Manage button is clicked.
-     */
-    public void setManageButtonClickListener(@Nullable OnClickListener listener) {
-        FooterViewRefactor.assertInLegacyMode();
-        mManageButtonClickListener = listener;
-        if (mFooterView != null) {
-            mFooterView.setManageButtonClickListener(mManageButtonClickListener);
-        }
-    }
-
-    @VisibleForTesting
-    protected void inflateFooterView() {
-        FooterViewRefactor.assertInLegacyMode();
-        FooterView footerView = (FooterView) LayoutInflater.from(mContext).inflate(
-                R.layout.status_bar_notification_footer, this, false);
-        setFooterView(footerView);
-    }
-
     private void inflateEmptyShadeView() {
         ModesEmptyShadeFix.assertInLegacyMode();
 
@@ -6095,11 +5879,6 @@
         mHighPriorityBeforeSpeedBump = highPriorityBeforeSpeedBump;
     }
 
-    void setFooterClearAllListener(FooterClearAllListener listener) {
-        FooterViewRefactor.assertInLegacyMode();
-        mFooterClearAllListener = listener;
-    }
-
     void setClearAllFinishedWhilePanelExpandedRunnable(Runnable runnable) {
         mClearAllFinishedWhilePanelExpandedRunnable = runnable;
     }
@@ -6398,17 +6177,6 @@
     }
 
     /**
-     * Sets whether the current user is set up, which is required to show the footer (b/193149550)
-     */
-    public void setCurrentUserSetup(boolean isCurrentUserSetup) {
-        FooterViewRefactor.assertInLegacyMode();
-        if (mIsCurrentUserSetup != isCurrentUserSetup) {
-            mIsCurrentUserSetup = isCurrentUserSetup;
-            updateFooter();
-        }
-    }
-
-    /**
      * Sets a {@link StackStateLogger} which is notified as the {@link StackStateAnimator} updates
      * the views.
      */
@@ -6849,15 +6617,29 @@
             mExpandedGroupView = changedRow;
             mNeedsAnimation = true;
         }
+
         changedRow.setChildrenExpanded(expanded);
         onChildHeightChanged(changedRow, false /* needsAnimation */);
+        updateGroupHeaderAlignment(changedRow, expanded);
 
-        runAfterAnimationFinished(new Runnable() {
-            @Override
-            public void run() {
-                changedRow.onFinishedExpansionChange();
-            }
-        });
+        runAfterAnimationFinished(changedRow::onFinishedExpansionChange);
+    }
+
+    private void updateGroupHeaderAlignment(ExpandableNotificationRow row, boolean expanded) {
+        if (!notificationsRedesignTemplates()) {
+            return;
+        }
+
+        NotificationChildrenContainer childrenContainer = row.getChildrenContainer();
+        if (childrenContainer == null) {
+            Log.wtf(TAG, "Tried to update group header alignment for something that's "
+                    + "not a group; key = " + row.getEntry().getKey());
+            return;
+        }
+        NotificationHeaderView header = childrenContainer.getGroupHeader();
+        if (header != null) {
+            header.centerTopLine(expanded);
+        }
     }
 
     private final ExpandHelper.Callback mExpandHelperCallback = new ExpandHelper.Callback() {
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 a33a9ed..c717e3b 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
@@ -29,15 +29,14 @@
 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.OnEmptySpaceClickListener;
 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.OnOverscrollTopChangedListener;
 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
-import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_GENTLE;
-import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_HIGH_PRIORITY;
 import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.SelectedRows;
 import static com.android.systemui.statusbar.notification.stack.StackStateAnimator.ANIMATION_DURATION_STANDARD;
-import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
 
 import android.animation.ObjectAnimator;
 import android.content.res.Configuration;
 import android.graphics.Point;
+import android.graphics.RenderEffect;
+import android.graphics.Shader;
 import android.os.Trace;
 import android.os.UserHandle;
 import android.provider.Settings;
@@ -64,14 +63,10 @@
 import com.android.systemui.Dumpable;
 import com.android.systemui.ExpandHelper;
 import com.android.systemui.Gefingerpoken;
-import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
 import com.android.systemui.classifier.Classifier;
 import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.dump.DumpManager;
-import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository;
-import com.android.systemui.keyguard.shared.model.KeyguardState;
-import com.android.systemui.keyguard.shared.model.TransitionStep;
 import com.android.systemui.media.controls.ui.controller.KeyguardMediaController;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.FalsingManager;
@@ -92,18 +87,13 @@
 import com.android.systemui.statusbar.LockscreenShadeTransitionController;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener;
-import com.android.systemui.statusbar.NotificationRemoteInputManager;
 import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.RemoteInputController;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.notification.ColorUpdateLogger;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
-import com.android.systemui.statusbar.notification.headsup.HeadsUpNotificationViewControllerEmptyImpl;
-import com.android.systemui.statusbar.notification.headsup.HeadsUpTouchHelper;
-import com.android.systemui.statusbar.notification.headsup.HeadsUpTouchHelper.HeadsUpNotificationViewController;
 import com.android.systemui.statusbar.notification.LaunchAnimationParameters;
-import com.android.systemui.statusbar.notification.NotificationActivityStarter;
 import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
 import com.android.systemui.statusbar.notification.collection.EntryWithDismissStats;
 import com.android.systemui.statusbar.notification.collection.NotifCollection;
@@ -116,13 +106,12 @@
 import com.android.systemui.statusbar.notification.collection.provider.NotificationDismissibilityProvider;
 import com.android.systemui.statusbar.notification.collection.provider.VisibilityLocationProviderDelegator;
 import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
-import com.android.systemui.statusbar.notification.collection.render.NotifStackController;
-import com.android.systemui.statusbar.notification.collection.render.NotifStats;
 import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
-import com.android.systemui.statusbar.notification.collection.render.SectionHeaderController;
-import com.android.systemui.statusbar.notification.dagger.SilentHeader;
-import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor;
-import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor;
+import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
+import com.android.systemui.statusbar.notification.headsup.HeadsUpNotificationViewControllerEmptyImpl;
+import com.android.systemui.statusbar.notification.headsup.HeadsUpTouchHelper;
+import com.android.systemui.statusbar.notification.headsup.HeadsUpTouchHelper.HeadsUpNotificationViewController;
+import com.android.systemui.statusbar.notification.headsup.OnHeadsUpChangedListener;
 import com.android.systemui.statusbar.notification.init.NotificationsController;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
@@ -137,13 +126,8 @@
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
-import com.android.systemui.statusbar.policy.DeviceProvisionedController;
-import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener;
-import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
-import com.android.systemui.statusbar.notification.headsup.OnHeadsUpChangedListener;
 import com.android.systemui.statusbar.policy.SensitiveNotificationProtectionController;
 import com.android.systemui.statusbar.policy.SplitShadeStateController;
-import com.android.systemui.statusbar.policy.ZenModeController;
 import com.android.systemui.tuner.TunerService;
 import com.android.systemui.util.Compile;
 import com.android.systemui.util.settings.SecureSettings;
@@ -179,10 +163,8 @@
     private HeadsUpTouchHelper mHeadsUpTouchHelper;
     private final NotificationRoundnessManager mNotificationRoundnessManager;
     private final TunerService mTunerService;
-    private final DeviceProvisionedController mDeviceProvisionedController;
     private final DynamicPrivacyController mDynamicPrivacyController;
     private final ConfigurationController mConfigurationController;
-    private final ZenModeController mZenModeController;
     private final MetricsLogger mMetricsLogger;
     private final ColorUpdateLogger mColorUpdateLogger;
 
@@ -193,7 +175,6 @@
     private final NotifPipeline mNotifPipeline;
     private final NotifCollection mNotifCollection;
     private final UiEventLogger mUiEventLogger;
-    private final NotificationRemoteInputManager mRemoteInputManager;
     private final VisibilityLocationProviderDelegator mVisibilityLocationProviderDelegator;
     private final ShadeController mShadeController;
     private final Provider<WindowRootView> mWindowRootView;
@@ -201,9 +182,7 @@
     private final SysuiStatusBarStateController mStatusBarStateController;
     private final KeyguardBypassController mKeyguardBypassController;
     private final PowerInteractor mPowerInteractor;
-    private final PrimaryBouncerInteractor mPrimaryBouncerInteractor;
     private final NotificationLockscreenUserManager mLockscreenUserManager;
-    private final SectionHeaderController mSilentHeaderController;
     private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
     private final InteractionJankMonitor mJankMonitor;
     private final NotificationStackSizeCalculator mNotificationStackSizeCalculator;
@@ -211,8 +190,6 @@
     private final NotificationStackScrollLogger mLogger;
 
     private final GroupExpansionManager mGroupExpansionManager;
-    private final SeenNotificationsInteractor mSeenNotificationsInteractor;
-    private final KeyguardTransitionRepository mKeyguardTransitionRepo;
     private NotificationStackScrollLayout mView;
     private TouchHandler mTouchHandler;
     private NotificationSwipeHelper mSwipeHelper;
@@ -220,7 +197,6 @@
     private Boolean mHistoryEnabled;
     private int mBarState;
     private HeadsUpAppearanceController mHeadsUpAppearanceController;
-    private boolean mIsInTransitionToAod = false;
 
     private final NotificationTargetsHelper mNotificationTargetsHelper;
     private final SecureSettings mSecureSettings;
@@ -235,11 +211,6 @@
 
     private final NotificationListContainerImpl mNotificationListContainer =
             new NotificationListContainerImpl();
-    private final NotifStackController mNotifStackController =
-            new NotifStackControllerImpl();
-
-    @Nullable
-    private NotificationActivityStarter mNotificationActivityStarter;
 
     @VisibleForTesting
     final View.OnAttachStateChangeListener mOnAttachStateChangeListener =
@@ -248,9 +219,6 @@
                 public void onViewAttachedToWindow(View v) {
                     mColorUpdateLogger.logTriggerEvent("NSSLC.onViewAttachedToWindow()");
                     mConfigurationController.addCallback(mConfigurationListener);
-                    if (!FooterViewRefactor.isEnabled()) {
-                        mZenModeController.addCallback(mZenModeControllerCallback);
-                    }
                     final int newBarState = mStatusBarStateController.getState();
                     if (newBarState != mBarState) {
                         mStateListener.onStateChanged(newBarState);
@@ -264,9 +232,6 @@
                 public void onViewDetachedFromWindow(View v) {
                     mColorUpdateLogger.logTriggerEvent("NSSLC.onViewDetachedFromWindow()");
                     mConfigurationController.removeCallback(mConfigurationListener);
-                    if (!FooterViewRefactor.isEnabled()) {
-                        mZenModeController.removeCallback(mZenModeControllerCallback);
-                    }
                     mStatusBarStateController.removeCallback(mStateListener);
                 }
             };
@@ -287,28 +252,6 @@
     @Nullable
     private ObjectAnimator mHideAlphaAnimator = null;
 
-    private final DeviceProvisionedListener mDeviceProvisionedListener =
-            new DeviceProvisionedListener() {
-                @Override
-                public void onDeviceProvisionedChanged() {
-                    updateCurrentUserIsSetup();
-                }
-
-                @Override
-                public void onUserSwitched() {
-                    updateCurrentUserIsSetup();
-                }
-
-                @Override
-                public void onUserSetupChanged() {
-                    updateCurrentUserIsSetup();
-                }
-
-                private void updateCurrentUserIsSetup() {
-                    mView.setCurrentUserSetup(mDeviceProvisionedController.isCurrentUserSetup());
-                }
-            };
-
     private final Runnable mSensitiveStateChangedListener = new Runnable() {
         @Override
         public void run() {
@@ -318,20 +261,10 @@
         }
     };
 
-    private final DynamicPrivacyController.Listener mDynamicPrivacyControllerListener = () -> {
-        if (!FooterViewRefactor.isEnabled()) {
-            // Let's update the footer once the notifications have been updated (in the next frame)
-            mView.post(this::updateFooter);
-        }
-    };
-
     @VisibleForTesting
     final ConfigurationListener mConfigurationListener = new ConfigurationListener() {
         @Override
         public void onDensityOrFontScaleChanged() {
-            if (!FooterViewRefactor.isEnabled()) {
-                updateShowEmptyShadeView();
-            }
             mView.reinflateViews();
         }
 
@@ -351,10 +284,6 @@
             mView.updateBgColor();
             mView.updateDecorViews();
             mView.reinflateViews();
-            if (!FooterViewRefactor.isEnabled()) {
-                updateShowEmptyShadeView();
-                updateFooter();
-            }
         }
 
         @Override
@@ -363,7 +292,6 @@
         }
     };
 
-    private NotifStats mNotifStats = NotifStats.getEmpty();
     private float mMaxAlphaForKeyguard = 1.0f;
     private String mMaxAlphaForKeyguardSource = "constructor";
     private float mMaxAlphaForUnhide = 1.0f;
@@ -401,19 +329,9 @@
                 }
 
                 @Override
-                public void onUpcomingStateChanged(int newState) {
-                    if (!FooterViewRefactor.isEnabled()) {
-                        mView.setUpcomingStatusBarState(newState);
-                    }
-                }
-
-                @Override
                 public void onStatePostChange() {
                     updateSensitivenessWithAnimation(mStatusBarStateController.goingToFullShade());
                     mView.onStatePostChange(mStatusBarStateController.fromShadeLocked());
-                    if (!FooterViewRefactor.isEnabled()) {
-                        updateImportantForAccessibility();
-                    }
                 }
             };
 
@@ -422,9 +340,6 @@
         public void onUserChanged(int userId) {
             updateSensitivenessWithAnimation(false);
             mHistoryEnabled = null;
-            if (!FooterViewRefactor.isEnabled()) {
-                updateFooter();
-            }
         }
     };
 
@@ -656,7 +571,7 @@
                                 == null) {
                             mHeadsUpManager.removeNotification(
                                     row.getEntry().getSbn().getKey(),
-                                    /* removeImmediately= */ true ,
+                                    /* removeImmediately= */ true,
                                     /* reason= */ "onChildSnappedBack"
                             );
                         }
@@ -714,14 +629,6 @@
                 }
             };
 
-    private final ZenModeController.Callback mZenModeControllerCallback =
-            new ZenModeController.Callback() {
-                @Override
-                public void onZenChanged(int zen) {
-                    updateShowEmptyShadeView();
-                }
-            };
-
     @Inject
     public NotificationStackScrollLayoutController(
             NotificationStackScrollLayout view,
@@ -734,16 +641,12 @@
             Provider<IStatusBarService> statusBarService,
             NotificationRoundnessManager notificationRoundnessManager,
             TunerService tunerService,
-            DeviceProvisionedController deviceProvisionedController,
             DynamicPrivacyController dynamicPrivacyController,
             @ShadeDisplayAware ConfigurationController configurationController,
             SysuiStatusBarStateController statusBarStateController,
             KeyguardMediaController keyguardMediaController,
             KeyguardBypassController keyguardBypassController,
             PowerInteractor powerInteractor,
-            PrimaryBouncerInteractor primaryBouncerInteractor,
-            KeyguardTransitionRepository keyguardTransitionRepo,
-            ZenModeController zenModeController,
             NotificationLockscreenUserManager lockscreenUserManager,
             MetricsLogger metricsLogger,
             ColorUpdateLogger colorUpdateLogger,
@@ -752,14 +655,11 @@
             FalsingManager falsingManager,
             NotificationSwipeHelper.Builder notificationSwipeHelperBuilder,
             GroupExpansionManager groupManager,
-            @SilentHeader SectionHeaderController silentHeaderController,
             NotifPipeline notifPipeline,
             NotifCollection notifCollection,
             LockscreenShadeTransitionController lockscreenShadeTransitionController,
             UiEventLogger uiEventLogger,
-            NotificationRemoteInputManager remoteInputManager,
             VisibilityLocationProviderDelegator visibilityLocationProviderDelegator,
-            SeenNotificationsInteractor seenNotificationsInteractor,
             NotificationListViewBinder viewBinder,
             ShadeController shadeController,
             Provider<WindowRootView> windowRootView,
@@ -775,7 +675,6 @@
             SensitiveNotificationProtectionController sensitiveNotificationProtectionController,
             WallpaperInteractor wallpaperInteractor) {
         mView = view;
-        mKeyguardTransitionRepo = keyguardTransitionRepo;
         mViewBinder = viewBinder;
         mStackStateLogger = stackLogger;
         mLogger = logger;
@@ -795,15 +694,12 @@
         }
         mNotificationRoundnessManager = notificationRoundnessManager;
         mTunerService = tunerService;
-        mDeviceProvisionedController = deviceProvisionedController;
         mDynamicPrivacyController = dynamicPrivacyController;
         mConfigurationController = configurationController;
         mStatusBarStateController = statusBarStateController;
         mKeyguardMediaController = keyguardMediaController;
         mKeyguardBypassController = keyguardBypassController;
         mPowerInteractor = powerInteractor;
-        mPrimaryBouncerInteractor = primaryBouncerInteractor;
-        mZenModeController = zenModeController;
         mLockscreenUserManager = lockscreenUserManager;
         mMetricsLogger = metricsLogger;
         mColorUpdateLogger = colorUpdateLogger;
@@ -815,13 +711,10 @@
         mJankMonitor = jankMonitor;
         mNotificationStackSizeCalculator = notificationStackSizeCalculator;
         mGroupExpansionManager = groupManager;
-        mSilentHeaderController = silentHeaderController;
         mNotifPipeline = notifPipeline;
         mNotifCollection = notifCollection;
         mUiEventLogger = uiEventLogger;
-        mRemoteInputManager = remoteInputManager;
         mVisibilityLocationProviderDelegator = visibilityLocationProviderDelegator;
-        mSeenNotificationsInteractor = seenNotificationsInteractor;
         mShadeController = shadeController;
         mWindowRootView = windowRootView;
         mNotificationTargetsHelper = notificationTargetsHelper;
@@ -850,18 +743,7 @@
         mView.setClearAllAnimationListener(this::onAnimationEnd);
         mView.setClearAllListener((selection) -> mUiEventLogger.log(
                 NotificationPanelEvent.fromSelection(selection)));
-        if (!FooterViewRefactor.isEnabled()) {
-            mView.setFooterClearAllListener(() ->
-                    mMetricsLogger.action(MetricsEvent.ACTION_DISMISS_ALL_NOTES));
-            mView.setIsRemoteInputActive(mRemoteInputManager.isRemoteInputActive());
-            mRemoteInputManager.addControllerCallback(new RemoteInputController.Callback() {
-                @Override
-                public void onRemoteInputActive(boolean active) {
-                    mView.setIsRemoteInputActive(active);
-                }
-            });
-        }
-        mView.setClearAllFinishedWhilePanelExpandedRunnable(()-> {
+        mView.setClearAllFinishedWhilePanelExpandedRunnable(() -> {
             final Runnable doCollapseRunnable = () ->
                     mShadeController.animateCollapseShade(CommandQueue.FLAG_EXCLUDE_NONE);
             mView.postDelayed(doCollapseRunnable, /* delayMillis = */ DELAY_BEFORE_SHADE_CLOSE);
@@ -889,19 +771,11 @@
         mView.setKeyguardBypassEnabled(mKeyguardBypassController.getBypassEnabled());
         mKeyguardBypassController
                 .registerOnBypassStateChangedListener(mView::setKeyguardBypassEnabled);
-        if (!FooterViewRefactor.isEnabled()) {
-            mView.setManageButtonClickListener(v -> {
-                if (mNotificationActivityStarter != null) {
-                    mNotificationActivityStarter.startHistoryIntent(v, mView.isHistoryShown());
-                }
-            });
-        }
 
         if (!SceneContainerFlag.isEnabled()) {
             mHeadsUpManager.addListener(mOnHeadsUpChangedListener);
         }
         mHeadsUpManager.setAnimationStateHandler(mView::setHeadsUpGoingAwayAnimationsAllowed);
-        mDynamicPrivacyController.addListener(mDynamicPrivacyControllerListener);
 
         mLockscreenShadeTransitionController.setStackScroller(this);
 
@@ -914,9 +788,6 @@
                     switch (key) {
                         case Settings.Secure.NOTIFICATION_HISTORY_ENABLED:
                             mHistoryEnabled = null;  // invalidate
-                            if (!FooterViewRefactor.isEnabled()) {
-                                updateFooter();
-                            }
                             break;
                         case HIGH_PRIORITY:
                             mView.setHighPriorityBeforeSpeedBump("1".equals(newValue));
@@ -938,12 +809,6 @@
             return kotlin.Unit.INSTANCE;
         });
 
-        if (!FooterViewRefactor.isEnabled()) {
-            // attach callback, and then call it to update mView immediately
-            mDeviceProvisionedController.addCallback(mDeviceProvisionedListener);
-            mDeviceProvisionedListener.onDeviceProvisionedChanged();
-        }
-
         if (screenshareNotificationHiding()) {
             mSensitiveNotificationProtectionController
                     .registerSensitiveStateListener(mSensitiveStateChangedListener);
@@ -953,20 +818,12 @@
             mOnAttachStateChangeListener.onViewAttachedToWindow(mView);
         }
         mView.addOnAttachStateChangeListener(mOnAttachStateChangeListener);
-        if (!FooterViewRefactor.isEnabled()) {
-            mSilentHeaderController.setOnClearSectionClickListener(v -> clearSilentNotifications());
-        }
 
         mGroupExpansionManager.registerGroupExpansionChangeListener(
                 (changedRow, expanded) -> mView.onGroupExpandChanged(changedRow, expanded));
 
         mViewBinder.bindWhileAttached(mView, this);
 
-        if (!FooterViewRefactor.isEnabled()) {
-            collectFlow(mView, mKeyguardTransitionRepo.getTransitions(),
-                    this::onKeyguardTransitionChanged);
-        }
-
         mView.setWallpaperInteractor(mWallpaperInteractor);
     }
 
@@ -1168,11 +1025,6 @@
         return mView != null && mView.isAddOrRemoveAnimationPending();
     }
 
-    public int getVisibleNotificationCount() {
-        FooterViewRefactor.assertInLegacyMode();
-        return mNotifStats.getNumActiveNotifs();
-    }
-
     public boolean isHistoryEnabled() {
         Boolean historyEnabled = mHistoryEnabled;
         if (historyEnabled == null) {
@@ -1284,9 +1136,6 @@
 
     public void setQsFullScreen(boolean fullScreen) {
         mView.setQsFullScreen(fullScreen);
-        if (!FooterViewRefactor.isEnabled()) {
-            updateShowEmptyShadeView();
-        }
     }
 
     public void setScrollingEnabled(boolean enabled) {
@@ -1390,6 +1239,22 @@
         updateAlpha();
     }
 
+    /**
+     * Applies a blur effect to the view.
+     *
+     * @param blurRadius Radius of blur
+     */
+    public void setBlurRadius(float blurRadius) {
+        if (blurRadius > 0.0f) {
+            mView.setRenderEffect(RenderEffect.createBlurEffect(
+                    blurRadius,
+                    blurRadius,
+                    Shader.TileMode.CLAMP));
+        } else {
+            mView.setRenderEffect(null);
+        }
+    }
+
     private void updateAlpha() {
         if (mView != null) {
             mView.setAlpha(Math.min(Math.min(mMaxAlphaFromView, mMaxAlphaForKeyguard),
@@ -1464,64 +1329,12 @@
     }
 
     /**
-     * Set the visibility of the view, and propagate it to specific children.
+     * Set the visibility of the view.
      *
      * @param visible either the view is visible or not.
      */
     public void updateVisibility(boolean visible) {
         mView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
-
-        // Refactor note: the empty shade's visibility doesn't seem to actually depend on the
-        // parent visibility (so this update seemingly doesn't do anything). Therefore, this is not
-        // modeled in the refactored code.
-        if (!FooterViewRefactor.isEnabled() && mView.getVisibility() == View.VISIBLE) {
-            // Synchronize EmptyShadeView visibility with the parent container.
-            updateShowEmptyShadeView();
-            updateImportantForAccessibility();
-        }
-    }
-
-    /**
-     * Update whether we should show the empty shade view ("no notifications" in the shade).
-     * <p>
-     * When in split mode, notifications are always visible regardless of the state of the
-     * QuickSettings panel. That being the case, empty view is always shown if the other conditions
-     * are true.
-     */
-    public void updateShowEmptyShadeView() {
-        FooterViewRefactor.assertInLegacyMode();
-
-        Trace.beginSection("NSSLC.updateShowEmptyShadeView");
-
-        final boolean shouldShow = getVisibleNotificationCount() == 0
-                && !mView.isQsFullScreen()
-                // Hide empty shade view when in transition to AOD.
-                // That avoids "No Notifications" to blink when transitioning to AOD.
-                // For more details, see: b/228790482
-                && !mIsInTransitionToAod
-                // Don't show any notification content if the bouncer is showing. See b/267060171.
-                && !mPrimaryBouncerInteractor.isBouncerShowing();
-
-        mView.updateEmptyShadeView(shouldShow, mZenModeController.areNotificationsHiddenInShade());
-
-        Trace.endSection();
-    }
-
-    /**
-     * Update the importantForAccessibility of NotificationStackScrollLayout.
-     * <p>
-     * We want the NSSL to be unimportant for accessibility when there's no
-     * notifications in it while the device is on lock screen, to avoid unlablel NSSL view.
-     * Otherwise, we want it to be important for accessibility to enable accessibility
-     * auto-scrolling in NSSL.
-     */
-    public void updateImportantForAccessibility() {
-        FooterViewRefactor.assertInLegacyMode();
-        if (getVisibleNotificationCount() == 0 && mView.onKeyguard()) {
-            mView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
-        } else {
-            mView.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
-        }
     }
 
     public boolean isShowingEmptyShadeView() {
@@ -1577,34 +1390,6 @@
         mView.setPulsing(pulsing, animatePulse);
     }
 
-    /**
-     * Return whether there are any clearable notifications
-     */
-    public boolean hasActiveClearableNotifications(@SelectedRows int selection) {
-        FooterViewRefactor.assertInLegacyMode();
-        return hasNotifications(selection, true /* clearable */);
-    }
-
-    public boolean hasNotifications(@SelectedRows int selection, boolean isClearable) {
-        FooterViewRefactor.assertInLegacyMode();
-        boolean hasAlertingMatchingClearable = isClearable
-                ? mNotifStats.getHasClearableAlertingNotifs()
-                : mNotifStats.getHasNonClearableAlertingNotifs();
-        boolean hasSilentMatchingClearable = isClearable
-                ? mNotifStats.getHasClearableSilentNotifs()
-                : mNotifStats.getHasNonClearableSilentNotifs();
-        switch (selection) {
-            case ROWS_GENTLE:
-                return hasSilentMatchingClearable;
-            case ROWS_HIGH_PRIORITY:
-                return hasAlertingMatchingClearable;
-            case ROWS_ALL:
-                return hasSilentMatchingClearable || hasAlertingMatchingClearable;
-            default:
-                throw new IllegalStateException("Bad selection: " + selection);
-        }
-    }
-
     /** Sets whether the NSSL is displayed over the unoccluded Lockscreen. */
     public void setOnLockscreen(boolean isOnLockscreen) {
         if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) return;
@@ -1637,9 +1422,6 @@
                 }
                 mHeadsUpManager.setRemoteInputActive(entry, remoteInputActive);
                 entry.notifyHeightChanged(true /* needsAnimation */);
-                if (!FooterViewRefactor.isEnabled()) {
-                    updateFooter();
-                }
             }
 
             public void lockScrollTo(NotificationEntry entry) {
@@ -1662,13 +1444,6 @@
         };
     }
 
-    public void updateFooter() {
-        FooterViewRefactor.assertInLegacyMode();
-        Trace.beginSection("NSSLC.updateFooter");
-        mView.updateFooter();
-        Trace.endSection();
-    }
-
     public void onUpdateRowStates() {
         mView.onUpdateRowStates();
     }
@@ -1695,18 +1470,10 @@
         return mView.getTransientViewCount();
     }
 
-    public View getTransientView(int i) {
-        return mView.getTransientView(i);
-    }
-
     public NotificationStackScrollLayout getView() {
         return mView;
     }
 
-    public float calculateGapHeight(ExpandableView previousView, ExpandableView child, int count) {
-        return mView.calculateGapHeight(previousView, child, count);
-    }
-
     NotificationRoundnessManager getNotificationRoundnessManager() {
         return mNotificationRoundnessManager;
     }
@@ -1715,10 +1482,6 @@
         return mNotificationListContainer;
     }
 
-    public NotifStackController getNotifStackController() {
-        return mNotifStackController;
-    }
-
     public void resetCheckSnoozeLeavebehind() {
         mView.resetCheckSnoozeLeavebehind();
     }
@@ -1772,13 +1535,6 @@
         return NotificationSwipeHelper.isTouchInView(event, view);
     }
 
-    public void clearSilentNotifications() {
-        FooterViewRefactor.assertInLegacyMode();
-        // Leave the shade open if there will be other notifs left over to clear
-        final boolean closeShade = !hasActiveClearableNotifications(ROWS_HIGH_PRIORITY);
-        mView.clearNotifications(ROWS_GENTLE, closeShade);
-    }
-
     private void onAnimationEnd(List<ExpandableNotificationRow> viewsToRemove,
             @SelectedRows int selectedRows) {
         if (selectedRows == ROWS_ALL) {
@@ -1880,10 +1636,6 @@
         mView.animateNextTopPaddingChange();
     }
 
-    public void setNotificationActivityStarter(NotificationActivityStarter activityStarter) {
-        mNotificationActivityStarter = activityStarter;
-    }
-
     public NotificationTargetsHelper getNotificationTargetsHelper() {
         return mNotificationTargetsHelper;
     }
@@ -1898,18 +1650,6 @@
     }
 
     @VisibleForTesting
-    void onKeyguardTransitionChanged(TransitionStep transitionStep) {
-        FooterViewRefactor.assertInLegacyMode();
-        boolean isTransitionToAod = transitionStep.getTo().equals(KeyguardState.AOD)
-                && (transitionStep.getFrom().equals(KeyguardState.GONE)
-                || transitionStep.getFrom().equals(KeyguardState.OCCLUDED));
-        if (mIsInTransitionToAod != isTransitionToAod) {
-            mIsInTransitionToAod = isTransitionToAod;
-            updateShowEmptyShadeView();
-        }
-    }
-
-    @VisibleForTesting
     TouchHandler getTouchHandler() {
         return mTouchHandler;
     }
@@ -2288,22 +2028,4 @@
                     && !mSwipeHelper.isSwiping();
         }
     }
-
-    private class NotifStackControllerImpl implements NotifStackController {
-        @Override
-        public void setNotifStats(@NonNull NotifStats notifStats) {
-            FooterViewRefactor.assertInLegacyMode();
-            mNotifStats = notifStats;
-
-            if (!FooterViewRefactor.isEnabled()) {
-                mView.setHasFilteredOutSeenNotifications(
-                        mSeenNotificationsInteractor
-                                .getHasFilteredOutSeenNotifications().getValue());
-
-                updateFooter();
-                updateShowEmptyShadeView();
-                updateImportantForAccessibility();
-            }
-        }
-    }
 }
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 1653029..06b989a 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
@@ -35,7 +35,6 @@
 import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.notification.SourceType;
 import com.android.systemui.statusbar.notification.emptyshade.ui.view.EmptyShadeView;
-import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor;
 import com.android.systemui.statusbar.notification.footer.ui.view.FooterView;
 import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -463,26 +462,23 @@
                 if (v == ambientState.getShelf()) {
                     continue;
                 }
-                if (FooterViewRefactor.isEnabled()) {
-                    if (v instanceof EmptyShadeView) {
-                        emptyShadeVisible = true;
-                    }
-                    if (v instanceof FooterView footerView) {
-                        if (emptyShadeVisible || notGoneIndex == 0) {
-                            // if the empty shade is visible or the footer is the first visible
-                            // view, we're in a transitory state so let's leave the footer alone.
-                            if (Flags.notificationsFooterVisibilityFix()
-                                    && !SceneContainerFlag.isEnabled()) {
-                                // ...except for the hidden state, to prevent it from flashing on
-                                // the screen (this piece is copied from updateChild, and is not
-                                // necessary in flexiglass).
-                                if (footerView.shouldBeHidden()
-                                        || !ambientState.isShadeExpanded()) {
-                                    footerView.getViewState().hidden = true;
-                                }
+                if (v instanceof EmptyShadeView) {
+                    emptyShadeVisible = true;
+                }
+                if (v instanceof FooterView footerView) {
+                    if (emptyShadeVisible || notGoneIndex == 0) {
+                        // if the empty shade is visible or the footer is the first visible
+                        // view, we're in a transitory state so let's leave the footer alone.
+                        if (Flags.notificationsFooterVisibilityFix()
+                                && !SceneContainerFlag.isEnabled()) {
+                            // ...except for the hidden state, to prevent it from flashing on
+                            // the screen (this piece is copied from updateChild, and is not
+                            // necessary in flexiglass).
+                            if (footerView.shouldBeHidden() || !ambientState.isShadeExpanded()) {
+                                footerView.getViewState().hidden = true;
                             }
-                            continue;
                         }
+                        continue;
                     }
                 }
 
@@ -699,44 +695,28 @@
                 viewEnd, /* hunMax */ ambientState.getMaxHeadsUpTranslation()
         );
         if (view instanceof FooterView) {
-            if (FooterViewRefactor.isEnabled()) {
-                if (SceneContainerFlag.isEnabled()) {
-                    final float footerEnd =
-                            stackTop + viewState.getYTranslation() + view.getIntrinsicHeight();
-                    final boolean noSpaceForFooter = footerEnd > ambientState.getStackCutoff();
-                    ((FooterView.FooterViewState) viewState).hideContent =
-                            noSpaceForFooter || (ambientState.isClearAllInProgress()
-                                    && !hasNonClearableNotifs(algorithmState));
-                } else {
-                    // TODO(b/333445519): shouldBeHidden should reflect whether the shade is closed
-                    //  already, so we shouldn't need to use ambientState here. However,
-                    //  currently it doesn't get updated quickly enough and can cause the footer to
-                    //  flash when closing the shade. As such, we temporarily also check the
-                    //  ambientState directly.
-                    if (((FooterView) view).shouldBeHidden() || !ambientState.isShadeExpanded()) {
-                        viewState.hidden = true;
-                    } else {
-                        final float footerEnd = algorithmState.mCurrentExpandedYPosition
-                                + view.getIntrinsicHeight();
-                        final boolean noSpaceForFooter =
-                                footerEnd > ambientState.getStackEndHeight();
-                        ((FooterView.FooterViewState) viewState).hideContent =
-                                noSpaceForFooter || (ambientState.isClearAllInProgress()
-                                        && !hasNonClearableNotifs(algorithmState));
-                    }
-                }
+            if (SceneContainerFlag.isEnabled()) {
+                final float footerEnd =
+                        stackTop + viewState.getYTranslation() + view.getIntrinsicHeight();
+                final boolean noSpaceForFooter = footerEnd > ambientState.getStackCutoff();
+                ((FooterView.FooterViewState) viewState).hideContent =
+                        noSpaceForFooter || (ambientState.isClearAllInProgress()
+                                && !hasNonClearableNotifs(algorithmState));
             } else {
-                final boolean shadeClosed = !ambientState.isShadeExpanded();
-                final boolean isShelfShowing = algorithmState.firstViewInShelf != null;
-                if (shadeClosed) {
+                // TODO(b/333445519): shouldBeHidden should reflect whether the shade is closed
+                //  already, so we shouldn't need to use ambientState here. However,
+                //  currently it doesn't get updated quickly enough and can cause the footer to
+                //  flash when closing the shade. As such, we temporarily also check the
+                //  ambientState directly.
+                if (((FooterView) view).shouldBeHidden() || !ambientState.isShadeExpanded()) {
                     viewState.hidden = true;
                 } else {
                     final float footerEnd = algorithmState.mCurrentExpandedYPosition
                             + view.getIntrinsicHeight();
-                    final boolean noSpaceForFooter = footerEnd > ambientState.getStackEndHeight();
+                    final boolean noSpaceForFooter =
+                            footerEnd > ambientState.getStackEndHeight();
                     ((FooterView.FooterViewState) viewState).hideContent =
-                            isShelfShowing || noSpaceForFooter
-                                    || (ambientState.isClearAllInProgress()
+                            noSpaceForFooter || (ambientState.isClearAllInProgress()
                                     && !hasNonClearableNotifs(algorithmState));
                 }
             }
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 5249a6d..d302fb6 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
@@ -106,9 +106,6 @@
     /** 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.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationStatsLoggerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationStatsLoggerImpl.kt
index 53749ff..c8c798d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationStatsLoggerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationStatsLoggerImpl.kt
@@ -16,9 +16,9 @@
 
 package com.android.systemui.statusbar.notification.stack.ui.view
 
-import android.os.Trace
 import android.service.notification.NotificationListenerService
 import androidx.annotation.VisibleForTesting
+import com.android.app.tracing.coroutines.TrackTracer
 import com.android.internal.statusbar.IStatusBarService
 import com.android.internal.statusbar.NotificationVisibility
 import com.android.systemui.dagger.SysUISingleton
@@ -183,8 +183,8 @@
 
             maybeLogVisibilityChanges(newlyVisible, noLongerVisible, activeNotifCount)
             updateExpansionStates(newlyVisible, noLongerVisible)
-            Trace.traceCounter(Trace.TRACE_TAG_APP, "Notifications [Active]", activeNotifCount)
-            Trace.traceCounter(Trace.TRACE_TAG_APP, "Notifications [Visible]", newVisibilities.size)
+            TrackTracer.instantForGroup("Notifications", "Active", activeNotifCount)
+            TrackTracer.instantForGroup("Notifications", "Visible", newVisibilities.size)
 
             lastLoggedVisibilities.clear()
             lastLoggedVisibilities.putAll(newVisibilities)
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 b456168..1d7e658 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
@@ -40,7 +40,6 @@
 import com.android.systemui.statusbar.notification.emptyshade.ui.view.EmptyShadeView
 import com.android.systemui.statusbar.notification.emptyshade.ui.viewbinder.EmptyShadeViewBinder
 import com.android.systemui.statusbar.notification.emptyshade.ui.viewmodel.EmptyShadeViewModel
-import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
 import com.android.systemui.statusbar.notification.footer.shared.NotifRedesignFooter
 import com.android.systemui.statusbar.notification.footer.ui.view.FooterView
 import com.android.systemui.statusbar.notification.footer.ui.viewbinder.FooterViewBinder
@@ -108,25 +107,20 @@
                 launch { bindShelf(shelf) }
                 bindHideList(viewController, viewModel, hiderTracker)
 
-                if (FooterViewRefactor.isEnabled) {
-                    val hasNonClearableSilentNotifications: StateFlow<Boolean> =
-                        viewModel.hasNonClearableSilentNotifications.stateIn(this)
-                    launch { reinflateAndBindFooter(view, hasNonClearableSilentNotifications) }
-                    launch {
-                        if (ModesEmptyShadeFix.isEnabled) {
-                            reinflateAndBindEmptyShade(view)
-                        } else {
-                            bindEmptyShadeLegacy(viewModel.emptyShadeViewFactory.create(), view)
-                        }
+                val hasNonClearableSilentNotifications: StateFlow<Boolean> =
+                    viewModel.hasNonClearableSilentNotifications.stateIn(this)
+                launch { reinflateAndBindFooter(view, hasNonClearableSilentNotifications) }
+                launch {
+                    if (ModesEmptyShadeFix.isEnabled) {
+                        reinflateAndBindEmptyShade(view)
+                    } else {
+                        bindEmptyShadeLegacy(viewModel.emptyShadeViewFactory.create(), view)
                     }
-                    launch {
-                        bindSilentHeaderClickListener(view, hasNonClearableSilentNotifications)
-                    }
-                    launch {
-                        viewModel.isImportantForAccessibility.collect { isImportantForAccessibility
-                            ->
-                            view.setImportantForAccessibilityYesNo(isImportantForAccessibility)
-                        }
+                }
+                launch { bindSilentHeaderClickListener(view, hasNonClearableSilentNotifications) }
+                launch {
+                    viewModel.isImportantForAccessibility.collect { isImportantForAccessibility ->
+                        view.setImportantForAccessibilityYesNo(isImportantForAccessibility)
                     }
                 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
index ea71460..3ea4d48 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/SharedNotificationContainerBinder.kt
@@ -28,7 +28,6 @@
 import com.android.systemui.keyguard.ui.viewmodel.ViewStateAccessor
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.scene.shared.flag.SceneContainerFlag
-import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
 import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator
 import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
@@ -81,9 +80,6 @@
 
                             controller.setOverExpansion(0f)
                             controller.setOverScrollAmount(0)
-                            if (!FooterViewRefactor.isEnabled) {
-                                controller.updateFooter()
-                            }
                         }
                     }
                 }
@@ -183,6 +179,10 @@
                         }
                     }
 
+                    if (Flags.bouncerUiRevamp()) {
+                        launch { viewModel.blurRadius.collect { controller.setBlurRadius(it) } }
+                    }
+
                     if (communalSettingsInteractor.isCommunalFlagEnabled()) {
                         launch {
                             viewModel.glanceableHubAlpha.collect {
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 38390e7..fcc671a 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
@@ -25,7 +25,6 @@
 import com.android.systemui.statusbar.notification.domain.interactor.HeadsUpNotificationInteractor
 import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyShadeFix
 import com.android.systemui.statusbar.notification.emptyshade.ui.viewmodel.EmptyShadeViewModel
-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.shelf.ui.viewmodel.NotificationShelfViewModel
@@ -75,46 +74,37 @@
      * we want it to be important for accessibility to enable accessibility auto-scrolling in NSSL.
      * See b/242235264 for more details.
      */
-    val isImportantForAccessibility: Flow<Boolean> by lazy {
-        if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) {
-            flowOf(true)
-        } else {
-            combine(
-                    activeNotificationsInteractor.areAnyNotificationsPresent,
-                    notificationStackInteractor.isShowingOnLockscreen,
-                ) { hasNotifications, isShowingOnLockscreen ->
-                    hasNotifications || !isShowingOnLockscreen
-                }
-                .distinctUntilChanged()
-                .dumpWhileCollecting("isImportantForAccessibility")
-                .flowOn(bgDispatcher)
-        }
-    }
+    val isImportantForAccessibility: Flow<Boolean> =
+        combine(
+                activeNotificationsInteractor.areAnyNotificationsPresent,
+                notificationStackInteractor.isShowingOnLockscreen,
+            ) { hasNotifications, isShowingOnLockscreen ->
+                hasNotifications || !isShowingOnLockscreen
+            }
+            .distinctUntilChanged()
+            .dumpWhileCollecting("isImportantForAccessibility")
+            .flowOn(bgDispatcher)
 
     val shouldShowEmptyShadeView: Flow<Boolean> by lazy {
         ModesEmptyShadeFix.assertInLegacyMode()
-        if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) {
-            flowOf(false)
-        } else {
-            combine(
-                    activeNotificationsInteractor.areAnyNotificationsPresent,
-                    shadeInteractor.isQsFullscreen,
-                    notificationStackInteractor.isShowingOnLockscreen,
-                ) { hasNotifications, isQsFullScreen, isShowingOnLockscreen ->
-                    when {
-                        hasNotifications -> false
-                        isQsFullScreen -> false
-                        // Do not show the empty shade if the lockscreen is visible (including AOD
-                        // b/228790482 and bouncer b/267060171), except if the shade is opened on
-                        // top.
-                        isShowingOnLockscreen -> false
-                        else -> true
-                    }
+        combine(
+                activeNotificationsInteractor.areAnyNotificationsPresent,
+                shadeInteractor.isQsFullscreen,
+                notificationStackInteractor.isShowingOnLockscreen,
+            ) { hasNotifications, isQsFullScreen, isShowingOnLockscreen ->
+                when {
+                    hasNotifications -> false
+                    isQsFullScreen -> false
+                    // Do not show the empty shade if the lockscreen is visible (including AOD
+                    // b/228790482 and bouncer b/267060171), except if the shade is opened on
+                    // top.
+                    isShowingOnLockscreen -> false
+                    else -> true
                 }
-                .distinctUntilChanged()
-                .dumpWhileCollecting("shouldShowEmptyShadeView")
-                .flowOn(bgDispatcher)
-        }
+            }
+            .distinctUntilChanged()
+            .dumpWhileCollecting("shouldShowEmptyShadeView")
+            .flowOn(bgDispatcher)
     }
 
     val shouldShowEmptyShadeViewAnimated: Flow<AnimatedValue<Boolean>> by lazy {
@@ -164,18 +154,14 @@
      */
     val shouldHideFooterView: Flow<Boolean> by lazy {
         SceneContainerFlag.assertInLegacyMode()
-        if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) {
-            flowOf(false)
-        } else {
-            // When the shade is closed, the footer is still present in the list, but not visible.
-            // This prevents the footer from being shown when a HUN is present, while still allowing
-            // the footer to be counted as part of the shade for measurements.
-            shadeInteractor.shadeExpansion
-                .map { it == 0f }
-                .distinctUntilChanged()
-                .dumpWhileCollecting("shouldHideFooterView")
-                .flowOn(bgDispatcher)
-        }
+        // When the shade is closed, the footer is still present in the list, but not visible.
+        // This prevents the footer from being shown when a HUN is present, while still allowing
+        // the footer to be counted as part of the shade for measurements.
+        shadeInteractor.shadeExpansion
+            .map { it == 0f }
+            .distinctUntilChanged()
+            .dumpWhileCollecting("shouldHideFooterView")
+            .flowOn(bgDispatcher)
     }
 
     /**
@@ -188,68 +174,64 @@
      */
     val shouldIncludeFooterView: Flow<AnimatedValue<Boolean>> by lazy {
         SceneContainerFlag.assertInLegacyMode()
-        if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) {
-            flowOf(AnimatedValue.NotAnimating(false))
-        } else {
-            combine(
-                    activeNotificationsInteractor.areAnyNotificationsPresent,
-                    userSetupInteractor.isUserSetUp,
-                    notificationStackInteractor.isShowingOnLockscreen,
-                    shadeInteractor.isQsFullscreen,
-                    remoteInputInteractor.isRemoteInputActive,
-                ) {
-                    hasNotifications,
-                    isUserSetUp,
-                    isShowingOnLockscreen,
-                    qsFullScreen,
-                    isRemoteInputActive ->
-                    when {
-                        !hasNotifications -> VisibilityChange.DISAPPEAR_WITH_ANIMATION
-                        // Hide the footer until the user setup is complete, to prevent access
-                        // to settings (b/193149550).
-                        !isUserSetUp -> VisibilityChange.DISAPPEAR_WITH_ANIMATION
-                        // Do not show the footer if the lockscreen is visible (incl. AOD),
-                        // except if the shade is opened on top. See also b/219680200.
-                        // Do not animate, as that makes the footer appear briefly when
-                        // transitioning between the shade and keyguard.
-                        isShowingOnLockscreen -> VisibilityChange.DISAPPEAR_WITHOUT_ANIMATION
-                        // Do not show the footer if quick settings are fully expanded (except
-                        // for the foldable split shade view). See b/201427195 && b/222699879.
-                        qsFullScreen -> VisibilityChange.DISAPPEAR_WITH_ANIMATION
-                        // Hide the footer if remote input is active (i.e. user is replying to a
-                        // notification). See b/75984847.
-                        isRemoteInputActive -> VisibilityChange.DISAPPEAR_WITH_ANIMATION
-                        else -> VisibilityChange.APPEAR_WITH_ANIMATION
-                    }
+        combine(
+                activeNotificationsInteractor.areAnyNotificationsPresent,
+                userSetupInteractor.isUserSetUp,
+                notificationStackInteractor.isShowingOnLockscreen,
+                shadeInteractor.isQsFullscreen,
+                remoteInputInteractor.isRemoteInputActive,
+            ) {
+                hasNotifications,
+                isUserSetUp,
+                isShowingOnLockscreen,
+                qsFullScreen,
+                isRemoteInputActive ->
+                when {
+                    !hasNotifications -> VisibilityChange.DISAPPEAR_WITH_ANIMATION
+                    // Hide the footer until the user setup is complete, to prevent access
+                    // to settings (b/193149550).
+                    !isUserSetUp -> VisibilityChange.DISAPPEAR_WITH_ANIMATION
+                    // Do not show the footer if the lockscreen is visible (incl. AOD),
+                    // except if the shade is opened on top. See also b/219680200.
+                    // Do not animate, as that makes the footer appear briefly when
+                    // transitioning between the shade and keyguard.
+                    isShowingOnLockscreen -> VisibilityChange.DISAPPEAR_WITHOUT_ANIMATION
+                    // Do not show the footer if quick settings are fully expanded (except
+                    // for the foldable split shade view). See b/201427195 && b/222699879.
+                    qsFullScreen -> VisibilityChange.DISAPPEAR_WITH_ANIMATION
+                    // Hide the footer if remote input is active (i.e. user is replying to a
+                    // notification). See b/75984847.
+                    isRemoteInputActive -> VisibilityChange.DISAPPEAR_WITH_ANIMATION
+                    else -> VisibilityChange.APPEAR_WITH_ANIMATION
                 }
-                .distinctUntilChanged(
-                    // Equivalent unless visibility changes
-                    areEquivalent = { a: VisibilityChange, b: VisibilityChange ->
-                        a.visible == b.visible
-                    }
-                )
-                // Should we animate the visibility change?
-                .sample(
-                    // TODO(b/322167853): This check is currently duplicated in FooterViewModel,
-                    //  but instead it should be a field in ShadeAnimationInteractor.
-                    combine(
-                            shadeInteractor.isShadeFullyExpanded,
-                            shadeInteractor.isShadeTouchable,
-                            ::Pair,
-                        )
-                        .onStart { emit(Pair(false, false)) }
-                ) { visibilityChange, (isShadeFullyExpanded, animationsEnabled) ->
-                    // Animate if the shade is interactive, but NOT on the lockscreen. Having
-                    // animations enabled while on the lockscreen makes the footer appear briefly
-                    // when transitioning between the shade and keyguard.
-                    val shouldAnimate =
-                        isShadeFullyExpanded && animationsEnabled && visibilityChange.canAnimate
-                    AnimatableEvent(visibilityChange.visible, shouldAnimate)
+            }
+            .distinctUntilChanged(
+                // Equivalent unless visibility changes
+                areEquivalent = { a: VisibilityChange, b: VisibilityChange ->
+                    a.visible == b.visible
                 }
-                .toAnimatedValueFlow()
-                .dumpWhileCollecting("shouldIncludeFooterView")
-                .flowOn(bgDispatcher)
-        }
+            )
+            // Should we animate the visibility change?
+            .sample(
+                // TODO(b/322167853): This check is currently duplicated in FooterViewModel,
+                //  but instead it should be a field in ShadeAnimationInteractor.
+                combine(
+                        shadeInteractor.isShadeFullyExpanded,
+                        shadeInteractor.isShadeTouchable,
+                        ::Pair,
+                    )
+                    .onStart { emit(Pair(false, false)) }
+            ) { visibilityChange, (isShadeFullyExpanded, animationsEnabled) ->
+                // Animate if the shade is interactive, but NOT on the lockscreen. Having
+                // animations enabled while on the lockscreen makes the footer appear briefly
+                // when transitioning between the shade and keyguard.
+                val shouldAnimate =
+                    isShadeFullyExpanded && animationsEnabled && visibilityChange.canAnimate
+                AnimatableEvent(visibilityChange.visible, shouldAnimate)
+            }
+            .toAnimatedValueFlow()
+            .dumpWhileCollecting("shouldIncludeFooterView")
+            .flowOn(bgDispatcher)
     }
 
     // This flow replaces shouldHideFooterView+shouldIncludeFooterView in flexiglass.
@@ -328,25 +310,15 @@
         APPEAR_WITH_ANIMATION(visible = true, canAnimate = true),
     }
 
-    val hasClearableAlertingNotifications: Flow<Boolean> by lazy {
-        if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) {
-            flowOf(false)
-        } else {
-            activeNotificationsInteractor.hasClearableAlertingNotifications.dumpWhileCollecting(
-                "hasClearableAlertingNotifications"
-            )
-        }
-    }
+    val hasClearableAlertingNotifications: Flow<Boolean> =
+        activeNotificationsInteractor.hasClearableAlertingNotifications.dumpWhileCollecting(
+            "hasClearableAlertingNotifications"
+        )
 
-    val hasNonClearableSilentNotifications: Flow<Boolean> by lazy {
-        if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) {
-            flowOf(false)
-        } else {
-            activeNotificationsInteractor.hasNonClearableSilentNotifications.dumpWhileCollecting(
-                "hasNonClearableSilentNotifications"
-            )
-        }
-    }
+    val hasNonClearableSilentNotifications: Flow<Boolean> =
+        activeNotificationsInteractor.hasNonClearableSilentNotifications.dumpWhileCollecting(
+            "hasNonClearableSilentNotifications"
+        )
 
     val topHeadsUpRow: Flow<HeadsUpRowKey?> by lazy {
         if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) {
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 fc8c70f..f0455fc 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
@@ -42,6 +42,7 @@
 import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
 import com.android.systemui.keyguard.shared.model.StatusBarState.SHADE
 import com.android.systemui.keyguard.shared.model.StatusBarState.SHADE_LOCKED
+import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition
 import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToGoneTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerToPrimaryBouncerTransitionViewModel
 import com.android.systemui.keyguard.ui.viewmodel.AodBurnInViewModel
@@ -154,6 +155,7 @@
     private val primaryBouncerToGoneTransitionViewModel: PrimaryBouncerToGoneTransitionViewModel,
     private val primaryBouncerToLockscreenTransitionViewModel:
         PrimaryBouncerToLockscreenTransitionViewModel,
+    private val primaryBouncerTransitions: Set<@JvmSuppressWildcards PrimaryBouncerTransition>,
     aodBurnInViewModel: AodBurnInViewModel,
     private val communalSceneInteractor: CommunalSceneInteractor,
     // Lazy because it's only used in the SceneContainer + Dual Shade configuration.
@@ -562,7 +564,7 @@
             lockscreenToDreamingTransitionViewModel.lockscreenAlpha,
             lockscreenToGoneTransitionViewModel.notificationAlpha(viewState),
             lockscreenToOccludedTransitionViewModel.lockscreenAlpha,
-            lockscreenToPrimaryBouncerTransitionViewModel.lockscreenAlpha,
+            lockscreenToPrimaryBouncerTransitionViewModel.notificationAlpha,
             alternateBouncerToPrimaryBouncerTransitionViewModel.notificationAlpha,
             occludedToAodTransitionViewModel.lockscreenAlpha,
             occludedToGoneTransitionViewModel.notificationAlpha(viewState),
@@ -626,6 +628,12 @@
             .dumpWhileCollecting("keyguardAlpha")
     }
 
+    val blurRadius =
+        primaryBouncerTransitions
+            .map { transition -> transition.notificationBlurRadius }
+            .merge()
+            .dumpWhileCollecting("blurRadius")
+
     /**
      * Returns a flow of the expected alpha while running a LOCKSCREEN<->GLANCEABLE_HUB or
      * DREAMING<->GLANCEABLE_HUB transition or idle on the hub.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideControllerStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideControllerStore.kt
index 744f969..2ae38dd4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideControllerStore.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/AutoHideControllerStore.kt
@@ -47,9 +47,9 @@
         StatusBarConnectedDisplays.assertInNewMode()
     }
 
-    override fun createInstanceForDisplay(displayId: Int): AutoHideController {
+    override fun createInstanceForDisplay(displayId: Int): AutoHideController? {
         val displayWindowProperties =
-            displayWindowPropertiesRepository.get(displayId, TYPE_STATUS_BAR)
+            displayWindowPropertiesRepository.get(displayId, TYPE_STATUS_BAR) ?: return null
         return autoHideControllerFactory.create(displayWindowProperties.context)
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
index ae31151..720a755 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
@@ -63,7 +63,7 @@
     boolean SPEW = false;
     boolean DEBUG_GESTURES = false;
     boolean DEBUG_MEDIA_FAKE_ARTWORK = false;
-    boolean DEBUG_CAMERA_LIFT = false;
+    boolean DEBUG_POWER_BUTTON_GESTURE = false;
     boolean DEBUG_WINDOW_STATE = false;
     boolean DEBUG_WAKEUP_DELAY = Compile.IS_DEBUG;
     boolean SHOW_LOCKSCREEN_MEDIA_ARTWORK = true;
@@ -312,6 +312,15 @@
     void setLaunchCameraOnFinishedGoingToSleep(boolean launch);
 
     void setLaunchCameraOnFinishedWaking(boolean launch);
+    /**
+     * Notifies SysUI to launch wallet when device finishes sleeping.
+     */
+    void setLaunchWalletOnFinishedGoingToSleep(boolean launch);
+
+    /**
+     * Notifies SysUI to launch wallet when device finishes waking.
+     */
+    void setLaunchWalletOnFinishedWaking(boolean launch);
 
     void setLaunchEmergencyActionOnFinishedGoingToSleep(boolean launch);
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
index adfcb71..1c19dbe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
@@ -16,6 +16,9 @@
 
 package com.android.systemui.statusbar.phone;
 
+import static android.service.quickaccesswallet.Flags.launchWalletOptionOnPowerDoubleTap;
+import static android.service.quickaccesswallet.Flags.launchWalletViaSysuiCallbacks;
+
 import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
 import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_WAKING;
 
@@ -70,6 +73,7 @@
 import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 import com.android.systemui.statusbar.policy.RemoteInputQuickSettingsDisabler;
+import com.android.systemui.wallet.controller.QuickAccessWalletController;
 
 import dagger.Lazy;
 
@@ -119,6 +123,11 @@
     private int mDisabled2;
 
     private final EmergencyGestureIntentFactory mEmergencyGestureIntentFactory;
+    private QuickAccessWalletController mWalletController;
+
+     enum PowerButtonLaunchGestureTarget {
+        LAUNCH_CAMERA_ON_GESTURE, LAUNCH_WALLET_ON_GESTURE
+    }
 
     @Inject
     CentralSurfacesCommandQueueCallbacks(
@@ -152,7 +161,8 @@
             QSHost qsHost,
             ActivityStarter activityStarter,
             KeyguardInteractor keyguardInteractor,
-            EmergencyGestureIntentFactory emergencyGestureIntentFactory) {
+            EmergencyGestureIntentFactory emergencyGestureIntentFactory,
+            QuickAccessWalletController walletController) {
         mCentralSurfaces = centralSurfaces;
         mQsController = quickSettingsController;
         mContext = context;
@@ -186,6 +196,7 @@
                 mVibratorOptional, resources);
         mActivityStarter = activityStarter;
         mEmergencyGestureIntentFactory = emergencyGestureIntentFactory;
+        mWalletController = walletController;
     }
 
     @Override
@@ -346,9 +357,15 @@
 
     @Override
     public void onCameraLaunchGestureDetected(int source) {
+        if (launchWalletOptionOnPowerDoubleTap() && launchWalletViaSysuiCallbacks()) {
+            onPowerButtonLaunchGestureTriggered(
+                    PowerButtonLaunchGestureTarget.LAUNCH_CAMERA_ON_GESTURE, source);
+            return;
+        }
+
         mCentralSurfaces.setLastCameraLaunchSource(source);
         if (mCentralSurfaces.isGoingToSleep()) {
-            if (CentralSurfaces.DEBUG_CAMERA_LIFT) {
+            if (CentralSurfaces.DEBUG_POWER_BUTTON_GESTURE) {
                 Slog.d(CentralSurfaces.TAG, "Finish going to sleep before launching camera");
             }
             mCentralSurfaces.setLaunchCameraOnFinishedGoingToSleep(true);
@@ -356,7 +373,7 @@
         }
         if (!mCameraLauncherLazy.get().canCameraGestureBeLaunched(
                 mPanelExpansionInteractor.getBarState())) {
-            if (CentralSurfaces.DEBUG_CAMERA_LIFT) {
+            if (CentralSurfaces.DEBUG_POWER_BUTTON_GESTURE) {
                 Slog.d(CentralSurfaces.TAG, "Can't launch camera right now");
             }
             return;
@@ -389,7 +406,7 @@
                         CentralSurfaces.LAUNCH_TRANSITION_TIMEOUT_MS + 1000L);
             }
             if (isWakingUpOrAwake()) {
-                if (CentralSurfaces.DEBUG_CAMERA_LIFT) {
+                if (CentralSurfaces.DEBUG_POWER_BUTTON_GESTURE) {
                     Slog.d(CentralSurfaces.TAG, "Launching camera");
                 }
                 if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
@@ -404,7 +421,7 @@
                 // we will dismiss us too early since we are waiting on an activity to be drawn and
                 // incorrectly get notified because of the screen on event (which resumes and pauses
                 // some activities)
-                if (CentralSurfaces.DEBUG_CAMERA_LIFT) {
+                if (CentralSurfaces.DEBUG_POWER_BUTTON_GESTURE) {
                     Slog.d(CentralSurfaces.TAG, "Deferring until screen turns on");
                 }
                 mCentralSurfaces.setLaunchCameraOnFinishedWaking(true);
@@ -413,6 +430,13 @@
     }
 
     @Override
+    public void onWalletLaunchGestureDetected() {
+        onPowerButtonLaunchGestureTriggered(
+                PowerButtonLaunchGestureTarget.LAUNCH_WALLET_ON_GESTURE,
+                /* cameraLaunchSource=*/ -1);
+    }
+
+    @Override
     public void onEmergencyActionLaunchGestureDetected() {
         Intent emergencyIntent = mEmergencyGestureIntentFactory.invoke(
                 EmergencyGesture.ACTION_LAUNCH_EMERGENCY);
@@ -577,4 +601,152 @@
                 HapticFeedbackConstants.GESTURE_START
         );
     }
+
+    private void onPowerButtonLaunchGestureTriggered(PowerButtonLaunchGestureTarget target,
+            int cameraLaunchSource) {
+        if (!launchWalletOptionOnPowerDoubleTap() || !launchWalletViaSysuiCallbacks()) {
+            return;
+        }
+
+        if (target == PowerButtonLaunchGestureTarget.LAUNCH_CAMERA_ON_GESTURE) {
+            mCentralSurfaces.setLastCameraLaunchSource(cameraLaunchSource);
+        }
+
+        if (mCentralSurfaces.isGoingToSleep()) {
+            setLaunchAppOnFinishedGoingToSleep(target);
+            return;
+        }
+
+        if (!canAppBeLaunched(PowerButtonLaunchGestureTarget.LAUNCH_CAMERA_ON_GESTURE)) {
+            if (CentralSurfaces.DEBUG_POWER_BUTTON_GESTURE) {
+                Slog.d(CentralSurfaces.TAG, "Can't launch app via power button gesture right "
+                        + "now");
+            }
+            return;
+        }
+
+        if (target == PowerButtonLaunchGestureTarget.LAUNCH_CAMERA_ON_GESTURE) {
+            mKeyguardInteractor.onCameraLaunchDetected(cameraLaunchSource);
+        }
+
+        wakeUpFromAppLaunch(target);
+        vibrateForCameraGesture();
+
+        if (target == PowerButtonLaunchGestureTarget.LAUNCH_CAMERA_ON_GESTURE &&
+                cameraLaunchSource == StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP) {
+            Slog.v(CentralSurfaces.TAG, "Camera launch");
+            mKeyguardUpdateMonitor.onCameraLaunched();
+        }
+
+        if (!mKeyguardStateController.isShowing()) {
+            switch (target) {
+                case LAUNCH_CAMERA_ON_GESTURE:
+                    startInsecureCameraIntent(cameraLaunchSource);
+                    break;
+                case LAUNCH_WALLET_ON_GESTURE:
+                    mWalletController.startGestureUiIntent(mActivityStarter,
+                            /* animationController=*/ null);
+                    break;
+            }
+        } else {
+            if (!mCentralSurfaces.isDeviceInteractive()) {
+                // Avoid flickering of the scrim when we instant launch and the bouncer
+                // comes on.
+                mCentralSurfaces.acquireGestureWakeLock(
+                        CentralSurfaces.LAUNCH_TRANSITION_TIMEOUT_MS + 1000L);
+            }
+            if (isWakingUpOrAwake()) {
+                if (CentralSurfaces.DEBUG_POWER_BUTTON_GESTURE) {
+                    Slog.d(CentralSurfaces.TAG, "Launching app via double power button "
+                            + "gesture");
+                }
+                if (mStatusBarKeyguardViewManager.isBouncerShowing()) {
+                    mStatusBarKeyguardViewManager.reset(true /* hide */);
+                }
+                mCentralSurfaces.startLaunchTransitionTimeout();
+                switch (target) {
+                    case LAUNCH_CAMERA_ON_GESTURE:
+                        mCameraLauncherLazy.get().launchCamera(cameraLaunchSource,
+                                mPanelExpansionInteractor.isFullyCollapsed());
+                        break;
+                    case LAUNCH_WALLET_ON_GESTURE:
+                        mWalletController.startGestureUiIntent(mActivityStarter,
+                                /* animationController=*/ null);
+                        break;
+                }
+                mCentralSurfaces.updateScrimController();
+            } else {
+                // We need to defer the app launch until the screen comes on, since otherwise
+                // we will dismiss us too early since we are waiting on an activity to be drawn and
+                // incorrectly get notified because of the screen on event (which resumes and pauses
+                // some activities)
+                if (CentralSurfaces.DEBUG_POWER_BUTTON_GESTURE) {
+                    Slog.d(CentralSurfaces.TAG, "Deferring until screen turns on");
+                }
+                setLaunchAppOnFinishedWaking(target);
+            }
+        }
+    }
+
+    private void setLaunchAppOnFinishedGoingToSleep(PowerButtonLaunchGestureTarget target) {
+        if (CentralSurfaces.DEBUG_POWER_BUTTON_GESTURE) {
+            Slog.d(CentralSurfaces.TAG,
+                    "Finish going to sleep before LAUNCHING");
+        }
+        switch (target) {
+            case LAUNCH_CAMERA_ON_GESTURE:
+                Slog.d(CentralSurfaces.TAG, "setLaunchCameraOnFinishedGoingToSleep");
+                mCentralSurfaces.setLaunchCameraOnFinishedGoingToSleep(true);
+                break;
+            case LAUNCH_WALLET_ON_GESTURE:
+                Slog.d(CentralSurfaces.TAG, "setLaunchWalletOnFinishedGoingToSleep");
+                mCentralSurfaces.setLaunchWalletOnFinishedGoingToSleep(true);
+                break;
+
+        }
+    }
+
+    private boolean canAppBeLaunched(PowerButtonLaunchGestureTarget target) {
+        if (target == PowerButtonLaunchGestureTarget.LAUNCH_CAMERA_ON_GESTURE &&
+                !mCameraLauncherLazy.get().canCameraGestureBeLaunched(
+                        mPanelExpansionInteractor.getBarState())) {
+            if (CentralSurfaces.DEBUG_POWER_BUTTON_GESTURE) {
+                Slog.d(CentralSurfaces.TAG, "Can't launch camera right now");
+            }
+            return false;
+        }
+        return true;
+    }
+
+    private void startInsecureCameraIntent(int source) {
+        final Intent cameraIntent = CameraIntents.getInsecureCameraIntent(mContext,
+                mUserTracker.getUserId());
+        cameraIntent.putExtra(CameraIntents.EXTRA_LAUNCH_SOURCE, source);
+        mActivityStarter.startActivityDismissingKeyguard(cameraIntent,
+                /* onlyProvisioned=*/ false, /* dismissShade=*/ true,
+                /* disallowEnterPictureInPictureWhileLaunching=*/ true,
+                /* callback=*/ null, /*flags=*/ 0,/* animationController=*/ null,
+                mUserTracker.getUserHandle());
+    }
+
+    private void setLaunchAppOnFinishedWaking(PowerButtonLaunchGestureTarget target) {
+        switch (target) {
+            case LAUNCH_CAMERA_ON_GESTURE:
+                mCentralSurfaces.setLaunchCameraOnFinishedWaking(true);
+                break;
+            case LAUNCH_WALLET_ON_GESTURE:
+                mCentralSurfaces.setLaunchWalletOnFinishedWaking(true);
+                break;
+        }
+    }
+
+    private void wakeUpFromAppLaunch(PowerButtonLaunchGestureTarget appLaunch) {
+        if (!mCentralSurfaces.isDeviceInteractive()) {
+            int reason = appLaunch == PowerButtonLaunchGestureTarget.LAUNCH_CAMERA_ON_GESTURE ?
+                    PowerManager.WAKE_REASON_CAMERA_LAUNCH : PowerManager.WAKE_REASON_GESTURE;
+            String details = appLaunch ==  PowerButtonLaunchGestureTarget.LAUNCH_CAMERA_ON_GESTURE ?
+                    "com.android.systemui:CAMERA_GESTURE" : "com.android.systemui:WALLET_GESTURE";
+            mPowerManager.wakeUp(SystemClock.uptimeMillis(), reason, details);
+        }
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt
index d4f2a93..7241c80 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesEmptyImpl.kt
@@ -142,6 +142,10 @@
 
     override fun setLaunchCameraOnFinishedWaking(launch: Boolean) {}
 
+    override fun setLaunchWalletOnFinishedGoingToSleep(launch: Boolean) {}
+
+    override fun setLaunchWalletOnFinishedWaking(launch: Boolean) {}
+
     override fun setLaunchEmergencyActionOnFinishedGoingToSleep(launch: Boolean) {}
 
     override fun setLaunchEmergencyActionOnFinishedWaking(launch: Boolean) {}
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 2bc417e..b146b92 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -27,6 +27,9 @@
 
 import static androidx.lifecycle.Lifecycle.State.RESUMED;
 
+import static android.service.quickaccesswallet.Flags.launchWalletOptionOnPowerDoubleTap;
+import static android.service.quickaccesswallet.Flags.launchWalletViaSysuiCallbacks;
+
 import static com.android.systemui.Dependency.TIME_TICK_HANDLER_NAME;
 import static com.android.systemui.Flags.keyboardShortcutHelperRewrite;
 import static com.android.systemui.Flags.lightRevealMigration;
@@ -35,7 +38,6 @@
 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.StatusBarState.SHADE;
-
 import android.annotation.Nullable;
 import android.app.ActivityOptions;
 import android.app.IWallpaperManager;
@@ -230,6 +232,7 @@
 import com.android.systemui.util.concurrency.MessageRouter;
 import com.android.systemui.util.kotlin.JavaAdapter;
 import com.android.systemui.volume.VolumeComponent;
+import com.android.systemui.wallet.controller.QuickAccessWalletController;
 import com.android.wm.shell.bubbles.Bubbles;
 import com.android.wm.shell.startingsurface.SplashscreenContentDrawer;
 import com.android.wm.shell.startingsurface.StartingSurface;
@@ -329,6 +332,20 @@
     }
 
     @Override
+    public void setLaunchWalletOnFinishedGoingToSleep(boolean launch) {
+        if (launchWalletOptionOnPowerDoubleTap() && launchWalletViaSysuiCallbacks()) {
+            mLaunchWalletOnFinishedGoingToSleep = launch;
+        }
+    }
+
+    @Override
+    public void setLaunchWalletOnFinishedWaking(boolean launch) {
+        if (launchWalletOptionOnPowerDoubleTap() && launchWalletViaSysuiCallbacks()) {
+            mLaunchWalletWhenFinishedWaking = launch;
+        }
+    }
+
+    @Override
     public void setLaunchEmergencyActionOnFinishedGoingToSleep(boolean launch) {
         mLaunchEmergencyActionOnFinishedGoingToSleep = launch;
     }
@@ -512,6 +529,8 @@
     private Runnable mLaunchTransitionCancelRunnable;
     private boolean mLaunchCameraWhenFinishedWaking;
     private boolean mLaunchCameraOnFinishedGoingToSleep;
+    private boolean mLaunchWalletWhenFinishedWaking;
+    private boolean mLaunchWalletOnFinishedGoingToSleep;
     private boolean mLaunchEmergencyActionWhenFinishedWaking;
     private boolean mLaunchEmergencyActionOnFinishedGoingToSleep;
     private int mLastCameraLaunchSource;
@@ -577,6 +596,7 @@
     private final EmergencyGestureIntentFactory mEmergencyGestureIntentFactory;
 
     private final ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
+    private final QuickAccessWalletController mWalletController;
 
     /**
      * Public constructor for CentralSurfaces.
@@ -691,7 +711,8 @@
             BrightnessMirrorShowingInteractor brightnessMirrorShowingInteractor,
             GlanceableHubContainerController glanceableHubContainerController,
             EmergencyGestureIntentFactory emergencyGestureIntentFactory,
-            ViewCaptureAwareWindowManager viewCaptureAwareWindowManager
+            ViewCaptureAwareWindowManager viewCaptureAwareWindowManager,
+            QuickAccessWalletController walletController
     ) {
         mContext = context;
         mNotificationsController = notificationsController;
@@ -790,6 +811,7 @@
             mGlanceableHubContainerController = null;
         }
         mEmergencyGestureIntentFactory = emergencyGestureIntentFactory;
+        mWalletController = walletController;
 
         mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
         mStartingSurfaceOptional = startingSurfaceOptional;
@@ -829,10 +851,6 @@
         mLightRevealScrim = lightRevealScrim;
 
         mViewCaptureAwareWindowManager = viewCaptureAwareWindowManager;
-
-        if (PredictiveBackSysUiFlag.isEnabled()) {
-            mContext.getApplicationInfo().setEnableOnBackInvokedCallback(true);
-        }
     }
 
     private void initBubbles(Bubbles bubbles) {
@@ -1469,14 +1487,11 @@
         mActivityTransitionAnimator.setCallback(mActivityTransitionAnimatorCallback);
         mActivityTransitionAnimator.addListener(mActivityTransitionAnimatorListener);
         mRemoteInputManager.addControllerCallback(mNotificationShadeWindowController);
-        mStackScrollerController.setNotificationActivityStarter(
-                mNotificationActivityStarterLazy.get());
         mGutsManager.setNotificationActivityStarter(mNotificationActivityStarterLazy.get());
         mShadeController.setNotificationPresenter(mPresenterLazy.get());
         mNotificationsController.initialize(
                 mPresenterLazy.get(),
                 mNotifListContainer,
-                mStackScrollerController.getNotifStackController(),
                 mNotificationActivityStarterLazy.get());
         mWindowRootViewVisibilityInteractor.setUp(mPresenterLazy.get(), mNotificationsController);
     }
@@ -2509,6 +2524,7 @@
             mCameraLauncherLazy.get().setLaunchingAffordance(false);
             releaseGestureWakeLock();
             mLaunchCameraWhenFinishedWaking = false;
+            mLaunchWalletWhenFinishedWaking = false;
             mDeviceInteractive = false;
 
             updateNotificationPanelTouchState();
@@ -2522,6 +2538,15 @@
                         mLastCameraLaunchSource));
             }
 
+            if (mLaunchWalletOnFinishedGoingToSleep && launchWalletOptionOnPowerDoubleTap()
+                    && launchWalletViaSysuiCallbacks()) {
+                mLaunchWalletOnFinishedGoingToSleep = false;
+
+                // This gets executed before we will show Keyguard, so post it in order that the
+                // state is correct.
+                mMainExecutor.execute(() -> mCommandQueueCallbacks.onWalletLaunchGestureDetected());
+            }
+
             if (mLaunchEmergencyActionOnFinishedGoingToSleep) {
                 mLaunchEmergencyActionOnFinishedGoingToSleep = false;
 
@@ -2627,6 +2652,12 @@
                         mShadeSurface.isFullyCollapsed());
                 mLaunchCameraWhenFinishedWaking = false;
             }
+            if (mLaunchWalletWhenFinishedWaking && launchWalletOptionOnPowerDoubleTap()
+                && launchWalletViaSysuiCallbacks()) {
+                mLaunchWalletWhenFinishedWaking = false;
+                mWalletController.startGestureUiIntent(mActivityStarter,
+                        /*animationController=*/ null);
+            }
             if (mLaunchEmergencyActionWhenFinishedWaking) {
                 mLaunchEmergencyActionWhenFinishedWaking = false;
                 Intent emergencyIntent = mEmergencyGestureIntentFactory.invoke(
@@ -2993,9 +3024,6 @@
         public void onConfigChanged(Configuration newConfig) {
             updateResources();
             updateDisplaySize(); // populates mDisplayMetrics
-            if (PredictiveBackSysUiFlag.isEnabled()) {
-                mContext.getApplicationInfo().setEnableOnBackInvokedCallback(true);
-            }
 
             if (DEBUG) {
                 Log.v(TAG, "configuration changed: " + mContext.getResources().getConfiguration());
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 6a77988..a339bc9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -47,6 +47,7 @@
 import com.android.systemui.battery.BatteryMeterView;
 import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
 import com.android.systemui.res.R;
+import com.android.systemui.statusbar.layout.StatusBarContentInsetsProvider;
 import com.android.systemui.statusbar.phone.SysuiDarkIconDispatcher.DarkChange;
 import com.android.systemui.statusbar.phone.ui.TintedIconManager;
 import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
index 2433b78..4c2bfe5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
@@ -64,6 +64,7 @@
 import com.android.systemui.statusbar.disableflags.DisableStateTracker;
 import com.android.systemui.statusbar.events.SystemStatusAnimationCallback;
 import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
+import com.android.systemui.statusbar.layout.StatusBarContentInsetsProvider;
 import com.android.systemui.statusbar.notification.AnimatableProperty;
 import com.android.systemui.statusbar.notification.PropertyAnimator;
 import com.android.systemui.statusbar.notification.stack.AnimationProperties;
@@ -528,6 +529,7 @@
             return;
         }
         mDozing = dozing;
+        updateViewState();
     }
 
     /** Animate the keyguard status bar in. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LetterboxModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LetterboxModule.kt
index 2e3f0d0..1e6a0f8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LetterboxModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LetterboxModule.kt
@@ -18,6 +18,7 @@
 package com.android.systemui.statusbar.phone
 
 import com.android.systemui.CoreStartable
+import com.android.systemui.statusbar.layout.LetterboxBackgroundProvider
 import dagger.Binds
 import dagger.Module
 import dagger.multibindings.ClassKey
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarControllerImpl.java
index ea67f1c..0a28551 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LightBarControllerImpl.java
@@ -44,6 +44,7 @@
 import com.android.systemui.statusbar.data.repository.DarkIconDispatcherStore;
 import com.android.systemui.statusbar.data.repository.StatusBarModePerDisplayRepository;
 import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore;
+import com.android.systemui.statusbar.layout.BoundsPair;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.util.Compile;
 import com.android.systemui.util.kotlin.JavaAdapterKt;
@@ -499,9 +500,9 @@
         /** Creates a {@link LightBarControllerImpl}. */
         LightBarControllerImpl create(
                 int displayId,
-                CoroutineScope coroutineScope,
-                DarkIconDispatcher darkIconDispatcher,
-                StatusBarModePerDisplayRepository statusBarModePerDisplayRepository);
+                @NonNull CoroutineScope coroutineScope,
+                @NonNull DarkIconDispatcher darkIconDispatcher,
+                @NonNull StatusBarModePerDisplayRepository statusBarModePerDisplayRepository);
     }
 
     public static class LegacyFactory implements LightBarController.Factory {
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 2467e08..aa13089 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
@@ -18,6 +18,7 @@
 import android.app.StatusBarManager.WINDOW_STATUS_BAR
 import android.graphics.Point
 import android.util.Log
+import android.view.Display.DEFAULT_DISPLAY
 import android.view.InputDevice
 import android.view.MotionEvent
 import android.view.View
@@ -39,13 +40,13 @@
 import com.android.systemui.shade.ShadeLogger
 import com.android.systemui.shade.ShadeViewController
 import com.android.systemui.shade.StatusBarLongPressGestureDetector
-import com.android.systemui.shade.data.repository.ShadeDisplaysRepository
 import com.android.systemui.shade.display.StatusBarTouchShadeDisplayPolicy
 import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor
 import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround
 import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator
 import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
 import com.android.systemui.statusbar.data.repository.StatusBarContentInsetsProviderStore
+import com.android.systemui.statusbar.layout.StatusBarContentInsetsProvider
 import com.android.systemui.statusbar.policy.Clock
 import com.android.systemui.statusbar.policy.ConfigurationController
 import com.android.systemui.statusbar.window.StatusBarWindowStateController
@@ -85,7 +86,6 @@
     private val darkIconDispatcher: DarkIconDispatcher,
     private val statusBarContentInsetsProvider: StatusBarContentInsetsProvider,
     private val lazyStatusBarShadeDisplayPolicy: Lazy<StatusBarTouchShadeDisplayPolicy>,
-    private val shadeDisplaysRepository: ShadeDisplaysRepository,
 ) : ViewController<PhoneStatusBarView>(view) {
 
     private lateinit var battery: BatteryMeterView
@@ -301,12 +301,12 @@
             }
 
             // With the StatusBarConnectedDisplays changes, status bar touches should result in
-            // shade interaction only if ShadeWindowGoesAround.isEnabled or if touch is on the
-            // display which currently hosts the shade.
+            // shade interaction only if ShadeWindowGoesAround.isEnabled or if touch is on default
+            // display.
             return if (
                 !StatusBarConnectedDisplays.isEnabled ||
                     ShadeWindowGoesAround.isEnabled ||
-                    context.displayId == shadeDisplaysRepository.displayId.value
+                    context.displayId == DEFAULT_DISPLAY
             ) {
                 shadeViewController.handleExternalTouch(event)
             } else {
@@ -367,7 +367,6 @@
         @DisplaySpecific private val darkIconDispatcher: DarkIconDispatcher,
         private val statusBarContentInsetsProviderStore: StatusBarContentInsetsProviderStore,
         private val lazyStatusBarShadeDisplayPolicy: Lazy<StatusBarTouchShadeDisplayPolicy>,
-        private val shadeDisplaysRepository: ShadeDisplaysRepository,
     ) {
         fun create(view: PhoneStatusBarView): PhoneStatusBarViewController {
             val statusBarMoveFromCenterAnimationController =
@@ -396,7 +395,6 @@
                 darkIconDispatcher,
                 statusBarContentInsetsProviderStore.defaultDisplay,
                 lazyStatusBarShadeDisplayPolicy,
-                shadeDisplaysRepository,
             )
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 324db79..d43fed0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -43,6 +43,7 @@
 import androidx.annotation.FloatRange;
 import androidx.annotation.Nullable;
 
+import com.android.app.tracing.coroutines.TrackTracer;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.colorextraction.ColorExtractor.GradientColors;
 import com.android.internal.graphics.ColorUtils;
@@ -554,7 +555,7 @@
 
         final ScrimState oldState = mState;
         mState = state;
-        Trace.traceCounter(Trace.TRACE_TAG_APP, "scrim_state", mState.ordinal());
+        TrackTracer.instantForGroup("scrim", "state", mState.ordinal());
 
         if (mCallback != null) {
             mCallback.onCancelled();
@@ -1279,10 +1280,9 @@
                 tint = getDebugScrimTint(scrimView);
             }
 
-            Trace.traceCounter(Trace.TRACE_TAG_APP, getScrimName(scrimView) + "_alpha",
+            TrackTracer.instantForGroup("scrim", getScrimName(scrimView) + "_alpha",
                     (int) (alpha * 255));
-
-            Trace.traceCounter(Trace.TRACE_TAG_APP, getScrimName(scrimView) + "_tint",
+            TrackTracer.instantForGroup("scrim", getScrimName(scrimView) + "_tint",
                     Color.alpha(tint));
             scrimView.setTint(tint);
             if (!mIsBouncerToGoneTransitionRunning) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index 198859a..8dcb663 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -17,8 +17,8 @@
 package com.android.systemui.statusbar.phone;
 
 import android.graphics.Color;
-import android.os.Trace;
 
+import com.android.app.tracing.coroutines.TrackTracer;
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.res.R;
 import com.android.systemui.scrim.ScrimView;
@@ -425,11 +425,11 @@
             tint = scrim == mScrimInFront ? ScrimController.DEBUG_FRONT_TINT
                     : ScrimController.DEBUG_BEHIND_TINT;
         }
-        Trace.traceCounter(Trace.TRACE_TAG_APP,
+        TrackTracer.instantForGroup("scrim",
                 scrim == mScrimInFront ? "front_scrim_alpha" : "back_scrim_alpha",
                 (int) (alpha * 255));
 
-        Trace.traceCounter(Trace.TRACE_TAG_APP,
+        TrackTracer.instantForGroup("scrim",
                 scrim == mScrimInFront ? "front_scrim_tint" : "back_scrim_tint",
                 Color.alpha(tint));
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.kt
index d699b38..f2fd794 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.kt
@@ -16,11 +16,7 @@
 package com.android.systemui.statusbar.phone
 
 import android.annotation.IntDef
-import android.content.Context
-import android.graphics.drawable.Icon
-import android.os.UserHandle
 import com.android.internal.statusbar.StatusBarIcon
-import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.CallIndicatorIconState
 import com.android.systemui.statusbar.pipeline.icons.shared.model.ModernStatusBarViewCreator
 
 /** Wraps [com.android.internal.statusbar.StatusBarIcon] so we can still have a uniform list */
@@ -136,30 +132,6 @@
             holder.tag = subId
             return holder
         }
-
-        /** Creates a new StatusBarIconHolder from a CallIndicatorIconState. */
-        @JvmStatic
-        fun fromCallIndicatorState(
-            context: Context,
-            state: CallIndicatorIconState,
-        ): StatusBarIconHolder {
-            val holder = StatusBarIconHolder()
-            val resId = if (state.isNoCalling) state.noCallingResId else state.callStrengthResId
-            val contentDescription =
-                if (state.isNoCalling) state.noCallingDescription else state.callStrengthDescription
-            holder.icon =
-                StatusBarIcon(
-                    UserHandle.SYSTEM,
-                    context.packageName,
-                    Icon.createWithResource(context, resId),
-                    0,
-                    0,
-                    contentDescription,
-                    StatusBarIcon.Type.SystemIcon,
-                )
-            holder.tag = state.subId
-            return holder
-        }
     }
 
     /**
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 3749b96..8443edd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -18,7 +18,6 @@
 
 import static android.view.WindowInsets.Type.navigationBars;
 
-import static com.android.systemui.Flags.predictiveBackAnimateBouncer;
 import static com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants.EXPANSION_HIDDEN;
 import static com.android.systemui.plugins.ActivityStarter.OnDismissAction;
 import static com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK;
@@ -328,7 +327,6 @@
     private float mQsExpansion;
 
     final Set<KeyguardViewManagerCallback> mCallbacks = new HashSet<>();
-    private boolean mIsBackAnimationEnabled;
     private final UdfpsOverlayInteractor mUdfpsOverlayInteractor;
     private final ActivityStarter mActivityStarter;
 
@@ -434,7 +432,6 @@
                 .map(SysUIUnfoldComponent::getFoldAodAnimationController).orElse(null);
         mAlternateBouncerInteractor = alternateBouncerInteractor;
         mBouncerInteractor = bouncerInteractor;
-        mIsBackAnimationEnabled = predictiveBackAnimateBouncer();
         mUdfpsOverlayInteractor = udfpsOverlayInteractor;
         mActivityStarter = activityStarter;
         mKeyguardTransitionInteractor = keyguardTransitionInteractor;
@@ -630,7 +627,7 @@
 
     private boolean shouldPlayBackAnimation() {
         // Suppress back animation when bouncer shouldn't be dismissed on back invocation.
-        return !needsFullscreenBouncer() && mIsBackAnimationEnabled;
+        return !needsFullscreenBouncer();
     }
 
     @Override
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 d5fafe2..30dc9b9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
@@ -17,8 +17,9 @@
 package com.android.systemui.statusbar.phone;
 
 import static com.android.systemui.Flags.statusBarSignalPolicyRefactor;
+import static com.android.systemui.common.shared.model.ContentDescription.loadContentDescription;
 
-import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.os.Handler;
 import android.util.ArraySet;
@@ -26,6 +27,7 @@
 
 import com.android.settingslib.mobile.TelephonyIcons;
 import com.android.systemui.CoreStartable;
+import com.android.systemui.common.shared.model.Icon;
 import com.android.systemui.dagger.SysUISingleton;
 import com.android.systemui.res.R;
 import com.android.systemui.statusbar.connectivity.IconState;
@@ -33,16 +35,13 @@
 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.pipeline.ethernet.domain.EthernetInteractor;
+import com.android.systemui.statusbar.pipeline.ethernet.shared.StatusBarSignalPolicyRefactorEthernet;
 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;
-import java.util.Objects;
-
 import javax.inject.Inject;
 
 /** Controls the signal policies for icons shown in the statusbar. */
@@ -59,56 +58,48 @@
     private final String mSlotMobile;
     private final String mSlotEthernet;
     private final String mSlotVpn;
-    private final String mSlotNoCalling;
-    private final String mSlotCallStrength;
 
     private final Context mContext;
     private final StatusBarIconController mIconController;
     private final NetworkController mNetworkController;
     private final SecurityController mSecurityController;
     private final Handler mHandler = Handler.getMain();
-    private final CarrierConfigTracker mCarrierConfigTracker;
     private final TunerService mTunerService;
     private final JavaAdapter mJavaAdapter;
     private final AirplaneModeInteractor mAirplaneModeInteractor;
+    private final EthernetInteractor mEthernetInteractor;
 
     private boolean mHideAirplane;
     private boolean mHideMobile;
     private boolean mHideEthernet;
-    private final boolean mActivityEnabled;
 
-    private final ArrayList<CallIndicatorIconState> mCallIndicatorStates = new ArrayList<>();
     private boolean mInitialized;
 
     @Inject
     public StatusBarSignalPolicy(
             Context context,
             StatusBarIconController iconController,
-            CarrierConfigTracker carrierConfigTracker,
             NetworkController networkController,
             SecurityController securityController,
             TunerService tunerService,
             JavaAdapter javaAdapter,
-            AirplaneModeInteractor airplaneModeInteractor
+            AirplaneModeInteractor airplaneModeInteractor,
+            EthernetInteractor ethernetInteractor
     ) {
         mContext = context;
 
         mIconController = iconController;
-        mCarrierConfigTracker = carrierConfigTracker;
         mJavaAdapter = javaAdapter;
         mNetworkController = networkController;
         mSecurityController = securityController;
         mTunerService = tunerService;
         mAirplaneModeInteractor = airplaneModeInteractor;
+        mEthernetInteractor = ethernetInteractor;
 
         mSlotAirplane = mContext.getString(com.android.internal.R.string.status_bar_airplane);
         mSlotMobile   = mContext.getString(com.android.internal.R.string.status_bar_mobile);
         mSlotEthernet = mContext.getString(com.android.internal.R.string.status_bar_ethernet);
         mSlotVpn      = mContext.getString(com.android.internal.R.string.status_bar_vpn);
-        mSlotNoCalling = mContext.getString(com.android.internal.R.string.status_bar_no_calling);
-        mSlotCallStrength =
-                mContext.getString(com.android.internal.R.string.status_bar_call_strength);
-        mActivityEnabled = mContext.getResources().getBoolean(R.bool.config_showActivity);
     }
 
     @Override
@@ -123,6 +114,9 @@
 
         mJavaAdapter.alwaysCollectFlow(
                 mAirplaneModeInteractor.isAirplaneMode(), this::updateAirplaneModeIcon);
+        if (StatusBarSignalPolicyRefactorEthernet.isEnabled()) {
+            mJavaAdapter.alwaysCollectFlow(mEthernetInteractor.getIcon(), this::updateEthernetIcon);
+        }
     }
 
     /** Call to initialize and register this class with the system. */
@@ -140,6 +134,9 @@
                     mAirplaneModeInteractor.isAirplaneMode(),
                     this::updateAirplaneModeIcon);
         }
+        if (StatusBarSignalPolicyRefactorEthernet.isEnabled()) {
+            mJavaAdapter.alwaysCollectFlow(mEthernetInteractor.getIcon(), this::updateEthernetIcon);
+        }
     }
 
     public void destroy() {
@@ -201,45 +198,11 @@
     }
 
     @Override
-    public void setCallIndicator(@NonNull IconState statusIcon, int subId) {
-        if (DEBUG) {
-            Log.d(TAG, "setCallIndicator: "
-                    + "statusIcon = " + statusIcon + ","
-                    + "subId = " + subId);
-        }
-        CallIndicatorIconState state = getNoCallingState(subId);
-        if (state == null) {
+    public void setEthernetIndicators(IconState state) {
+        if (StatusBarSignalPolicyRefactorEthernet.isEnabled()) {
             return;
         }
-        if (statusIcon.icon == R.drawable.ic_shade_no_calling_sms) {
-            state.isNoCalling = statusIcon.visible;
-            state.noCallingDescription = statusIcon.contentDescription;
-        } else {
-            state.callStrengthResId = statusIcon.icon;
-            state.callStrengthDescription = statusIcon.contentDescription;
-        }
-        if (mCarrierConfigTracker.getCallStrengthConfig(subId)) {
-            mIconController.setCallStrengthIcons(mSlotCallStrength,
-                    CallIndicatorIconState.copyStates(mCallIndicatorStates));
-        } else {
-            mIconController.removeIcon(mSlotCallStrength, subId);
-        }
-        mIconController.setNoCallingIcons(mSlotNoCalling,
-                CallIndicatorIconState.copyStates(mCallIndicatorStates));
-    }
 
-    private CallIndicatorIconState getNoCallingState(int subId) {
-        for (CallIndicatorIconState state : mCallIndicatorStates) {
-            if (state.subId == subId) {
-                return state;
-            }
-        }
-        Log.e(TAG, "Unexpected subscription " + subId);
-        return null;
-    }
-
-    @Override
-    public void setEthernetIndicators(IconState state) {
         int resId = state.icon;
         String description = state.contentDescription;
 
@@ -251,6 +214,22 @@
         }
     }
 
+    private void updateEthernetIcon(@Nullable Icon.Resource ethernetIcon) {
+        if (StatusBarSignalPolicyRefactorEthernet.isUnexpectedlyInLegacyMode()) {
+            return;
+        }
+
+        if (ethernetIcon != null) {
+            mIconController.setIcon(
+                    mSlotEthernet,
+                    ethernetIcon.getRes(),
+                    loadContentDescription(ethernetIcon.getContentDescription(), mContext));
+            mIconController.setIconVisibility(mSlotEthernet, true);
+        } else {
+            mIconController.setIconVisibility(mSlotEthernet, false);
+        }
+    }
+
     @Override
     public void setIsAirplaneMode(IconState icon) {
         if (statusBarSignalPolicyRefactor()) {
@@ -287,64 +266,4 @@
                     mContext.getString(R.string.accessibility_airplane_mode));
         }
     }
-
-    /**
-     * Stores the statusbar state for no Calling & SMS.
-     */
-    public static class CallIndicatorIconState {
-        public boolean isNoCalling;
-        public int noCallingResId;
-        public int callStrengthResId;
-        public int subId;
-        public String noCallingDescription;
-        public String callStrengthDescription;
-
-        private CallIndicatorIconState(int subId) {
-            this.subId = subId;
-            this.noCallingResId = R.drawable.ic_shade_no_calling_sms;
-            this.callStrengthResId = TelephonyIcons.MOBILE_CALL_STRENGTH_ICONS[0];
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            // Skipping reference equality bc this should be more of a value type
-            if (o == null || getClass() != o.getClass()) {
-                return false;
-            }
-            CallIndicatorIconState that = (CallIndicatorIconState) o;
-            return  isNoCalling == that.isNoCalling
-                    && noCallingResId == that.noCallingResId
-                    && callStrengthResId == that.callStrengthResId
-                    && subId == that.subId
-                    && noCallingDescription == that.noCallingDescription
-                    && callStrengthDescription == that.callStrengthDescription;
-
-        }
-
-        @Override
-        public int hashCode() {
-            return Objects.hash(isNoCalling, noCallingResId,
-                    callStrengthResId, subId, noCallingDescription, callStrengthDescription);
-        }
-
-        private void copyTo(CallIndicatorIconState other) {
-            other.isNoCalling = isNoCalling;
-            other.noCallingResId = noCallingResId;
-            other.callStrengthResId = callStrengthResId;
-            other.subId = subId;
-            other.noCallingDescription = noCallingDescription;
-            other.callStrengthDescription = callStrengthDescription;
-        }
-
-        private static List<CallIndicatorIconState> copyStates(
-                List<CallIndicatorIconState> inStates) {
-            ArrayList<CallIndicatorIconState> outStates = new ArrayList<>();
-            for (CallIndicatorIconState state : inStates) {
-                CallIndicatorIconState copy = new CallIndicatorIconState(state.subId);
-                state.copyTo(copy);
-                outStates.add(copy);
-            }
-            return outStates;
-        }
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusOverlayHoverListener.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusOverlayHoverListener.kt
index 394502b..031754d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusOverlayHoverListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusOverlayHoverListener.kt
@@ -33,6 +33,7 @@
 import com.android.systemui.lifecycle.repeatWhenAttached
 import com.android.systemui.plugins.DarkIconDispatcher
 import com.android.systemui.res.R
+import com.android.systemui.statusbar.data.repository.StatusBarConfigurationController
 import com.android.systemui.statusbar.data.repository.StatusBarConfigurationControllerStore
 import com.android.systemui.statusbar.data.repository.SysuiDarkIconDispatcherStore
 import com.android.systemui.statusbar.phone.SysuiDarkIconDispatcher.DarkChange
@@ -54,7 +55,7 @@
 ) {
 
     /** Creates listener always using the same light color for overlay */
-    fun createListener(view: View) =
+    fun createListener(view: View): StatusOverlayHoverListener =
         StatusOverlayHoverListener(
             view,
             configurationController,
@@ -65,8 +66,10 @@
     /**
      * Creates listener using [DarkIconDispatcher] to determine light or dark color of the overlay
      */
-    fun createDarkAwareListener(view: View) =
-        createDarkAwareListener(view, view.darkIconDispatcher.darkChangeFlow())
+    fun createDarkAwareListener(view: View): StatusOverlayHoverListener? {
+        val darkIconDispatcher = view.darkIconDispatcher ?: return null
+        return createDarkAwareListener(view, darkIconDispatcher.darkChangeFlow())
+    }
 
     /**
      * Creates listener using [DarkIconDispatcher] to determine light or dark color of the overlay
@@ -78,27 +81,34 @@
         rightHoverMargin: Int = 0,
         topHoverMargin: Int = 0,
         bottomHoverMargin: Int = 0,
-    ) =
-        createDarkAwareListener(
+    ): StatusOverlayHoverListener? {
+        val darkIconDispatcher = view.darkIconDispatcher ?: return null
+        return createDarkAwareListener(
             view,
-            view.darkIconDispatcher.darkChangeFlow(),
+            darkIconDispatcher.darkChangeFlow(),
             leftHoverMargin,
             rightHoverMargin,
             topHoverMargin,
             bottomHoverMargin,
         )
+    }
 
     /**
      * Creates listener using provided [DarkChange] producer to determine light or dark color of the
      * overlay
      */
-    fun createDarkAwareListener(view: View, darkFlow: StateFlow<DarkChange>) =
-        StatusOverlayHoverListener(
+    fun createDarkAwareListener(
+        view: View,
+        darkFlow: StateFlow<DarkChange>,
+    ): StatusOverlayHoverListener? {
+        val configurationController = view.statusBarConfigurationController ?: return null
+        return StatusOverlayHoverListener(
             view,
-            view.statusBarConfigurationController,
+            configurationController,
             view.resources,
             darkFlow.map { toHoverTheme(view, it) },
         )
+    }
 
     private fun createDarkAwareListener(
         view: View,
@@ -107,10 +117,11 @@
         rightHoverMargin: Int = 0,
         topHoverMargin: Int = 0,
         bottomHoverMargin: Int = 0,
-    ) =
-        StatusOverlayHoverListener(
+    ): StatusOverlayHoverListener? {
+        val configurationController = view.statusBarConfigurationController ?: return null
+        return StatusOverlayHoverListener(
             view,
-            view.statusBarConfigurationController,
+            configurationController,
             view.resources,
             darkFlow.map { toHoverTheme(view, it) },
             leftHoverMargin,
@@ -118,11 +129,12 @@
             topHoverMargin,
             bottomHoverMargin,
         )
+    }
 
-    private val View.statusBarConfigurationController
+    private val View.statusBarConfigurationController: StatusBarConfigurationController?
         get() = statusBarConfigurationControllerStore.forDisplay(context.displayId)
 
-    private val View.darkIconDispatcher
+    private val View.darkIconDispatcher: SysuiDarkIconDispatcher?
         get() = darkIconDispatcherStore.forDisplay(context.displayId)
 
     private fun toHoverTheme(view: View, darkChange: DarkChange): HoverTheme {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
index 03324d2..c47ed17 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
@@ -16,8 +16,6 @@
 
 package com.android.systemui.statusbar.phone;
 
-import static com.android.systemui.Flags.predictiveBackAnimateDialogs;
-
 import android.app.AlertDialog;
 import android.app.Dialog;
 import android.content.BroadcastReceiver;
@@ -285,15 +283,13 @@
         for (int i = 0; i < mOnCreateRunnables.size(); i++) {
             mOnCreateRunnables.get(i).run();
         }
-        if (predictiveBackAnimateDialogs()) {
-            View targetView = getWindow().getDecorView();
-            DialogKt.registerAnimationOnBackInvoked(
-                    /* dialog = */ this,
-                    /* targetView = */ targetView,
-                    /* backAnimationSpec= */mDelegate.getBackAnimationSpec(
-                            () -> targetView.getResources().getDisplayMetrics())
-            );
-        }
+        View targetView = getWindow().getDecorView();
+        DialogKt.registerAnimationOnBackInvoked(
+                /* dialog = */ this,
+                /* targetView = */ targetView,
+                /* backAnimationSpec= */mDelegate.getBackAnimationSpec(
+                        () -> targetView.getResources().getDisplayMetrics())
+        );
     }
 
     private void updateWindowSize() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt
index 4f32aaa26..037dda9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarPhoneModule.kt
@@ -31,8 +31,10 @@
 import com.android.systemui.statusbar.core.StatusBarInitializerStore
 import com.android.systemui.statusbar.core.StatusBarOrchestrator
 import com.android.systemui.statusbar.core.StatusBarRootModernization
+import com.android.systemui.statusbar.data.repository.DarkIconDispatcherStore
 import com.android.systemui.statusbar.data.repository.PrivacyDotViewControllerStoreModule
 import com.android.systemui.statusbar.data.repository.PrivacyDotWindowControllerStoreModule
+import com.android.systemui.statusbar.data.repository.StatusBarConfigurationControllerStore
 import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore
 import com.android.systemui.statusbar.events.PrivacyDotViewControllerModule
 import com.android.systemui.statusbar.phone.AutoHideControllerStore
@@ -107,10 +109,14 @@
             implFactory: StatusBarInitializerImpl.Factory,
             statusBarWindowControllerStore: StatusBarWindowControllerStore,
             statusBarModeRepositoryStore: StatusBarModeRepositoryStore,
+            statusBarConfigurationControllerStore: StatusBarConfigurationControllerStore,
+            darkIconDispatcherStore: DarkIconDispatcherStore,
         ): StatusBarInitializerImpl {
             return implFactory.create(
                 statusBarWindowControllerStore.defaultDisplay,
                 statusBarModeRepositoryStore.defaultDisplay,
+                statusBarConfigurationControllerStore.defaultDisplay,
+                darkIconDispatcherStore.defaultDisplay,
             )
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/data/repository/DarkIconRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/data/repository/DarkIconRepository.kt
index 49356eb..0464654 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/data/repository/DarkIconRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/data/repository/DarkIconRepository.kt
@@ -15,12 +15,14 @@
  */
 package com.android.systemui.statusbar.phone.data.repository
 
+import android.util.Log
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.statusbar.data.repository.SysuiDarkIconDispatcherStore
 import com.android.systemui.statusbar.phone.SysuiDarkIconDispatcher.DarkChange
 import dagger.Binds
 import dagger.Module
 import javax.inject.Inject
+import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
 
 /** Dark-mode state for tinting icons. */
@@ -33,8 +35,22 @@
 @Inject
 constructor(private val darkIconDispatcherStore: SysuiDarkIconDispatcherStore) :
     DarkIconRepository {
-    override fun darkState(displayId: Int): StateFlow<DarkChange> =
-        darkIconDispatcherStore.forDisplay(displayId).darkChangeFlow()
+    override fun darkState(displayId: Int): StateFlow<DarkChange> {
+        val perDisplayDakIconDispatcher = darkIconDispatcherStore.forDisplay(displayId)
+        if (perDisplayDakIconDispatcher == null) {
+            Log.e(
+                TAG,
+                "DarkIconDispatcher for display $displayId is null. Returning flow of " +
+                    "DarkChange.EMPTY",
+            )
+            return MutableStateFlow(DarkChange.EMPTY)
+        }
+        return perDisplayDakIconDispatcher.darkChangeFlow()
+    }
+
+    private companion object {
+        const val TAG = "DarkIconRepositoryImpl"
+    }
 }
 
 @Module
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/domain/interactor/LightsOutInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/domain/interactor/LightsOutInteractor.kt
index ed8b3e8..b15fffb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/domain/interactor/LightsOutInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/domain/interactor/LightsOutInteractor.kt
@@ -34,8 +34,8 @@
 @Inject
 constructor(private val repository: StatusBarModeRepositoryStore) {
 
-    fun isLowProfile(displayId: Int): Flow<Boolean> =
-        repository.forDisplay(displayId).statusBarMode.map {
+    fun isLowProfile(displayId: Int): Flow<Boolean>? =
+        repository.forDisplay(displayId)?.statusBarMode?.map {
             when (it) {
                 StatusBarMode.LIGHTS_OUT,
                 StatusBarMode.LIGHTS_OUT_TRANSPARENT -> true
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 d257288..e622d8f 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
@@ -47,6 +47,7 @@
 import com.android.systemui.demomode.DemoMode;
 import com.android.systemui.demomode.DemoModeController;
 import com.android.systemui.dump.DumpManager;
+import com.android.systemui.plugins.DarkIconDispatcher;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.res.R;
 import com.android.systemui.scene.shared.flag.SceneContainerFlag;
@@ -59,6 +60,9 @@
 import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips;
 import com.android.systemui.statusbar.core.StatusBarConnectedDisplays;
 import com.android.systemui.statusbar.core.StatusBarRootModernization;
+import com.android.systemui.statusbar.data.repository.DarkIconDispatcherStore;
+import com.android.systemui.statusbar.data.repository.StatusBarConfigurationController;
+import com.android.systemui.statusbar.data.repository.StatusBarConfigurationControllerStore;
 import com.android.systemui.statusbar.disableflags.DisableFlagsLogger;
 import com.android.systemui.statusbar.events.SystemStatusAnimationCallback;
 import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
@@ -77,7 +81,10 @@
 import com.android.systemui.statusbar.pipeline.shared.ui.binder.HomeStatusBarViewBinder;
 import com.android.systemui.statusbar.pipeline.shared.ui.binder.StatusBarVisibilityChangeListener;
 import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.HomeStatusBarViewModel;
+import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.HomeStatusBarViewModel.HomeStatusBarViewModelFactory;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.statusbar.window.StatusBarWindowController;
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore;
 import com.android.systemui.statusbar.window.StatusBarWindowStateController;
 import com.android.systemui.statusbar.window.StatusBarWindowStateListener;
 import com.android.systemui.util.CarrierConfigTracker;
@@ -136,6 +143,8 @@
     private StatusBarVisibilityModel mLastModifiedVisibility =
             StatusBarVisibilityModel.createDefaultModel();
     private DarkIconManager mDarkIconManager;
+    private HomeStatusBarViewModel mHomeStatusBarViewModel;
+
     private final HomeStatusBarComponent.Factory mHomeStatusBarComponentFactory;
     private final CommandQueue mCommandQueue;
     private final CollapsedStatusBarFragmentLogger mCollapsedStatusBarFragmentLogger;
@@ -145,8 +154,8 @@
     private final ShadeExpansionStateManager mShadeExpansionStateManager;
     private final StatusBarIconController mStatusBarIconController;
     private final CarrierConfigTracker mCarrierConfigTracker;
-    private final HomeStatusBarViewModel mHomeStatusBarViewModel;
     private final HomeStatusBarViewBinder mHomeStatusBarViewBinder;
+    private final HomeStatusBarViewModelFactory mHomeStatusBarViewModelFactory;
     private final StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager;
     private final DarkIconManager.Factory mDarkIconManagerFactory;
     private final SecureSettings mSecureSettings;
@@ -156,6 +165,9 @@
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
     private final NotificationIconContainerStatusBarViewBinder mNicViewBinder;
     private final DemoModeController mDemoModeController;
+    private final StatusBarWindowControllerStore mStatusBarWindowControllerStore;
+    private final StatusBarConfigurationControllerStore mStatusBarConfigurationControllerStore;
+    private final DarkIconDispatcherStore mDarkIconDispatcherStore;
 
     private List<String> mBlockedIcons = new ArrayList<>();
     private Map<Startable, Startable.State> mStartableStates = new ArrayMap<>();
@@ -247,7 +259,7 @@
             ShadeExpansionStateManager shadeExpansionStateManager,
             StatusBarIconController statusBarIconController,
             DarkIconManager.Factory darkIconManagerFactory,
-            HomeStatusBarViewModel homeStatusBarViewModel,
+            HomeStatusBarViewModelFactory homeStatusBarViewModelFactory,
             HomeStatusBarViewBinder homeStatusBarViewBinder,
             StatusBarHideIconsForBouncerManager statusBarHideIconsForBouncerManager,
             KeyguardStateController keyguardStateController,
@@ -263,13 +275,16 @@
             DumpManager dumpManager,
             StatusBarWindowStateController statusBarWindowStateController,
             KeyguardUpdateMonitor keyguardUpdateMonitor,
-            DemoModeController demoModeController) {
+            DemoModeController demoModeController,
+            StatusBarWindowControllerStore statusBarWindowControllerStore,
+            StatusBarConfigurationControllerStore statusBarConfigurationControllerStore,
+            DarkIconDispatcherStore darkIconDispatcherStore) {
         mHomeStatusBarComponentFactory = homeStatusBarComponentFactory;
         mOngoingCallController = ongoingCallController;
         mAnimationScheduler = animationScheduler;
         mShadeExpansionStateManager = shadeExpansionStateManager;
         mStatusBarIconController = statusBarIconController;
-        mHomeStatusBarViewModel = homeStatusBarViewModel;
+        mHomeStatusBarViewModelFactory = homeStatusBarViewModelFactory;
         mHomeStatusBarViewBinder = homeStatusBarViewBinder;
         mStatusBarHideIconsForBouncerManager = statusBarHideIconsForBouncerManager;
         mDarkIconManagerFactory = darkIconManagerFactory;
@@ -287,6 +302,9 @@
         mStatusBarWindowStateController = statusBarWindowStateController;
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mDemoModeController = demoModeController;
+        mStatusBarWindowControllerStore = statusBarWindowControllerStore;
+        mStatusBarConfigurationControllerStore = statusBarConfigurationControllerStore;
+        mDarkIconDispatcherStore = darkIconDispatcherStore;
     }
 
     private final DemoMode mDemoModeCallback = new DemoMode() {
@@ -337,8 +355,27 @@
     public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
         super.onViewCreated(view, savedInstanceState);
         mDumpManager.registerDumpable(getDumpableName(), this);
-        mHomeStatusBarComponent = mHomeStatusBarComponentFactory.create(
-                (PhoneStatusBarView) getView());
+        int displayId = view.getContext().getDisplayId();
+        StatusBarConfigurationController configurationController =
+                mStatusBarConfigurationControllerStore.forDisplay(displayId);
+        if (configurationController == null) {
+            return;
+        }
+        StatusBarWindowController statusBarWindowController =
+                mStatusBarWindowControllerStore.forDisplay(displayId);
+        if (statusBarWindowController == null) {
+            return;
+        }
+        DarkIconDispatcher darkIconDispatcher = mDarkIconDispatcherStore.forDisplay(displayId);
+        if (darkIconDispatcher == null) {
+            return;
+        }
+        mHomeStatusBarComponent =
+                mHomeStatusBarComponentFactory.create(
+                        (PhoneStatusBarView) getView(),
+                        configurationController,
+                        statusBarWindowController,
+                        darkIconDispatcher);
         mHomeStatusBarComponent.init();
         mStartableStates.clear();
         for (Startable startable : mHomeStatusBarComponent.getStartables()) {
@@ -376,6 +413,7 @@
         mCarrierConfigTracker.addCallback(mCarrierConfigCallback);
         mCarrierConfigTracker.addDefaultDataSubscriptionChangedListener(mDefaultDataListener);
 
+        mHomeStatusBarViewModel = mHomeStatusBarViewModelFactory.create(displayId);
         mHomeStatusBarViewBinder.bind(
                 view.getContext().getDisplayId(),
                 mStatusBar,
@@ -453,6 +491,9 @@
     @Override
     public void onResume() {
         super.onResume();
+        if (mHomeStatusBarComponent == null) {
+            return;
+        }
         mCommandQueue.addCallback(this);
         mStatusBarStateController.addCallback(this);
         initOngoingCallChip();
@@ -468,6 +509,9 @@
     @Override
     public void onPause() {
         super.onPause();
+        if (mHomeStatusBarComponent == null) {
+            return;
+        }
         mCommandQueue.removeCallback(this);
         mStatusBarStateController.removeCallback(this);
         if (!StatusBarRootModernization.isEnabled()) {
@@ -480,6 +524,9 @@
     @Override
     public void onDestroyView() {
         super.onDestroyView();
+        if (mHomeStatusBarComponent == null) {
+            return;
+        }
         mStatusBarIconController.removeIconGroup(mDarkIconManager);
         mCarrierConfigTracker.removeCallback(mCarrierConfigCallback);
         mCarrierConfigTracker.removeDataSubscriptionChangedListener(mDefaultDataListener);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarComponent.java
index f8ad0f2..7207d0a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarComponent.java
@@ -20,15 +20,17 @@
 import com.android.systemui.dagger.qualifiers.DisplaySpecific;
 import com.android.systemui.dagger.qualifiers.RootView;
 import com.android.systemui.plugins.DarkIconDispatcher;
+import com.android.systemui.statusbar.data.repository.StatusBarConfigurationController;
+import com.android.systemui.statusbar.layout.StatusBarBoundsProvider;
 import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor;
 import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
 import com.android.systemui.statusbar.phone.LegacyLightsOutNotifController;
 import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions;
 import com.android.systemui.statusbar.phone.PhoneStatusBarView;
 import com.android.systemui.statusbar.phone.PhoneStatusBarViewController;
-import com.android.systemui.statusbar.phone.StatusBarBoundsProvider;
 import com.android.systemui.statusbar.phone.StatusBarDemoMode;
 import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
+import com.android.systemui.statusbar.window.StatusBarWindowController;
 
 import dagger.BindsInstance;
 import dagger.Subcomponent;
@@ -57,7 +59,10 @@
     interface Factory {
         /** */
         HomeStatusBarComponent create(
-                @BindsInstance @RootView PhoneStatusBarView phoneStatusBarView);
+                @BindsInstance @RootView PhoneStatusBarView phoneStatusBarView,
+                @BindsInstance StatusBarConfigurationController configurationController,
+                @BindsInstance StatusBarWindowController statusBarWindowController,
+                @BindsInstance @DisplaySpecific DarkIconDispatcher darkIconDispatcher);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarModule.java
index 182f8d7..6a331b9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/HomeStatusBarModule.java
@@ -22,19 +22,14 @@
 import com.android.systemui.battery.BatteryMeterView;
 import com.android.systemui.dagger.qualifiers.DisplaySpecific;
 import com.android.systemui.dagger.qualifiers.RootView;
-import com.android.systemui.plugins.DarkIconDispatcher;
 import com.android.systemui.res.R;
 import com.android.systemui.statusbar.HeadsUpStatusBarView;
-import com.android.systemui.statusbar.data.repository.DarkIconDispatcherStore;
-import com.android.systemui.statusbar.data.repository.StatusBarConfigurationController;
-import com.android.systemui.statusbar.data.repository.StatusBarConfigurationControllerStore;
 import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions;
 import com.android.systemui.statusbar.phone.PhoneStatusBarView;
 import com.android.systemui.statusbar.phone.PhoneStatusBarViewController;
 import com.android.systemui.statusbar.phone.StatusBarLocation;
 import com.android.systemui.statusbar.policy.Clock;
 import com.android.systemui.statusbar.window.StatusBarWindowController;
-import com.android.systemui.statusbar.window.StatusBarWindowControllerStore;
 
 import dagger.Module;
 import dagger.Provides;
@@ -149,29 +144,4 @@
     static int displayId(@RootView PhoneStatusBarView view) {
         return view.getContext().getDisplayId();
     }
-
-    /** */
-    @Provides
-    @HomeStatusBarScope
-    static StatusBarConfigurationController configurationController(
-            @DisplaySpecific int displayId, StatusBarConfigurationControllerStore store) {
-        return store.forDisplay(displayId);
-    }
-
-    /** */
-    @Provides
-    @HomeStatusBarScope
-    static StatusBarWindowController provideWindowController(
-            @DisplaySpecific int displayId, StatusBarWindowControllerStore store) {
-        return store.forDisplay(displayId);
-    }
-
-    /** */
-    @Provides
-    @HomeStatusBarScope
-    @DisplaySpecific
-    static DarkIconDispatcher darkIconDispatcher(
-            @DisplaySpecific int displayId, DarkIconDispatcherStore store) {
-        return store.forDisplay(displayId);
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarStartablesModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarStartablesModule.kt
index ba91814..b56a9a1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarStartablesModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarStartablesModule.kt
@@ -16,7 +16,7 @@
 
 package com.android.systemui.statusbar.phone.fragment.dagger
 
-import com.android.systemui.statusbar.phone.StatusBarBoundsProvider
+import com.android.systemui.statusbar.layout.StatusBarBoundsProvider
 import dagger.Binds
 import dagger.Module
 import dagger.multibindings.IntoSet
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 0459b97..6c3438b 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
@@ -26,7 +26,6 @@
 
 import com.android.internal.statusbar.StatusBarIcon;
 import com.android.systemui.res.R;
-import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.CallIndicatorIconState;
 
 import java.util.List;
 
@@ -85,15 +84,6 @@
      * {@link com.android.systemui.statusbar.pipeline.mobile.ui.binder.MobileIconBinder}.
      */
     void setNewMobileIconSubIds(List<Integer> subIds);
-    /**
-     * Display the no calling & SMS icons.
-     */
-    void setCallStrengthIcons(String slot, List<CallIndicatorIconState> states);
-
-    /**
-     * Display the no calling & SMS icons.
-     */
-    void setNoCallingIcons(String slot, List<CallIndicatorIconState> states);
 
     /** Sets whether the icon in the given slot should be visible or not. */
     void setIconVisibility(String slot, boolean b);
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 e66e8138..42a72d2 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
@@ -47,7 +47,6 @@
 import com.android.systemui.statusbar.StatusIconDisplayable;
 import com.android.systemui.statusbar.phone.StatusBarIconHolder;
 import com.android.systemui.statusbar.phone.StatusBarIconHolder.BindableIconHolder;
-import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.CallIndicatorIconState;
 import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags;
 import com.android.systemui.statusbar.pipeline.icons.shared.BindableIconsRegistry;
 import com.android.systemui.statusbar.pipeline.icons.shared.model.BindableIcon;
@@ -332,56 +331,6 @@
         }
     }
 
-    /**
-     * Accept a list of CallIndicatorIconStates, and show the call strength icons.
-     * @param slot statusbar slot for the call strength icons
-     * @param states All of the no Calling & SMS icon states
-     */
-    @Override
-    public void setCallStrengthIcons(String slot, List<CallIndicatorIconState> states) {
-        Slot callStrengthSlot = mStatusBarIconList.getSlot(slot);
-        Collections.reverse(states);
-        for (CallIndicatorIconState state : states) {
-            if (!state.isNoCalling) {
-                StatusBarIconHolder holder = callStrengthSlot.getHolderForTag(state.subId);
-                if (holder == null) {
-                    holder = StatusBarIconHolder.fromCallIndicatorState(mContext, state);
-                } else {
-                    holder.setIcon(new StatusBarIcon(UserHandle.SYSTEM, mContext.getPackageName(),
-                            Icon.createWithResource(mContext, state.callStrengthResId), 0, 0,
-                            state.callStrengthDescription, StatusBarIcon.Type.SystemIcon));
-                }
-                setIcon(slot, holder);
-            }
-            setIconVisibility(slot, !state.isNoCalling, state.subId);
-        }
-    }
-
-    /**
-     * Accept a list of CallIndicatorIconStates, and show the no calling icons.
-     * @param slot statusbar slot for the no calling icons
-     * @param states All of the no Calling & SMS icon states
-     */
-    @Override
-    public void setNoCallingIcons(String slot, List<CallIndicatorIconState> states) {
-        Slot noCallingSlot = mStatusBarIconList.getSlot(slot);
-        Collections.reverse(states);
-        for (CallIndicatorIconState state : states) {
-            if (state.isNoCalling) {
-                StatusBarIconHolder holder = noCallingSlot.getHolderForTag(state.subId);
-                if (holder == null) {
-                    holder = StatusBarIconHolder.fromCallIndicatorState(mContext, state);
-                } else {
-                    holder.setIcon(new StatusBarIcon(UserHandle.SYSTEM, mContext.getPackageName(),
-                            Icon.createWithResource(mContext, state.noCallingResId), 0, 0,
-                            state.noCallingDescription, StatusBarIcon.Type.SystemIcon));
-                }
-                setIcon(slot, holder);
-            }
-            setIconVisibility(slot, state.isNoCalling, state.subId);
-        }
-    }
-
     private final CommandQueue.Callbacks mCommandQueueCallbacks = new CommandQueue.Callbacks() {
         @Override
         public void setIcon(String slot, StatusBarIcon icon) {
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 96666d8..c71162a 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
@@ -56,8 +56,8 @@
 import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepositoryImpl
 import com.android.systemui.statusbar.pipeline.shared.ui.binder.HomeStatusBarViewBinder
 import com.android.systemui.statusbar.pipeline.shared.ui.binder.HomeStatusBarViewBinderImpl
-import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.HomeStatusBarViewModel
-import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.HomeStatusBarViewModelImpl
+import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.HomeStatusBarViewModel.HomeStatusBarViewModelFactory
+import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.HomeStatusBarViewModelImpl.HomeStatusBarViewModelFactoryImpl
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.RealWifiRepository
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
 import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepositorySwitcher
@@ -148,7 +148,9 @@
     abstract fun bindCarrierConfigStartable(impl: CarrierConfigCoreStartable): CoreStartable
 
     @Binds
-    abstract fun homeStatusBarViewModel(impl: HomeStatusBarViewModelImpl): HomeStatusBarViewModel
+    abstract fun homeStatusBarViewModelFactory(
+        impl: HomeStatusBarViewModelFactoryImpl
+    ): HomeStatusBarViewModelFactory
 
     @Binds
     abstract fun homeStatusBarViewBinder(impl: HomeStatusBarViewBinderImpl): HomeStatusBarViewBinder
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PredictiveBackSysUiFlag.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ethernet/shared/StatusBarSignalPolicyRefactorEthernet.kt
similarity index 69%
rename from packages/SystemUI/src/com/android/systemui/statusbar/phone/PredictiveBackSysUiFlag.kt
rename to packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ethernet/shared/StatusBarSignalPolicyRefactorEthernet.kt
index 74d6ba5..48747df 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PredictiveBackSysUiFlag.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/ethernet/shared/StatusBarSignalPolicyRefactorEthernet.kt
@@ -14,17 +14,17 @@
  * limitations under the License.
  */
 
-package com.android.systemui.statusbar.phone
+package com.android.systemui.statusbar.pipeline.ethernet.shared
 
 import com.android.systemui.Flags
 import com.android.systemui.flags.FlagToken
 import com.android.systemui.flags.RefactorFlagUtils
 
-/** Helper for reading or using the predictive back flag state. */
+/** Helper for reading or using the status bar signal policy refactor ethernet flag state. */
 @Suppress("NOTHING_TO_INLINE")
-object PredictiveBackSysUiFlag {
+object StatusBarSignalPolicyRefactorEthernet {
     /** The aconfig flag name */
-    const val FLAG_NAME = Flags.FLAG_PREDICTIVE_BACK_SYSUI
+    const val FLAG_NAME = Flags.FLAG_STATUS_BAR_SIGNAL_POLICY_REFACTOR_ETHERNET
 
     /** A token used for dependency declaration */
     val token: FlagToken
@@ -33,7 +33,7 @@
     /** Is the refactor enabled */
     @JvmStatic
     inline val isEnabled
-        get() = Flags.predictiveBackSysui()
+        get() = Flags.statusBarSignalPolicyRefactorEthernet()
 
     /**
      * Called to ensure code is only run when the flag is enabled. This protects users from the
@@ -45,6 +45,14 @@
         RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME)
 
     /**
+     * Called to ensure code is only run when the flag is enabled. 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.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
index 99b4aa4..d69dc1e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryImpl.kt
@@ -143,7 +143,7 @@
                     object :
                         TelephonyCallback(),
                         TelephonyCallback.CarrierNetworkListener,
-                        TelephonyCallback.CarrierRoamingNtnModeListener,
+                        TelephonyCallback.CarrierRoamingNtnListener,
                         TelephonyCallback.DataActivityListener,
                         TelephonyCallback.DataConnectionStateListener,
                         TelephonyCallback.DataEnabledListener,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt
index 7aab47a..3a6716a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImpl.kt
@@ -21,7 +21,7 @@
 import android.telephony.TelephonyCallback
 import android.telephony.TelephonyManager
 import android.telephony.satellite.NtnSignalStrengthCallback
-import android.telephony.satellite.SatelliteCommunicationAllowedStateCallback
+import android.telephony.satellite.SatelliteCommunicationAccessStateCallback
 import android.telephony.satellite.SatelliteManager
 import android.telephony.satellite.SatelliteManager.SATELLITE_RESULT_SUCCESS
 import android.telephony.satellite.SatelliteModemStateCallback
@@ -263,9 +263,9 @@
 
     private fun isSatelliteAvailableFlow(sm: SupportedSatelliteManager): Flow<Boolean> =
         conflatedCallbackFlow {
-                val callback = SatelliteCommunicationAllowedStateCallback { allowed ->
+                val callback = SatelliteCommunicationAccessStateCallback { allowed ->
                     logBuffer.i({ bool1 = allowed }) {
-                        "onSatelliteCommunicationAllowedStateChanged: $bool1"
+                        "onSatelliteCommunicationAccessAllowedStateChanged: $bool1"
                     }
 
                     trySend(allowed)
@@ -273,20 +273,20 @@
 
                 var registered = false
                 try {
-                    logBuffer.i { "registerForCommunicationAllowedStateChanged" }
-                    sm.registerForCommunicationAllowedStateChanged(
+                    logBuffer.i { "registerForCommunicationAccessStateChanged" }
+                    sm.registerForCommunicationAccessStateChanged(
                         bgDispatcher.asExecutor(),
                         callback,
                     )
                     registered = true
                 } catch (e: Exception) {
-                    logBuffer.e("Error calling registerForCommunicationAllowedStateChanged", e)
+                    logBuffer.e("Error calling registerForCommunicationAccessStateChanged", e)
                 }
 
                 awaitClose {
                     if (registered) {
-                        logBuffer.i { "unRegisterForCommunicationAllowedStateChanged" }
-                        sm.unregisterForCommunicationAllowedStateChanged(callback)
+                        logBuffer.i { "unRegisterForCommunicationAccessStateChanged" }
+                        sm.unregisterForCommunicationAccessStateChanged(callback)
                     }
                 }
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt
index 7e06c35..2541d84 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/HomeStatusBarViewBinder.kt
@@ -41,6 +41,7 @@
 import com.android.systemui.statusbar.notification.icon.ui.viewbinder.ConnectedDisplaysStatusBarNotificationIconViewStore
 import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
 import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment
+import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization
 import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.HomeStatusBarViewModel
 import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.HomeStatusBarViewModel.VisibilityModel
 import javax.inject.Inject
@@ -106,16 +107,19 @@
                 }
 
                 if (NotificationsLiveDataStoreRefactor.isEnabled) {
-                    val displayId = view.display.displayId
                     val lightsOutView: View = view.requireViewById(R.id.notification_lights_out)
                     launch {
-                        viewModel.areNotificationsLightsOut(displayId).collect { show ->
+                        viewModel.areNotificationsLightsOut.collect { show ->
                             animateLightsOutView(lightsOutView, show)
                         }
                     }
                 }
 
-                if (Flags.statusBarScreenSharingChips() && !StatusBarNotifChips.isEnabled) {
+                if (
+                    Flags.statusBarScreenSharingChips() &&
+                        !StatusBarNotifChips.isEnabled &&
+                        !StatusBarChipsModernization.isEnabled
+                ) {
                     val primaryChipView: View =
                         view.requireViewById(R.id.ongoing_activity_chip_primary)
                     launch {
@@ -157,7 +161,11 @@
                     }
                 }
 
-                if (Flags.statusBarScreenSharingChips() && StatusBarNotifChips.isEnabled) {
+                if (
+                    Flags.statusBarScreenSharingChips() &&
+                        StatusBarNotifChips.isEnabled &&
+                        !StatusBarChipsModernization.isEnabled
+                ) {
                     val primaryChipView: View =
                         view.requireViewById(R.id.ongoing_activity_chip_primary)
                     val secondaryChipView: View =
@@ -209,7 +217,7 @@
                     StatusBarOperatorNameViewBinder.bind(
                         operatorNameView,
                         viewModel.operatorNameViewModel,
-                        viewModel::areaTint,
+                        viewModel.areaTint,
                     )
                     launch {
                         viewModel.shouldShowOperatorNameView.collect {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/StatusBarOperatorNameViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/StatusBarOperatorNameViewBinder.kt
index b7744d3..5dd76f4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/StatusBarOperatorNameViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/StatusBarOperatorNameViewBinder.kt
@@ -32,19 +32,16 @@
     fun bind(
         operatorFrameView: View,
         viewModel: StatusBarOperatorNameViewModel,
-        areaTint: (Int) -> Flow<StatusBarTintColor>,
+        areaTint: Flow<StatusBarTintColor>,
     ) {
         operatorFrameView.repeatWhenAttached {
             repeatOnLifecycle(Lifecycle.State.STARTED) {
-                val displayId = operatorFrameView.display.displayId
-
                 val operatorNameText =
                     operatorFrameView.requireViewById<TextView>(R.id.operator_name)
                 launch { viewModel.operatorName.collect { operatorNameText.text = it } }
 
                 launch {
-                    val tint = areaTint(displayId)
-                    tint.collect { statusBarTintColors ->
+                    areaTint.collect { statusBarTintColors ->
                         operatorNameText.setTextColor(
                             statusBarTintColors.tint(operatorNameText.viewBoundsOnScreen())
                         )
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt
index c3299bb..b78e010 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/composable/StatusBarRoot.kt
@@ -53,13 +53,14 @@
 import com.android.systemui.statusbar.pipeline.shared.ui.binder.HomeStatusBarViewBinder
 import com.android.systemui.statusbar.pipeline.shared.ui.binder.StatusBarVisibilityChangeListener
 import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.HomeStatusBarViewModel
+import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.HomeStatusBarViewModel.HomeStatusBarViewModelFactory
 import javax.inject.Inject
 
 /** Factory to simplify the dependency management for [StatusBarRoot] */
 class StatusBarRootFactory
 @Inject
 constructor(
-    private val homeStatusBarViewModel: HomeStatusBarViewModel,
+    private val homeStatusBarViewModelFactory: HomeStatusBarViewModelFactory,
     private val homeStatusBarViewBinder: HomeStatusBarViewBinder,
     private val notificationIconsBinder: NotificationIconContainerStatusBarViewBinder,
     private val darkIconManagerFactory: DarkIconManager.Factory,
@@ -70,17 +71,20 @@
 ) {
     fun create(root: ViewGroup, andThen: (ViewGroup) -> Unit): ComposeView {
         val composeView = ComposeView(root.context)
+        val displayId = root.context.displayId
+        val darkIconDispatcher =
+            darkIconDispatcherStore.forDisplay(root.context.displayId) ?: return composeView
         composeView.apply {
             setContent {
                 StatusBarRoot(
                     parent = root,
-                    statusBarViewModel = homeStatusBarViewModel,
+                    statusBarViewModel = homeStatusBarViewModelFactory.create(displayId),
                     statusBarViewBinder = homeStatusBarViewBinder,
                     notificationIconsBinder = notificationIconsBinder,
                     darkIconManagerFactory = darkIconManagerFactory,
                     iconController = iconController,
                     ongoingCallController = ongoingCallController,
-                    darkIconDispatcher = darkIconDispatcherStore.forDisplay(root.context.displayId),
+                    darkIconDispatcher = darkIconDispatcher,
                     eventAnimationInteractor = eventAnimationInteractor,
                     onViewCreated = andThen,
                 )
@@ -162,11 +166,19 @@
                         statusBarViewModel.iconBlockList,
                     )
 
-                    if (!StatusBarChipsModernization.isEnabled) {
+                    if (StatusBarChipsModernization.isEnabled) {
+                        // Make sure the primary chip is hidden when StatusBarChipsModernization is
+                        // enabled. OngoingActivityChips will be shown in a composable container
+                        // when this flag is enabled.
+                        phoneStatusBarView
+                            .requireViewById<View>(R.id.ongoing_activity_chip_primary)
+                            .visibility = View.GONE
+                    } else {
                         ongoingCallController.setChipView(
                             phoneStatusBarView.requireViewById(R.id.ongoing_activity_chip_primary)
                         )
                     }
+
                     // For notifications, first inflate the [NotificationIconContainer]
                     val notificationIconArea =
                         phoneStatusBarView.requireViewById<ViewGroup>(R.id.notification_icon_area)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt
index dcfbc5d..3f701fc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModel.kt
@@ -19,7 +19,6 @@
 import android.annotation.ColorInt
 import android.graphics.Rect
 import android.view.View
-import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
@@ -53,7 +52,9 @@
 import com.android.systemui.statusbar.pipeline.shared.domain.interactor.HomeStatusBarIconBlockListInteractor
 import com.android.systemui.statusbar.pipeline.shared.domain.interactor.HomeStatusBarInteractor
 import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.HomeStatusBarViewModel.VisibilityModel
-import javax.inject.Inject
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
@@ -63,6 +64,7 @@
 import kotlinx.coroutines.flow.distinctUntilChanged
 import kotlinx.coroutines.flow.emptyFlow
 import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.stateIn
 
@@ -117,6 +119,7 @@
     val shouldShowOperatorNameView: Flow<Boolean>
     val isClockVisible: Flow<VisibilityModel>
     val isNotificationIconContainerVisible: Flow<VisibilityModel>
+
     /**
      * Pair of (system info visibility, event animation state). The animation state can be used to
      * respond to the system event chip animations. In all cases, system info visibility correctly
@@ -136,13 +139,13 @@
      * whether there are notifications when the device is in
      * [android.view.View.SYSTEM_UI_FLAG_LOW_PROFILE].
      */
-    fun areNotificationsLightsOut(displayId: Int): Flow<Boolean>
+    val areNotificationsLightsOut: Flow<Boolean>
 
     /**
-     * Given a displayId, returns a flow of [StatusBarTintColor], a functional interface that will
-     * allow a view to calculate its correct tint depending on location
+     * A flow of [StatusBarTintColor], a functional interface that will allow a view to calculate
+     * its correct tint depending on location
      */
-    fun areaTint(displayId: Int): Flow<StatusBarTintColor>
+    val areaTint: Flow<StatusBarTintColor>
 
     /** Models the current visibility for a specific child view of status bar. */
     data class VisibilityModel(
@@ -156,17 +159,22 @@
         val baseVisibility: VisibilityModel,
         val animationState: SystemEventAnimationState,
     )
+
+    /** Interface for the assisted factory, to allow for providing a fake in tests */
+    interface HomeStatusBarViewModelFactory {
+        fun create(displayId: Int): HomeStatusBarViewModel
+    }
 }
 
-@SysUISingleton
 class HomeStatusBarViewModelImpl
-@Inject
+@AssistedInject
 constructor(
+    @Assisted thisDisplayId: Int,
     homeStatusBarInteractor: HomeStatusBarInteractor,
     homeStatusBarIconBlockListInteractor: HomeStatusBarIconBlockListInteractor,
-    private val lightsOutInteractor: LightsOutInteractor,
-    private val notificationsInteractor: ActiveNotificationsInteractor,
-    private val darkIconInteractor: DarkIconInteractor,
+    lightsOutInteractor: LightsOutInteractor,
+    notificationsInteractor: ActiveNotificationsInteractor,
+    darkIconInteractor: DarkIconInteractor,
     headsUpNotificationInteractor: HeadsUpNotificationInteractor,
     keyguardTransitionInteractor: KeyguardTransitionInteractor,
     keyguardInteractor: KeyguardInteractor,
@@ -210,22 +218,22 @@
             }
             .stateIn(coroutineScope, SharingStarted.WhileSubscribed(), initialValue = false)
 
-    override fun areNotificationsLightsOut(displayId: Int): Flow<Boolean> =
+    override val areNotificationsLightsOut: Flow<Boolean> =
         if (NotificationsLiveDataStoreRefactor.isUnexpectedlyInLegacyMode()) {
             emptyFlow()
         } else {
             combine(
                     notificationsInteractor.areAnyNotificationsPresent,
-                    lightsOutInteractor.isLowProfile(displayId),
+                    lightsOutInteractor.isLowProfile(thisDisplayId) ?: flowOf(false),
                 ) { hasNotifications, isLowProfile ->
                     hasNotifications && isLowProfile
                 }
                 .distinctUntilChanged()
         }
 
-    override fun areaTint(displayId: Int): Flow<StatusBarTintColor> =
+    override val areaTint: Flow<StatusBarTintColor> =
         darkIconInteractor
-            .darkState(displayId)
+            .darkState(thisDisplayId)
             .map { (areas: Collection<Rect>, tint: Int) ->
                 StatusBarTintColor { viewBounds: Rect ->
                     if (DarkIconDispatcher.isInAreas(areas, viewBounds)) {
@@ -363,6 +371,13 @@
     // Similar to the above, but uses INVISIBLE in place of GONE
     @View.Visibility
     private fun Boolean.toVisibleOrInvisible(): Int = if (this) View.VISIBLE else View.INVISIBLE
+
+    /** Inject this to create the display-dependent view model */
+    @AssistedFactory
+    interface HomeStatusBarViewModelFactoryImpl :
+        HomeStatusBarViewModel.HomeStatusBarViewModelFactory {
+        override fun create(displayId: Int): HomeStatusBarViewModelImpl
+    }
 }
 
 /** Lookup the color for a given view in the status bar */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
index 31cae79..81d06a8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
@@ -32,6 +32,7 @@
 
 import androidx.annotation.VisibleForTesting;
 
+import com.android.app.tracing.coroutines.TrackTracer;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
@@ -241,7 +242,7 @@
 
     private void setKeyguardFadingAway(boolean keyguardFadingAway) {
         if (mKeyguardFadingAway != keyguardFadingAway) {
-            Trace.traceCounter(Trace.TRACE_TAG_APP, "keyguardFadingAway",
+            TrackTracer.instantForGroup("keyguard", "FadingAway",
                     keyguardFadingAway ? 1 : 0);
             mKeyguardFadingAway = keyguardFadingAway;
             invokeForEachCallback(Callback::onKeyguardFadingAwayChanged);
@@ -356,7 +357,7 @@
     @Override
     public void notifyKeyguardGoingAway(boolean keyguardGoingAway) {
         if (mKeyguardGoingAway != keyguardGoingAway) {
-            Trace.traceCounter(Trace.TRACE_TAG_APP, "keyguardGoingAway",
+            Trace.traceCounter(Trace.TRACE_TAG_APP, "keyguard##GoingAway",
                     keyguardGoingAway ? 1 : 0);
             mKeyguardGoingAway = keyguardGoingAway;
             mKeyguardInteractorLazy.get().setIsKeyguardGoingAway(keyguardGoingAway);
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 12ed647..fdc2d8d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/domain/interactor/ZenModeInteractor.kt
@@ -16,8 +16,8 @@
 
 package com.android.systemui.statusbar.policy.domain.interactor
 
-import android.app.NotificationManager.INTERRUPTION_FILTER_NONE
 import android.content.Context
+import android.media.AudioManager
 import android.provider.Settings
 import android.provider.Settings.Secure.ZEN_DURATION_FOREVER
 import android.provider.Settings.Secure.ZEN_DURATION_PROMPT
@@ -29,6 +29,7 @@
 import com.android.settingslib.notification.modes.ZenIcon
 import com.android.settingslib.notification.modes.ZenIconLoader
 import com.android.settingslib.notification.modes.ZenMode
+import com.android.settingslib.volume.shared.model.AudioStream
 import com.android.systemui.dagger.qualifiers.Background
 import com.android.systemui.modes.shared.ModesUi
 import com.android.systemui.shared.notifications.data.repository.NotificationSettingsRepository
@@ -67,6 +68,17 @@
     deviceProvisioningRepository: DeviceProvisioningRepository,
     userSetupRepository: UserSetupRepository,
 ) {
+    /**
+     * List of predicates to determine if the [ZenMode] blocks an audio stream. Typical use case
+     * would be: `zenModeByStreamPredicates[stream](zenMode)`
+     */
+    private val zenModeByStreamPredicates =
+        mapOf<Int, (ZenMode) -> Boolean>(
+            AudioManager.STREAM_MUSIC to { it.policy.priorityCategoryMedia == STATE_DISALLOW },
+            AudioManager.STREAM_ALARM to { it.policy.priorityCategoryAlarms == STATE_DISALLOW },
+            AudioManager.STREAM_SYSTEM to { it.policy.priorityCategorySystem == STATE_DISALLOW },
+        )
+
     val isZenAvailable: Flow<Boolean> =
         combine(
             deviceProvisioningRepository.isDeviceProvisioned,
@@ -125,21 +137,16 @@
             .flowOn(bgDispatcher)
             .distinctUntilChanged()
 
-    val activeModesBlockingEverything: Flow<ActiveZenModes> = getFilteredActiveModesFlow { mode ->
-        mode.interruptionFilter == INTERRUPTION_FILTER_NONE
-    }
+    fun canBeBlockedByZenMode(stream: AudioStream): Boolean =
+        zenModeByStreamPredicates.containsKey(stream.value)
 
-    val activeModesBlockingMedia: Flow<ActiveZenModes> = getFilteredActiveModesFlow { mode ->
-        mode.policy.priorityCategoryMedia == STATE_DISALLOW
-    }
-
-    val activeModesBlockingAlarms: Flow<ActiveZenModes> = getFilteredActiveModesFlow { mode ->
-        mode.policy.priorityCategoryAlarms == STATE_DISALLOW
-    }
-
-    private fun getFilteredActiveModesFlow(predicate: (ZenMode) -> Boolean): Flow<ActiveZenModes> {
+    fun activeModesBlockingStream(stream: AudioStream): Flow<ActiveZenModes> {
+        val isBlockingStream = zenModeByStreamPredicates[stream.value]
+        require(isBlockingStream != null) {
+            "$stream is unsupported. Use canBeBlockedByZenMode to check if the stream can be affected by the Zen Mode."
+        }
         return modes
-            .map { modes -> modes.filter { mode -> predicate(mode) } }
+            .map { modes -> modes.filter { isBlockingStream(it) } }
             .map { modes -> buildActiveZenModes(modes) }
             .flowOn(bgDispatcher)
             .distinctUntilChanged()
@@ -194,7 +201,6 @@
                         )
                         null
                     }
-
                     ZEN_DURATION_FOREVER -> null
                     else -> Duration.ofMinutes(zenDuration.toLong())
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.kt
index 1e043ec..ecfcb29 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.kt
@@ -23,7 +23,7 @@
 import com.android.systemui.animation.ActivityTransitionAnimator
 import com.android.systemui.fragments.FragmentHostManager
 import com.android.systemui.statusbar.data.repository.StatusBarConfigurationController
-import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
+import com.android.systemui.statusbar.layout.StatusBarContentInsetsProvider
 import java.util.Optional
 
 /** Encapsulates all logic for the status bar window state management. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerImpl.java
index 811a2ec..8518acb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerImpl.java
@@ -58,7 +58,7 @@
 import com.android.systemui.statusbar.core.StatusBarConnectedDisplays;
 import com.android.systemui.statusbar.core.StatusBarRootModernization;
 import com.android.systemui.statusbar.data.repository.StatusBarConfigurationController;
-import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider;
+import com.android.systemui.statusbar.layout.StatusBarContentInsetsProvider;
 import com.android.systemui.statusbar.window.StatusBarWindowModule.InternalWindowViewInflater;
 import com.android.systemui.unfold.UnfoldTransitionProgressProvider;
 import com.android.systemui.unfold.util.JankMonitorTransitionProgressListener;
@@ -163,7 +163,18 @@
         mLp = getBarLayoutParams(mContext.getDisplay().getRotation());
         Trace.endSection();
 
-        mWindowManager.addView(mStatusBarWindowView, mLp);
+        try {
+            mWindowManager.addView(mStatusBarWindowView, mLp);
+        } catch (WindowManager.InvalidDisplayException e) {
+            // Wrapping this in a try/catch to avoid crashes when a display is instantly removed
+            // after being added, and initialization hasn't finished yet.
+            Log.e(
+                    TAG,
+                    "Unable to add view to WindowManager. Display with id "
+                            + mContext.getDisplayId()
+                            + " doesn't exist anymore.",
+                    e);
+        }
         mLpChanged.copyFrom(mLp);
 
         mContentInsetsProvider.addCallback(this::calculateStatusBarLocationsForAllRotations);
@@ -176,7 +187,15 @@
     public void stop() {
         StatusBarConnectedDisplays.assertInNewMode();
 
-        mWindowManager.removeView(mStatusBarWindowView);
+        try {
+            mWindowManager.removeView(mStatusBarWindowView);
+        } catch (IllegalArgumentException e) {
+            // Wrapping this in a try/catch to avoid crashes when a display is instantly removed
+            // after being added, and initialization hasn't finished yet.
+            // When that happens, adding the View to WindowManager fails, and therefore removing
+            // it here will fail too, since it wasn't added in the first place.
+            Log.e(TAG, "Failed to remove View from WindowManager. View was not attached", e);
+        }
 
         if (StatusBarRootModernization.isEnabled()) {
             return;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStore.kt
index 7403161..f7688d2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStore.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowControllerStore.kt
@@ -54,19 +54,23 @@
         StatusBarConnectedDisplays.assertInNewMode()
     }
 
-    override fun createInstanceForDisplay(displayId: Int): StatusBarWindowController {
+    override fun createInstanceForDisplay(displayId: Int): StatusBarWindowController? {
         val statusBarDisplayContext =
             displayWindowPropertiesRepository.get(
                 displayId = displayId,
                 windowType = WindowManager.LayoutParams.TYPE_STATUS_BAR,
-            )
+            ) ?: return null
+        val statusBarConfigurationController =
+            statusBarConfigurationControllerStore.forDisplay(displayId) ?: return null
+        val contentInsetsProvider =
+            statusBarContentInsetsProviderStore.forDisplay(displayId) ?: return null
         val viewCaptureAwareWindowManager =
             viewCaptureAwareWindowManagerFactory.create(statusBarDisplayContext.windowManager)
         return controllerFactory.create(
             statusBarDisplayContext.context,
             viewCaptureAwareWindowManager,
-            statusBarConfigurationControllerStore.forDisplay(displayId),
-            statusBarContentInsetsProviderStore.forDisplay(displayId),
+            statusBarConfigurationController,
+            contentInsetsProvider,
         )
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStatePerDisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStatePerDisplayRepository.kt
index bef8c84..460650a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStatePerDisplayRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/data/repository/StatusBarWindowStatePerDisplayRepository.kt
@@ -24,7 +24,7 @@
 import android.app.StatusBarManager.WindowVisibleState
 import com.android.systemui.dagger.qualifiers.Application
 import com.android.systemui.statusbar.CommandQueue
-import com.android.systemui.statusbar.window.data.model.StatusBarWindowState
+import com.android.systemui.statusbar.window.shared.model.StatusBarWindowState
 import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
 import dagger.assisted.Assisted
 import dagger.assisted.AssistedFactory
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/shared/model/StatusBarWindowState.kt b/packages/SystemUI/src/com/android/systemui/statusbar/window/shared/model/StatusBarWindowState.kt
index a99046e..5c7e66f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/window/shared/model/StatusBarWindowState.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/shared/model/StatusBarWindowState.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.statusbar.window.data.model
+package com.android.systemui.statusbar.window.shared.model
 
 /**
  * Represents the state of the status bar *window* as a whole (as opposed to individual views within
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/TouchpadTutorialModule.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/TouchpadTutorialModule.kt
index a6c0665..c43f31b 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/TouchpadTutorialModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/TouchpadTutorialModule.kt
@@ -30,9 +30,15 @@
 import com.android.systemui.touchpad.tutorial.ui.gesture.VelocityTracker
 import com.android.systemui.touchpad.tutorial.ui.gesture.VerticalVelocityTracker
 import com.android.systemui.touchpad.tutorial.ui.view.TouchpadTutorialActivity
+import com.android.systemui.touchpad.tutorial.ui.viewmodel.BackGestureRecognizerProvider
 import com.android.systemui.touchpad.tutorial.ui.viewmodel.BackGestureScreenViewModel
 import com.android.systemui.touchpad.tutorial.ui.viewmodel.EasterEggGestureViewModel
+import com.android.systemui.touchpad.tutorial.ui.viewmodel.EasterEggRecognizerProvider
+import com.android.systemui.touchpad.tutorial.ui.viewmodel.GestureRecognizerAdapter
+import com.android.systemui.touchpad.tutorial.ui.viewmodel.HomeGestureRecognizerProvider
 import com.android.systemui.touchpad.tutorial.ui.viewmodel.HomeGestureScreenViewModel
+import com.android.systemui.touchpad.tutorial.ui.viewmodel.RecentAppsGestureRecognizerProvider
+import com.android.systemui.touchpad.tutorial.ui.viewmodel.RecentAppsGestureScreenViewModel
 import dagger.Binds
 import dagger.Module
 import dagger.Provides
@@ -53,14 +59,46 @@
         fun touchpadScreensProvider(
             backGestureScreenViewModel: BackGestureScreenViewModel,
             homeGestureScreenViewModel: HomeGestureScreenViewModel,
+            easterEggGestureViewModel: EasterEggGestureViewModel,
         ): TouchpadTutorialScreensProvider {
             return ScreensProvider(
                 backGestureScreenViewModel,
                 homeGestureScreenViewModel,
-                EasterEggGestureViewModel(),
+                easterEggGestureViewModel,
             )
         }
 
+        @Provides
+        fun recentAppsViewModel(
+            recognizerProvider: RecentAppsGestureRecognizerProvider,
+            adapterFactory: GestureRecognizerAdapter.Factory,
+        ): RecentAppsGestureScreenViewModel {
+            return RecentAppsGestureScreenViewModel(adapterFactory.create(recognizerProvider))
+        }
+
+        @Provides
+        fun backViewModel(
+            recognizerProvider: BackGestureRecognizerProvider,
+            adapterFactory: GestureRecognizerAdapter.Factory,
+        ): BackGestureScreenViewModel {
+            return BackGestureScreenViewModel(adapterFactory.create(recognizerProvider))
+        }
+
+        @Provides
+        fun homeViewModel(
+            recognizerProvider: HomeGestureRecognizerProvider,
+            adapterFactory: GestureRecognizerAdapter.Factory,
+        ): HomeGestureScreenViewModel {
+            return HomeGestureScreenViewModel(adapterFactory.create(recognizerProvider))
+        }
+
+        @Provides
+        fun easterEggViewModel(
+            adapterFactory: GestureRecognizerAdapter.Factory
+        ): EasterEggGestureViewModel {
+            return EasterEggGestureViewModel(adapterFactory.create(EasterEggRecognizerProvider()))
+        }
+
         @SysUISingleton
         @Provides
         fun touchpadGesturesInteractor(
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 ae32b7a..bce55cb 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
@@ -50,7 +50,7 @@
         )
     GestureTutorialScreen(
         screenConfig = screenConfig,
-        gestureUiStateFlow = viewModel.gestureUiState,
+        tutorialStateFlow = viewModel.tutorialState,
         motionEventConsumer = {
             easterEggGestureViewModel.accept(it)
             viewModel.handleEvent(it)
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 73c54af..284e23e 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
@@ -18,7 +18,6 @@
 
 import android.view.MotionEvent
 import androidx.activity.compose.BackHandler
-import androidx.annotation.RawRes
 import androidx.compose.animation.core.Animatable
 import androidx.compose.animation.core.tween
 import androidx.compose.foundation.layout.Box
@@ -27,77 +26,21 @@
 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.graphics.graphicsLayer
 import androidx.compose.ui.input.pointer.pointerInteropFilter
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
 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.TutorialActionState.NotStarted
 import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialScreenConfig
-import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState.Finished
-import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState.NotStarted
-import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState
 import kotlinx.coroutines.flow.Flow
 
-sealed interface GestureUiState {
-    data object NotStarted : GestureUiState
-
-    data class Finished(@RawRes val successAnimation: Int) : GestureUiState
-
-    data class InProgress(
-        val progress: Float = 0f,
-        val progressStartMarker: String,
-        val progressEndMarker: String,
-    ) : GestureUiState
-
-    data object Error : GestureUiState
-}
-
-fun GestureState.toGestureUiState(
-    progressStartMarker: String,
-    progressEndMarker: String,
-    successAnimation: Int,
-): GestureUiState {
-    return when (this) {
-        GestureState.NotStarted -> NotStarted
-        is GestureState.InProgress ->
-            GestureUiState.InProgress(this.progress, progressStartMarker, progressEndMarker)
-        is GestureState.Finished -> GestureUiState.Finished(successAnimation)
-        GestureState.Error -> GestureUiState.Error
-    }
-}
-
-fun GestureUiState.toTutorialActionState(previousState: TutorialActionState): TutorialActionState {
-    return when (this) {
-        NotStarted -> TutorialActionState.NotStarted
-        is GestureUiState.InProgress -> {
-            val inProgress =
-                TutorialActionState.InProgress(
-                    progress = progress,
-                    startMarker = progressStartMarker,
-                    endMarker = progressEndMarker,
-                )
-            if (
-                previousState is TutorialActionState.InProgressAfterError ||
-                    previousState is TutorialActionState.Error
-            ) {
-                return TutorialActionState.InProgressAfterError(inProgress)
-            } else {
-                return inProgress
-            }
-        }
-        is Finished -> TutorialActionState.Finished(successAnimation)
-        GestureUiState.Error -> TutorialActionState.Error
-    }
-}
-
 @Composable
 fun GestureTutorialScreen(
     screenConfig: TutorialScreenConfig,
-    gestureUiStateFlow: Flow<GestureUiState>,
+    tutorialStateFlow: Flow<TutorialActionState>,
     motionEventConsumer: (MotionEvent) -> Boolean,
     easterEggTriggeredFlow: Flow<Boolean>,
     onEasterEggFinished: () -> Unit,
@@ -106,25 +49,21 @@
 ) {
     BackHandler(onBack = onBack)
     val easterEggTriggered by easterEggTriggeredFlow.collectAsStateWithLifecycle(false)
-    val gestureState by gestureUiStateFlow.collectAsStateWithLifecycle(NotStarted)
+    val tutorialState by tutorialStateFlow.collectAsStateWithLifecycle(NotStarted)
     TouchpadGesturesHandlingBox(
         motionEventConsumer,
-        gestureState,
+        tutorialState,
         easterEggTriggered,
         onEasterEggFinished,
     ) {
-        var lastState: TutorialActionState by remember {
-            mutableStateOf(TutorialActionState.NotStarted)
-        }
-        lastState = gestureState.toTutorialActionState(lastState)
-        ActionTutorialContent(lastState, onDoneButtonClicked, screenConfig)
+        ActionTutorialContent(tutorialState, onDoneButtonClicked, screenConfig)
     }
 }
 
 @Composable
 private fun TouchpadGesturesHandlingBox(
     motionEventConsumer: (MotionEvent) -> Boolean,
-    gestureState: GestureUiState,
+    tutorialState: TutorialActionState,
     easterEggTriggered: Boolean,
     onEasterEggFinished: () -> Unit,
     modifier: Modifier = Modifier,
@@ -150,7 +89,7 @@
                 .pointerInteropFilter(
                     onTouchEvent = { event ->
                         // FINISHED is the final state so we don't need to process touches anymore
-                        if (gestureState is Finished) {
+                        if (tutorialState is TutorialActionState.Finished) {
                             false
                         } else {
                             motionEventConsumer(event)
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 4f1f40d..4acdb60 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
@@ -49,7 +49,7 @@
         )
     GestureTutorialScreen(
         screenConfig = screenConfig,
-        gestureUiStateFlow = viewModel.gestureUiState,
+        tutorialStateFlow = viewModel.tutorialState,
         motionEventConsumer = {
             easterEggGestureViewModel.accept(it)
             viewModel.handleEvent(it)
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/RecentAppsGestureTutorialScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/RecentAppsGestureTutorialScreen.kt
index 6c9e26c..8dd53a7 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/RecentAppsGestureTutorialScreen.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/composable/RecentAppsGestureTutorialScreen.kt
@@ -50,7 +50,7 @@
         )
     GestureTutorialScreen(
         screenConfig = screenConfig,
-        gestureUiStateFlow = viewModel.gestureUiState,
+        tutorialStateFlow = viewModel.tutorialState,
         motionEventConsumer = {
             easterEggGestureViewModel.accept(it)
             viewModel.handleEvent(it)
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureFlowAdapter.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureFlowAdapter.kt
deleted file mode 100644
index 23e31b0..0000000
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/GestureFlowAdapter.kt
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.touchpad.tutorial.ui.gesture
-
-import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
-import kotlinx.coroutines.channels.awaitClose
-import kotlinx.coroutines.flow.Flow
-
-class GestureFlowAdapter(gestureRecognizer: GestureRecognizer) {
-
-    val gestureStateAsFlow: Flow<GestureState> = conflatedCallbackFlow {
-        val callback: (GestureState) -> Unit = { trySend(it) }
-        gestureRecognizer.addGestureStateCallback(callback)
-        awaitClose { gestureRecognizer.clearGestureStateCallback() }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadEventsFilter.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadEventsFilter.kt
index bddeb0b..b4b8ff0 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadEventsFilter.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/gesture/TouchpadEventsFilter.kt
@@ -20,6 +20,7 @@
 import android.view.MotionEvent
 import android.view.MotionEvent.ACTION_DOWN
 import android.view.MotionEvent.BUTTON_PRIMARY
+import com.android.systemui.touchpad.tutorial.ui.viewmodel.GestureRecognizerAdapter
 
 object TouchpadEventsFilter {
 
@@ -42,3 +43,12 @@
         false
     }
 }
+
+fun GestureRecognizerAdapter.handleTouchpadMotionEvent(event: MotionEvent): Boolean {
+    return if (TouchpadEventsFilter.isTouchpadAndNonClickEvent(event)) {
+        this.accept(event)
+        true
+    } else {
+        false
+    }
+}
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 cefe382..3264300 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
@@ -58,6 +58,7 @@
     private val backGestureViewModel: BackGestureScreenViewModel,
     private val homeGestureViewModel: HomeGestureScreenViewModel,
     private val recentAppsGestureViewModel: RecentAppsGestureScreenViewModel,
+    private val easterEggGestureViewModel: EasterEggGestureViewModel,
 ) : ComponentActivity() {
 
     private val tutorialViewModel by
@@ -74,7 +75,7 @@
                     backGestureViewModel,
                     homeGestureViewModel,
                     recentAppsGestureViewModel,
-                    EasterEggGestureViewModel(),
+                    easterEggGestureViewModel,
                     closeTutorial = ::finishTutorial,
                 )
             }
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/BackGestureRecognizerProvider.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/BackGestureRecognizerProvider.kt
new file mode 100644
index 0000000..b089882
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/BackGestureRecognizerProvider.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.touchpad.tutorial.ui.viewmodel
+
+import com.android.systemui.touchpad.tutorial.ui.gesture.BackGestureRecognizer
+import com.android.systemui.touchpad.tutorial.ui.gesture.GestureRecognizer
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+
+class BackGestureRecognizerProvider @Inject constructor(val resources: TouchpadGestureResources) :
+    GestureRecognizerProvider {
+
+    override val recognizer: Flow<GestureRecognizer> =
+        resources.distanceThreshold().map { distance ->
+            BackGestureRecognizer(gestureDistanceThresholdPx = distance)
+        }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/BackGestureScreenViewModel.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/BackGestureScreenViewModel.kt
index 93e8d31..7a3d4d1 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/BackGestureScreenViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/BackGestureScreenViewModel.kt
@@ -17,55 +17,39 @@
 package com.android.systemui.touchpad.tutorial.ui.viewmodel
 
 import android.view.MotionEvent
-import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState
 import com.android.systemui.res.R
-import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState
-import com.android.systemui.touchpad.tutorial.ui.composable.toGestureUiState
-import com.android.systemui.touchpad.tutorial.ui.gesture.BackGestureRecognizer
 import com.android.systemui.touchpad.tutorial.ui.gesture.GestureDirection
-import com.android.systemui.touchpad.tutorial.ui.gesture.GestureFlowAdapter
 import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState
 import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.InProgress
+import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.NotStarted
 import com.android.systemui.touchpad.tutorial.ui.gesture.handleTouchpadMotionEvent
 import com.android.systemui.util.kotlin.pairwiseBy
-import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.flatMapLatest
 
-class BackGestureScreenViewModel
-@Inject
-constructor(configurationInteractor: ConfigurationInteractor) : TouchpadTutorialScreenViewModel {
+class BackGestureScreenViewModel(val gestureRecognizer: GestureRecognizerAdapter) :
+    TouchpadTutorialScreenViewModel {
 
-    private var recognizer: BackGestureRecognizer? = null
-
-    private val distanceThreshold: Flow<Int> =
-        configurationInteractor
-            .dimensionPixelSize(R.dimen.touchpad_tutorial_gestures_distance_threshold)
-            .distinctUntilChanged()
-
-    @OptIn(ExperimentalCoroutinesApi::class)
-    override val gestureUiState: Flow<GestureUiState> =
-        distanceThreshold
-            .flatMapLatest {
-                recognizer = BackGestureRecognizer(gestureDistanceThresholdPx = it)
-                GestureFlowAdapter(recognizer!!).gestureStateAsFlow
+    override val tutorialState: Flow<TutorialActionState> =
+        gestureRecognizer.gestureState
+            .pairwiseBy(NotStarted) { previous, current ->
+                current to toAnimationProperties(current, previous)
             }
-            .pairwiseBy(GestureState.NotStarted) { previous, current ->
-                toGestureUiState(current, previous)
-            }
+            .mapToTutorialState()
 
     override fun handleEvent(event: MotionEvent): Boolean {
-        return recognizer?.handleTouchpadMotionEvent(event) ?: false
+        return gestureRecognizer.handleTouchpadMotionEvent(event)
     }
 
-    private fun toGestureUiState(current: GestureState, previous: GestureState): GestureUiState {
+    private fun toAnimationProperties(
+        current: GestureState,
+        previous: GestureState,
+    ): TutorialAnimationProperties {
         val (startMarker, endMarker) =
             if (current is InProgress && current.direction == GestureDirection.LEFT) {
                 "gesture to L" to "end progress L"
             } else "gesture to R" to "end progress R"
-        return current.toGestureUiState(
+        return TutorialAnimationProperties(
             progressStartMarker = startMarker,
             progressEndMarker = endMarker,
             successAnimation = successAnimation(previous),
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/EasterEggGestureViewModel.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/EasterEggGestureViewModel.kt
index 69cdab6..9ca456d 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/EasterEggGestureViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/EasterEggGestureViewModel.kt
@@ -17,8 +17,6 @@
 package com.android.systemui.touchpad.tutorial.ui.viewmodel
 
 import android.view.MotionEvent
-import com.android.systemui.touchpad.tutorial.ui.gesture.EasterEggGestureRecognizer
-import com.android.systemui.touchpad.tutorial.ui.gesture.GestureFlowAdapter
 import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState
 import com.android.systemui.touchpad.tutorial.ui.gesture.handleTouchpadMotionEvent
 import java.util.function.Consumer
@@ -29,14 +27,10 @@
 import kotlinx.coroutines.flow.onStart
 import kotlinx.coroutines.flow.receiveAsFlow
 
-class EasterEggGestureViewModel(
-    private val gestureRecognizer: EasterEggGestureRecognizer = EasterEggGestureRecognizer()
-) : Consumer<MotionEvent> {
+class EasterEggGestureViewModel(val gestureRecognizer: GestureRecognizerAdapter) :
+    Consumer<MotionEvent> {
 
-    private val gestureDone =
-        GestureFlowAdapter(gestureRecognizer).gestureStateAsFlow.filter {
-            it == GestureState.Finished
-        }
+    private val gestureDone = gestureRecognizer.gestureState.filter { it == GestureState.Finished }
 
     private val easterEggFinished = Channel<Unit>()
 
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/EasterEggRecognizerProvider.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/EasterEggRecognizerProvider.kt
new file mode 100644
index 0000000..c48ccb5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/EasterEggRecognizerProvider.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.touchpad.tutorial.ui.viewmodel
+
+import com.android.systemui.touchpad.tutorial.ui.gesture.EasterEggGestureRecognizer
+import com.android.systemui.touchpad.tutorial.ui.gesture.GestureRecognizer
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+
+class EasterEggRecognizerProvider : GestureRecognizerProvider {
+
+    override val recognizer: Flow<GestureRecognizer> =
+        MutableStateFlow(EasterEggGestureRecognizer())
+}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/GestureRecognizerAdapter.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/GestureRecognizerAdapter.kt
new file mode 100644
index 0000000..8e7375f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/GestureRecognizerAdapter.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.touchpad.tutorial.ui.viewmodel
+
+import android.view.MotionEvent
+import com.android.systemui.inputdevice.tutorial.InputDeviceTutorialLogger
+import com.android.systemui.touchpad.tutorial.ui.gesture.GestureRecognizer
+import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState
+import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+import java.util.function.Consumer
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flatMapLatest
+
+/**
+ * Adapter for [GestureRecognizer] exposing [GestureState] as Flow and ensuring that motion events
+ * are always handled by latest [GestureRecognizer].
+ */
+class GestureRecognizerAdapter
+@AssistedInject
+constructor(
+    @Assisted provider: GestureRecognizerProvider,
+    private val logger: InputDeviceTutorialLogger,
+) : Consumer<MotionEvent> {
+
+    private var gestureRecognizer: GestureRecognizer? = null
+
+    @OptIn(ExperimentalCoroutinesApi::class)
+    val gestureState: Flow<GestureState> =
+        provider.recognizer.flatMapLatest {
+            gestureRecognizer = it
+            gestureStateAsFlow(it)
+        }
+
+    override fun accept(event: MotionEvent) {
+        if (gestureRecognizer == null) {
+            logger.w("sending MotionEvent before gesture recognizer is initialized")
+        } else {
+            gestureRecognizer?.accept(event)
+        }
+    }
+
+    private fun gestureStateAsFlow(recognizer: GestureRecognizer): Flow<GestureState> =
+        conflatedCallbackFlow {
+            val callback: (GestureState) -> Unit = { trySend(it) }
+            recognizer.addGestureStateCallback(callback)
+            awaitClose { recognizer.clearGestureStateCallback() }
+        }
+
+    @AssistedFactory
+    interface Factory {
+        fun create(provider: GestureRecognizerProvider): GestureRecognizerAdapter
+    }
+}
diff --git a/media/java/android/media/quality/ParamCapability.aidl b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/GestureRecognizerProvider.kt
similarity index 63%
copy from media/java/android/media/quality/ParamCapability.aidl
copy to packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/GestureRecognizerProvider.kt
index b43409d..585bc0c 100644
--- a/media/java/android/media/quality/ParamCapability.aidl
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/GestureRecognizerProvider.kt
@@ -14,6 +14,12 @@
  * limitations under the License.
  */
 
-package android.media.quality;
+package com.android.systemui.touchpad.tutorial.ui.viewmodel
 
-parcelable ParamCapability;
+import com.android.systemui.touchpad.tutorial.ui.gesture.GestureRecognizer
+import kotlinx.coroutines.flow.Flow
+
+/** Observes state of the system and provides always up-to-date [GestureRecognizer] */
+interface GestureRecognizerProvider {
+    val recognizer: Flow<GestureRecognizer>
+}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/HomeGestureRecognizerProvider.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/HomeGestureRecognizerProvider.kt
new file mode 100644
index 0000000..6d818a6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/HomeGestureRecognizerProvider.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.touchpad.tutorial.ui.viewmodel
+
+import com.android.systemui.res.R
+import com.android.systemui.touchpad.tutorial.ui.gesture.GestureRecognizer
+import com.android.systemui.touchpad.tutorial.ui.gesture.HomeGestureRecognizer
+import com.android.systemui.touchpad.tutorial.ui.gesture.VelocityTracker
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.map
+
+class HomeGestureRecognizerProvider
+@Inject
+constructor(val resources: TouchpadGestureResources, val velocityTracker: VelocityTracker) :
+    GestureRecognizerProvider {
+
+    override val recognizer: Flow<GestureRecognizer> =
+        resources
+            .distanceThreshold()
+            .combine(
+                resources.velocityThreshold(R.dimen.touchpad_home_gesture_velocity_threshold),
+                { distance, velocity -> distance to velocity },
+            )
+            .map { (distance, velocity) ->
+                HomeGestureRecognizer(
+                    gestureDistanceThresholdPx = distance,
+                    velocityThresholdPxPerMs = velocity,
+                    velocityTracker = velocityTracker,
+                )
+            }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/HomeGestureScreenViewModel.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/HomeGestureScreenViewModel.kt
index 9a817d8..c75d44f 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/HomeGestureScreenViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/HomeGestureScreenViewModel.kt
@@ -16,70 +16,29 @@
 
 package com.android.systemui.touchpad.tutorial.ui.viewmodel
 
-import android.content.res.Resources
 import android.view.MotionEvent
-import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
-import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState
 import com.android.systemui.res.R
-import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState
-import com.android.systemui.touchpad.tutorial.ui.composable.toGestureUiState
-import com.android.systemui.touchpad.tutorial.ui.gesture.GestureFlowAdapter
-import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState
-import com.android.systemui.touchpad.tutorial.ui.gesture.HomeGestureRecognizer
-import com.android.systemui.touchpad.tutorial.ui.gesture.VelocityTracker
-import com.android.systemui.touchpad.tutorial.ui.gesture.VerticalVelocityTracker
 import com.android.systemui.touchpad.tutorial.ui.gesture.handleTouchpadMotionEvent
-import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.map
 
-class HomeGestureScreenViewModel
-@Inject
-constructor(
-    val configurationInteractor: ConfigurationInteractor,
-    @Main val resources: Resources,
-    val velocityTracker: VelocityTracker = VerticalVelocityTracker(),
-) : TouchpadTutorialScreenViewModel {
+class HomeGestureScreenViewModel(private val gestureRecognizer: GestureRecognizerAdapter) :
+    TouchpadTutorialScreenViewModel {
 
-    private var recognizer: HomeGestureRecognizer? = null
-
-    private val distanceThreshold: Flow<Int> =
-        configurationInteractor
-            .dimensionPixelSize(R.dimen.touchpad_tutorial_gestures_distance_threshold)
-            .distinctUntilChanged()
-
-    private val velocityThreshold: Flow<Float> =
-        configurationInteractor.onAnyConfigurationChange
-            .map { resources.getDimension(R.dimen.touchpad_home_gesture_velocity_threshold) }
-            .distinctUntilChanged()
-
-    @OptIn(ExperimentalCoroutinesApi::class)
-    override val gestureUiState: Flow<GestureUiState> =
-        distanceThreshold
-            .combine(velocityThreshold, { distance, velocity -> distance to velocity })
-            .flatMapLatest { (distance, velocity) ->
-                recognizer =
-                    HomeGestureRecognizer(
-                        gestureDistanceThresholdPx = distance,
-                        velocityThresholdPxPerMs = velocity,
-                        velocityTracker = velocityTracker,
+    override val tutorialState: Flow<TutorialActionState> =
+        gestureRecognizer.gestureState
+            .map {
+                it to
+                    TutorialAnimationProperties(
+                        progressStartMarker = "drag with gesture",
+                        progressEndMarker = "release playback realtime",
+                        successAnimation = R.raw.trackpad_home_success,
                     )
-                GestureFlowAdapter(recognizer!!).gestureStateAsFlow
             }
-            .map { toGestureUiState(it) }
-
-    private fun toGestureUiState(it: GestureState) =
-        it.toGestureUiState(
-            progressStartMarker = "drag with gesture",
-            progressEndMarker = "release playback realtime",
-            successAnimation = R.raw.trackpad_home_success,
-        )
+            .mapToTutorialState()
 
     override fun handleEvent(event: MotionEvent): Boolean {
-        return recognizer?.handleTouchpadMotionEvent(event) ?: false
+        return gestureRecognizer.handleTouchpadMotionEvent(event)
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/RecentAppsGestureRecognizerProvider.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/RecentAppsGestureRecognizerProvider.kt
new file mode 100644
index 0000000..3e0b434
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/RecentAppsGestureRecognizerProvider.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.touchpad.tutorial.ui.viewmodel
+
+import com.android.systemui.res.R
+import com.android.systemui.touchpad.tutorial.ui.gesture.GestureRecognizer
+import com.android.systemui.touchpad.tutorial.ui.gesture.RecentAppsGestureRecognizer
+import com.android.systemui.touchpad.tutorial.ui.gesture.VelocityTracker
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.map
+
+class RecentAppsGestureRecognizerProvider
+@Inject
+constructor(val resources: TouchpadGestureResources, val velocityTracker: VelocityTracker) :
+    GestureRecognizerProvider {
+
+    override val recognizer: Flow<GestureRecognizer> =
+        resources
+            .distanceThreshold()
+            .combine(
+                resources.velocityThreshold(
+                    R.dimen.touchpad_recent_apps_gesture_velocity_threshold
+                ),
+                { distance, velocity -> distance to velocity },
+            )
+            .map { (distance, velocity) ->
+                RecentAppsGestureRecognizer(
+                    gestureDistanceThresholdPx = distance,
+                    velocityThresholdPxPerMs = velocity,
+                    velocityTracker = velocityTracker,
+                )
+            }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/RecentAppsGestureScreenViewModel.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/RecentAppsGestureScreenViewModel.kt
index 8215078..9fab5f3 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/RecentAppsGestureScreenViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/RecentAppsGestureScreenViewModel.kt
@@ -16,74 +16,29 @@
 
 package com.android.systemui.touchpad.tutorial.ui.viewmodel
 
-import android.content.res.Resources
 import android.view.MotionEvent
-import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
-import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState
 import com.android.systemui.res.R
-import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState
-import com.android.systemui.touchpad.tutorial.ui.composable.toGestureUiState
-import com.android.systemui.touchpad.tutorial.ui.gesture.GestureFlowAdapter
-import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState
-import com.android.systemui.touchpad.tutorial.ui.gesture.RecentAppsGestureRecognizer
-import com.android.systemui.touchpad.tutorial.ui.gesture.VelocityTracker
-import com.android.systemui.touchpad.tutorial.ui.gesture.VerticalVelocityTracker
 import com.android.systemui.touchpad.tutorial.ui.gesture.handleTouchpadMotionEvent
-import javax.inject.Inject
-import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.flatMapLatest
 import kotlinx.coroutines.flow.map
 
-class RecentAppsGestureScreenViewModel
-@Inject
-constructor(
-    configurationInteractor: ConfigurationInteractor,
-    @Main private val resources: Resources,
-    private val velocityTracker: VelocityTracker = VerticalVelocityTracker(),
-) : TouchpadTutorialScreenViewModel {
+class RecentAppsGestureScreenViewModel(private val gestureRecognizer: GestureRecognizerAdapter) :
+    TouchpadTutorialScreenViewModel {
 
-    private var recognizer: RecentAppsGestureRecognizer? = null
-
-    private val distanceThreshold: Flow<Int> =
-        configurationInteractor.onAnyConfigurationChange
+    override val tutorialState: Flow<TutorialActionState> =
+        gestureRecognizer.gestureState
             .map {
-                resources.getDimensionPixelSize(
-                    R.dimen.touchpad_tutorial_gestures_distance_threshold
-                )
-            }
-            .distinctUntilChanged()
-
-    private val velocityThreshold: Flow<Float> =
-        configurationInteractor.onAnyConfigurationChange
-            .map { resources.getDimension(R.dimen.touchpad_recent_apps_gesture_velocity_threshold) }
-            .distinctUntilChanged()
-
-    @OptIn(ExperimentalCoroutinesApi::class)
-    override val gestureUiState: Flow<GestureUiState> =
-        distanceThreshold
-            .combine(velocityThreshold, { distance, velocity -> distance to velocity })
-            .flatMapLatest { (distance, velocity) ->
-                recognizer =
-                    RecentAppsGestureRecognizer(
-                        gestureDistanceThresholdPx = distance,
-                        velocityThresholdPxPerMs = velocity,
-                        velocityTracker = velocityTracker,
+                it to
+                    TutorialAnimationProperties(
+                        progressStartMarker = "drag with gesture",
+                        progressEndMarker = "onPause",
+                        successAnimation = R.raw.trackpad_recent_apps_success,
                     )
-                GestureFlowAdapter(recognizer!!).gestureStateAsFlow
             }
-            .map { toGestureUiState(it) }
-
-    private fun toGestureUiState(it: GestureState) =
-        it.toGestureUiState(
-            progressStartMarker = "drag with gesture",
-            progressEndMarker = "onPause",
-            successAnimation = R.raw.trackpad_recent_apps_success,
-        )
+            .mapToTutorialState()
 
     override fun handleEvent(event: MotionEvent): Boolean {
-        return recognizer?.handleTouchpadMotionEvent(event) ?: false
+        return gestureRecognizer.handleTouchpadMotionEvent(event)
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadGestureResources.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadGestureResources.kt
new file mode 100644
index 0000000..3d99bd8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadGestureResources.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.touchpad.tutorial.ui.viewmodel
+
+import android.content.res.Resources
+import androidx.annotation.DimenRes
+import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.res.R
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.map
+
+class TouchpadGestureResources
+@Inject
+constructor(val configurationInteractor: ConfigurationInteractor, @Main val resources: Resources) {
+
+    fun distanceThreshold(): Flow<Int> =
+        configurationInteractor.onAnyConfigurationChange
+            .map {
+                resources.getDimensionPixelSize(
+                    R.dimen.touchpad_tutorial_gestures_distance_threshold
+                )
+            }
+            .distinctUntilChanged()
+
+    fun velocityThreshold(@DimenRes resId: Int): Flow<Float> =
+        configurationInteractor.onAnyConfigurationChange
+            .map { resources.getDimension(resId) }
+            .distinctUntilChanged()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadTutorialScreenViewModel.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadTutorialScreenViewModel.kt
index 31e953d..3b6e3c7 100644
--- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadTutorialScreenViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/viewmodel/TouchpadTutorialScreenViewModel.kt
@@ -17,11 +17,62 @@
 package com.android.systemui.touchpad.tutorial.ui.viewmodel
 
 import android.view.MotionEvent
-import com.android.systemui.touchpad.tutorial.ui.composable.GestureUiState
+import androidx.annotation.RawRes
+import com.android.systemui.inputdevice.tutorial.ui.composable.TutorialActionState
+import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState
+import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.Finished
+import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.InProgress
+import com.android.systemui.touchpad.tutorial.ui.gesture.GestureState.NotStarted
 import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.flow
 
 interface TouchpadTutorialScreenViewModel {
-    val gestureUiState: Flow<GestureUiState>
+    val tutorialState: Flow<TutorialActionState>
 
     fun handleEvent(event: MotionEvent): Boolean
 }
+
+data class TutorialAnimationProperties(
+    val progressStartMarker: String,
+    val progressEndMarker: String,
+    @RawRes val successAnimation: Int,
+)
+
+fun Flow<Pair<GestureState, TutorialAnimationProperties>>.mapToTutorialState():
+    Flow<TutorialActionState> {
+    return flow<TutorialActionState> {
+        var lastState: TutorialActionState = TutorialActionState.NotStarted
+        collect { (gestureState, animationProperties) ->
+            val newState = gestureState.toTutorialActionState(animationProperties, lastState)
+            lastState = newState
+            emit(newState)
+        }
+    }
+}
+
+fun GestureState.toTutorialActionState(
+    properties: TutorialAnimationProperties,
+    previousState: TutorialActionState,
+): TutorialActionState {
+    return when (this) {
+        NotStarted -> TutorialActionState.NotStarted
+        is InProgress -> {
+            val inProgress =
+                TutorialActionState.InProgress(
+                    progress = progress,
+                    startMarker = properties.progressStartMarker,
+                    endMarker = properties.progressEndMarker,
+                )
+            if (
+                previousState is TutorialActionState.InProgressAfterError ||
+                    previousState is TutorialActionState.Error
+            ) {
+                TutorialActionState.InProgressAfterError(inProgress)
+            } else {
+                inProgress
+            }
+        }
+        is Finished -> TutorialActionState.Finished(properties.successAnimation)
+        GestureState.Error -> TutorialActionState.Error
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTraceLogger.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTraceLogger.kt
index 6597097..7d3966b 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTraceLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTraceLogger.kt
@@ -17,8 +17,9 @@
 
 import android.content.Context
 import android.hardware.devicestate.DeviceStateManager
-import android.os.Trace
 import com.android.app.tracing.TraceStateLogger
+import com.android.app.tracing.coroutines.TrackTracer
+import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.systemui.CoreStartable
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Application
@@ -29,7 +30,6 @@
 import javax.inject.Inject
 import kotlin.coroutines.CoroutineContext
 import kotlinx.coroutines.CoroutineScope
-import com.android.app.tracing.coroutines.launchTraced as launch
 import kotlinx.coroutines.plus
 
 /**
@@ -45,7 +45,7 @@
     @Application applicationScope: CoroutineScope,
     @Background private val coroutineContext: CoroutineContext,
     private val deviceStateRepository: DeviceStateRepository,
-    private val deviceStateManager: DeviceStateManager
+    private val deviceStateManager: DeviceStateManager,
 ) : CoreStartable {
     private val isFoldable: Boolean = isDeviceFoldable(context.resources, deviceStateManager)
 
@@ -61,7 +61,7 @@
 
         bgScope.launch {
             foldStateRepository.hingeAngle.collect {
-                Trace.traceCounter(Trace.TRACE_TAG_APP, "hingeAngle", it.toInt())
+                TrackTracer.instantForGroup("unfold", "hingeAngle", it.toInt())
             }
         }
         bgScope.launch {
diff --git a/packages/SystemUI/src/com/android/systemui/util/CarrierConfigTracker.java b/packages/SystemUI/src/com/android/systemui/util/CarrierConfigTracker.java
index f755feb..10a4f56 100644
--- a/packages/SystemUI/src/com/android/systemui/util/CarrierConfigTracker.java
+++ b/packages/SystemUI/src/com/android/systemui/util/CarrierConfigTracker.java
@@ -60,10 +60,6 @@
     private final Set<CarrierConfigChangedListener> mListeners = new ArraySet<>();
     private final Set<DefaultDataSubscriptionChangedListener> mDataListeners =
             new ArraySet<>();
-    private boolean mDefaultCallStrengthConfigLoaded;
-    private boolean mDefaultCallStrengthConfig;
-    private boolean mDefaultNoCallingConfigLoaded;
-    private boolean mDefaultNoCallingConfig;
     private boolean mDefaultCarrierProvisionsWifiMergedNetworksLoaded;
     private boolean mDefaultCarrierProvisionsWifiMergedNetworks;
     private boolean mDefaultShowOperatorNameConfigLoaded;
@@ -146,42 +142,6 @@
     }
 
     /**
-     * Returns the KEY_DISPLAY_CALL_STRENGTH_INDICATOR_BOOL value for the given subId.
-     */
-    public boolean getCallStrengthConfig(int subId) {
-        synchronized (mCallStrengthConfigs) {
-            if (mCallStrengthConfigs.indexOfKey(subId) >= 0) {
-                return mCallStrengthConfigs.get(subId);
-            }
-        }
-        if (!mDefaultCallStrengthConfigLoaded) {
-            mDefaultCallStrengthConfig =
-                    CarrierConfigManager.getDefaultConfig().getBoolean(
-                            CarrierConfigManager.KEY_DISPLAY_CALL_STRENGTH_INDICATOR_BOOL);
-            mDefaultCallStrengthConfigLoaded = true;
-        }
-        return mDefaultCallStrengthConfig;
-    }
-
-    /**
-     * Returns the KEY_USE_IP_FOR_CALLING_INDICATOR_BOOL value for the given subId.
-     */
-    public boolean getNoCallingConfig(int subId) {
-        synchronized (mNoCallingConfigs) {
-            if (mNoCallingConfigs.indexOfKey(subId) >= 0) {
-                return mNoCallingConfigs.get(subId);
-            }
-        }
-        if (!mDefaultNoCallingConfigLoaded) {
-            mDefaultNoCallingConfig =
-                    CarrierConfigManager.getDefaultConfig().getBoolean(
-                            CarrierConfigManager.KEY_USE_IP_FOR_CALLING_INDICATOR_BOOL);
-            mDefaultNoCallingConfigLoaded = true;
-        }
-        return mDefaultNoCallingConfig;
-    }
-
-    /**
      * Returns the KEY_CARRIER_PROVISIONS_WIFI_MERGED_NETWORKS_BOOL value for the given subId.
      */
     public boolean getCarrierProvisionsWifiMergedNetworksBool(int subId) {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractor.kt
index fa108842..3b0c8a6 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/domain/interactor/VolumeDialogCallbacksInteractor.kt
@@ -46,7 +46,7 @@
 constructor(
     private val volumeDialogController: VolumeDialogController,
     @VolumeDialogPlugin private val coroutineScope: CoroutineScope,
-    @Background private val bgHandler: Handler,
+    @Background private val bgHandler: Handler?,
 ) {
 
     @SuppressLint("SharedFlowCreation") // event-bus needed
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt
index 8bb0279..e8d19dd 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/binder/VolumeDialogRingerViewBinder.kt
@@ -71,6 +71,7 @@
 
     fun CoroutineScope.bind(view: View) {
         val volumeDialogBackgroundView = view.requireViewById<View>(R.id.volume_dialog_background)
+        val ringerBackgroundView = view.requireViewById<View>(R.id.ringer_buttons_background)
         val drawerContainer = view.requireViewById<MotionLayout>(R.id.volume_ringer_drawer)
         val unselectedButtonUiModel = RingerButtonUiModel.getUnselectedButton(view.context)
         val selectedButtonUiModel = RingerButtonUiModel.getSelectedButton(view.context)
@@ -82,12 +83,31 @@
             view.context.resources.getDimensionPixelSize(
                 R.dimen.volume_dialog_background_corner_radius
             )
+        val bottomDefaultRadius = volumeDialogBgFullRadius.toFloat()
+        val bottomCornerRadii =
+            floatArrayOf(
+                0F,
+                0F,
+                0F,
+                0F,
+                bottomDefaultRadius,
+                bottomDefaultRadius,
+                bottomDefaultRadius,
+                bottomDefaultRadius,
+            )
         var backgroundAnimationProgress: Float by
             Delegates.observable(0F) { _, _, progress ->
+                ringerBackgroundView.applyCorners(
+                    fullRadius = volumeDialogBgFullRadius,
+                    diff = volumeDialogBgFullRadius - volumeDialogBgSmallRadius,
+                    progress,
+                    isBottom = false,
+                )
                 volumeDialogBackgroundView.applyCorners(
                     fullRadius = volumeDialogBgFullRadius,
                     diff = volumeDialogBgFullRadius - volumeDialogBgSmallRadius,
                     progress,
+                    isBottom = true,
                 )
             }
         val ringerDrawerTransitionListener = VolumeDialogRingerDrawerTransitionListener {
@@ -95,6 +115,7 @@
         }
         drawerContainer.setTransitionListener(ringerDrawerTransitionListener)
         volumeDialogBackgroundView.background = volumeDialogBackgroundView.background.mutate()
+        ringerBackgroundView.background = ringerBackgroundView.background.mutate()
 
         viewModel.ringerViewModel
             .mapLatest { ringerState ->
@@ -106,6 +127,8 @@
                         drawerContainer.visibility = View.VISIBLE
                         when (uiModel.drawerState) {
                             is RingerDrawerState.Initial -> {
+                                (volumeDialogBackgroundView.background as GradientDrawable)
+                                    .cornerRadii = bottomCornerRadii
                                 drawerContainer.animateAndBindDrawerButtons(
                                     viewModel,
                                     uiModel,
@@ -114,6 +137,7 @@
                                 )
                                 ringerDrawerTransitionListener.setProgressChangeEnabled(true)
                                 drawerContainer.closeDrawer(
+                                    ringerBackgroundView,
                                     uiModel.currentButtonIndex,
                                     ringerState.orientation,
                                 )
@@ -156,6 +180,7 @@
                                             )
                                         }
                                         drawerContainer.closeDrawer(
+                                            ringerBackgroundView,
                                             uiModel.currentButtonIndex,
                                             ringerState.orientation,
                                         )
@@ -178,12 +203,18 @@
                                 } else {
                                     ringerDrawerTransitionListener.setProgressChangeEnabled(true)
                                 }
-                                updateOpenState(drawerContainer, ringerState.orientation)
+                                updateOpenState(
+                                    drawerContainer,
+                                    ringerState.orientation,
+                                    ringerBackgroundView,
+                                )
                                 drawerContainer.transitionToState(
                                     R.id.volume_dialog_ringer_drawer_open
                                 )
                                 volumeDialogBackgroundView.background =
                                     volumeDialogBackgroundView.background.mutate()
+                                ringerBackgroundView.background =
+                                    ringerBackgroundView.background.mutate()
                             }
                         }
                     }
@@ -214,14 +245,14 @@
         ) {
             val count = uiModel.availableButtons.size
             val selectedButton =
-                getChildAt(count - uiModel.currentButtonIndex - 1)
+                getChildAt(count - uiModel.currentButtonIndex)
                     .requireViewById<ImageButton>(R.id.volume_drawer_button)
             val previousIndex =
                 uiModel.availableButtons.indexOfFirst {
                     it?.ringerMode == uiModel.drawerState.previousMode
                 }
             val unselectedButton =
-                getChildAt(count - previousIndex - 1)
+                getChildAt(count - previousIndex)
                     .requireViewById<ImageButton>(R.id.volume_drawer_button)
             // We only need to execute on roundness animation end and volume dialog background
             // progress update once because these changes should be applied once on volume dialog
@@ -269,7 +300,7 @@
         val count = uiModel.availableButtons.size
         uiModel.availableButtons.fastForEachIndexed { index, ringerButton ->
             ringerButton?.let {
-                val view = getChildAt(count - index - 1)
+                val view = getChildAt(count - index)
                 val isOpen = uiModel.drawerState is RingerDrawerState.Open
                 if (index == uiModel.currentButtonIndex) {
                     view.bindDrawerButton(
@@ -322,7 +353,7 @@
     }
 
     private fun MotionLayout.ensureChildCount(@LayoutRes viewLayoutId: Int, count: Int) {
-        val childCountDelta = childCount - count
+        val childCountDelta = childCount - count - 1
         when {
             childCountDelta > 0 -> {
                 removeViews(0, childCountDelta)
@@ -337,9 +368,13 @@
         }
     }
 
-    private fun MotionLayout.closeDrawer(selectedIndex: Int, orientation: Int) {
+    private fun MotionLayout.closeDrawer(
+        ringerBackground: View,
+        selectedIndex: Int,
+        orientation: Int,
+    ) {
         setTransition(R.id.close_to_open_transition)
-        updateCloseState(this, selectedIndex, orientation)
+        updateCloseState(this, selectedIndex, orientation, ringerBackground)
         transitionToState(R.id.volume_dialog_ringer_drawer_close)
     }
 
@@ -382,8 +417,14 @@
         }
     }
 
-    private fun View.applyCorners(fullRadius: Int, diff: Int, progress: Float) {
-        (background as GradientDrawable).cornerRadius = fullRadius - progress * diff
+    private fun View.applyCorners(fullRadius: Int, diff: Int, progress: Float, isBottom: Boolean) {
+        val radius = fullRadius - progress * diff
+        (background as GradientDrawable).cornerRadii =
+            if (isBottom) {
+                floatArrayOf(0F, 0F, 0F, 0F, radius, radius, radius, radius)
+            } else {
+                FloatArray(8) { radius }
+            }
         background.invalidateSelf()
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/util/RingerDrawerConstraintsUtils.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/util/RingerDrawerConstraintsUtils.kt
index 25ba1bd..fb9884c 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/util/RingerDrawerConstraintsUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ringer/ui/util/RingerDrawerConstraintsUtils.kt
@@ -18,20 +18,28 @@
 
 import android.content.res.Configuration.ORIENTATION_LANDSCAPE
 import android.content.res.Configuration.ORIENTATION_PORTRAIT
+import android.util.TypedValue
 import android.view.View
 import androidx.constraintlayout.motion.widget.MotionLayout
 import androidx.constraintlayout.widget.ConstraintSet
 import com.android.systemui.res.R
 import com.android.systemui.util.children
 
-fun updateOpenState(ringerDrawer: MotionLayout, orientation: Int) {
+fun updateOpenState(ringerDrawer: MotionLayout, orientation: Int, ringerBackground: View) {
     val openSet = ringerDrawer.cloneConstraintSet(R.id.volume_dialog_ringer_drawer_open)
+    openSet.setVisibility(ringerBackground.id, View.VISIBLE)
     openSet.adjustOpenConstraintsForDrawer(ringerDrawer, orientation)
     ringerDrawer.updateState(R.id.volume_dialog_ringer_drawer_open, openSet)
 }
 
-fun updateCloseState(ringerDrawer: MotionLayout, selectedIndex: Int, orientation: Int) {
+fun updateCloseState(
+    ringerDrawer: MotionLayout,
+    selectedIndex: Int,
+    orientation: Int,
+    ringerBackground: View,
+) {
     val closeSet = ringerDrawer.cloneConstraintSet(R.id.volume_dialog_ringer_drawer_close)
+    closeSet.setVisibility(ringerBackground.id, View.VISIBLE)
     closeSet.adjustClosedConstraintsForDrawer(ringerDrawer, selectedIndex, orientation)
     ringerDrawer.updateState(R.id.volume_dialog_ringer_drawer_close, closeSet)
 }
@@ -41,17 +49,6 @@
     index: Int,
     button: View,
 ) {
-    if (motionLayout.getChildAt(index - 1) == null) {
-        connect(button.id, ConstraintSet.TOP, motionLayout.id, ConstraintSet.TOP)
-    } else {
-        connect(
-            button.id,
-            ConstraintSet.TOP,
-            motionLayout.getChildAt(index - 1).id,
-            ConstraintSet.BOTTOM,
-        )
-    }
-
     if (motionLayout.getChildAt(index + 1) == null) {
         connect(button.id, ConstraintSet.BOTTOM, motionLayout.id, ConstraintSet.BOTTOM)
     } else {
@@ -62,10 +59,7 @@
             ConstraintSet.TOP,
         )
     }
-    connect(button.id, ConstraintSet.START, motionLayout.id, ConstraintSet.START)
     connect(button.id, ConstraintSet.END, motionLayout.id, ConstraintSet.END)
-    clear(button.id, ConstraintSet.LEFT)
-    clear(button.id, ConstraintSet.RIGHT)
 }
 
 private fun ConstraintSet.setButtonPositionLandscapeConstraints(
@@ -73,81 +67,120 @@
     index: Int,
     button: View,
 ) {
-    if (motionLayout.getChildAt(index - 1) == null) {
-        connect(button.id, ConstraintSet.LEFT, motionLayout.id, ConstraintSet.LEFT)
-    } else {
-        connect(
-            button.id,
-            ConstraintSet.LEFT,
-            motionLayout.getChildAt(index - 1).id,
-            ConstraintSet.RIGHT,
-        )
-    }
     if (motionLayout.getChildAt(index + 1) == null) {
-        connect(button.id, ConstraintSet.RIGHT, motionLayout.id, ConstraintSet.RIGHT)
+        connect(button.id, ConstraintSet.END, motionLayout.id, ConstraintSet.END)
     } else {
         connect(
             button.id,
-            ConstraintSet.RIGHT,
+            ConstraintSet.END,
             motionLayout.getChildAt(index + 1).id,
-            ConstraintSet.LEFT,
+            ConstraintSet.START,
         )
     }
-    connect(button.id, ConstraintSet.TOP, motionLayout.id, ConstraintSet.TOP)
     connect(button.id, ConstraintSet.BOTTOM, motionLayout.id, ConstraintSet.BOTTOM)
-    clear(button.id, ConstraintSet.START)
-    clear(button.id, ConstraintSet.END)
+
+    // Index 1 is the first button in the children of motionLayout.
+    if (index == 1) {
+        clear(button.id, ConstraintSet.START)
+    }
 }
 
 private fun ConstraintSet.adjustOpenConstraintsForDrawer(
     motionLayout: MotionLayout,
     lastOrientation: Int,
 ) {
-    motionLayout.children.forEachIndexed { index, button ->
-        setAlpha(button.id, 1.0F)
-        constrainWidth(
-            button.id,
-            motionLayout.context.resources.getDimensionPixelSize(
-                R.dimen.volume_dialog_ringer_drawer_button_size
-            ),
-        )
-        constrainHeight(
-            button.id,
-            motionLayout.context.resources.getDimensionPixelSize(
-                R.dimen.volume_dialog_ringer_drawer_button_size
-            ),
-        )
-        when (lastOrientation) {
-            ORIENTATION_LANDSCAPE -> {
-                setButtonPositionLandscapeConstraints(motionLayout, index, button)
-                if (index != motionLayout.childCount - 1) {
-                    setMargin(
-                        button.id,
-                        ConstraintSet.RIGHT,
-                        motionLayout.context.resources.getDimensionPixelSize(
-                            R.dimen.volume_dialog_components_spacing
-                        ),
-                    )
-                } else {
-                    setMargin(button.id, ConstraintSet.RIGHT, 0)
+    motionLayout.children.forEachIndexed { index, view ->
+        if (view.id != R.id.ringer_buttons_background) {
+            setAlpha(view.id, 1.0F)
+            constrainWidth(
+                view.id,
+                motionLayout.context.resources.getDimensionPixelSize(
+                    R.dimen.volume_dialog_ringer_drawer_button_size
+                ),
+            )
+            constrainHeight(
+                view.id,
+                motionLayout.context.resources.getDimensionPixelSize(
+                    R.dimen.volume_dialog_ringer_drawer_button_size
+                ),
+            )
+            when (lastOrientation) {
+                ORIENTATION_LANDSCAPE -> {
+                    if (index == 1) {
+                        setMargin(
+                            view.id,
+                            ConstraintSet.START,
+                            motionLayout.context.resources.getDimensionPixelSize(
+                                R.dimen.volume_dialog_ringer_drawer_margin
+                            ),
+                        )
+                    }
+                    setButtonPositionLandscapeConstraints(motionLayout, index, view)
+                    if (index != motionLayout.childCount - 1) {
+                        setMargin(
+                            view.id,
+                            ConstraintSet.END,
+                            motionLayout.context.resources.getDimensionPixelSize(
+                                R.dimen.volume_dialog_components_spacing
+                            ),
+                        )
+                    } else {
+                        setMargin(view.id, ConstraintSet.END, 0)
+                    }
+                    setMargin(view.id, ConstraintSet.BOTTOM, 0)
                 }
-                setMargin(button.id, ConstraintSet.BOTTOM, 0)
-            }
-            ORIENTATION_PORTRAIT -> {
-                setButtonPositionPortraitConstraints(motionLayout, index, button)
-                if (index != motionLayout.childCount - 1) {
-                    setMargin(
-                        button.id,
-                        ConstraintSet.BOTTOM,
-                        motionLayout.context.resources.getDimensionPixelSize(
-                            R.dimen.volume_dialog_components_spacing
-                        ),
-                    )
-                } else {
-                    setMargin(button.id, ConstraintSet.BOTTOM, 0)
+
+                ORIENTATION_PORTRAIT -> {
+                    if (index == 1) {
+                        setMargin(view.id, ConstraintSet.START, 0)
+                    }
+                    setButtonPositionPortraitConstraints(motionLayout, index, view)
+                    if (index != motionLayout.childCount - 1) {
+                        setMargin(
+                            view.id,
+                            ConstraintSet.BOTTOM,
+                            motionLayout.context.resources.getDimensionPixelSize(
+                                R.dimen.volume_dialog_components_spacing
+                            ),
+                        )
+                    } else {
+                        setMargin(view.id, ConstraintSet.BOTTOM, 0)
+                    }
+                    setMargin(view.id, ConstraintSet.END, 0)
                 }
-                setMargin(button.id, ConstraintSet.RIGHT, 0)
             }
+        } else {
+            constrainWidth(
+                view.id,
+                when (lastOrientation) {
+                    ORIENTATION_LANDSCAPE ->
+                        (motionLayout.context.resources.getDimensionPixelSize(
+                            R.dimen.volume_dialog_ringer_drawer_button_size
+                        ) * (motionLayout.childCount - 1)) +
+                            (motionLayout.context.resources.getDimensionPixelSize(
+                                R.dimen.volume_dialog_ringer_drawer_margin
+                            ) * 2) +
+                            (motionLayout.context.resources.getDimensionPixelSize(
+                                R.dimen.volume_dialog_components_spacing
+                            ) * (motionLayout.childCount - 2))
+
+                    ORIENTATION_PORTRAIT ->
+                        motionLayout.context.resources.getDimensionPixelSize(
+                            R.dimen.volume_dialog_width
+                        )
+
+                    else -> 0
+                },
+            )
+            connect(view.id, ConstraintSet.BOTTOM, motionLayout.id, ConstraintSet.BOTTOM)
+            connect(
+                view.id,
+                ConstraintSet.START,
+                motionLayout.getChildAt(1).id,
+                ConstraintSet.START,
+            )
+            connect(view.id, ConstraintSet.END, motionLayout.id, ConstraintSet.END)
+            connect(view.id, ConstraintSet.TOP, motionLayout.getChildAt(1).id, ConstraintSet.TOP)
         }
     }
 }
@@ -157,52 +190,91 @@
     selectedIndex: Int,
     lastOrientation: Int,
 ) {
-    motionLayout.children.forEachIndexed { index, button ->
-        setMargin(button.id, ConstraintSet.RIGHT, 0)
-        setMargin(button.id, ConstraintSet.BOTTOM, 0)
-        when (lastOrientation) {
-            ORIENTATION_LANDSCAPE -> {
-                setButtonPositionLandscapeConstraints(motionLayout, index, button)
-                if (selectedIndex != motionLayout.childCount - index - 1) {
-                    setAlpha(button.id, 0.0F)
-                    constrainWidth(button.id, 0)
-                } else {
-                    setAlpha(button.id, 1.0F)
-                    constrainWidth(
-                        button.id,
-                        motionLayout.context.resources.getDimensionPixelSize(
-                            R.dimen.volume_dialog_ringer_drawer_button_size
-                        ),
-                    )
-                }
-                constrainHeight(
-                    button.id,
-                    motionLayout.context.resources.getDimensionPixelSize(
-                        R.dimen.volume_dialog_ringer_drawer_button_size
-                    ),
-                )
-            }
-            ORIENTATION_PORTRAIT -> {
-                setButtonPositionPortraitConstraints(motionLayout, index, button)
-                if (selectedIndex != motionLayout.childCount - index - 1) {
-                    setAlpha(button.id, 0.0F)
-                    constrainHeight(button.id, 0)
-                } else {
-                    setAlpha(button.id, 1.0F)
+    motionLayout.children.forEachIndexed { index, view ->
+        if (view.id != R.id.ringer_buttons_background) {
+            setMargin(view.id, ConstraintSet.END, 0)
+            setMargin(view.id, ConstraintSet.BOTTOM, 0)
+            when (lastOrientation) {
+                ORIENTATION_LANDSCAPE -> {
+                    setButtonPositionLandscapeConstraints(motionLayout, index, view)
+                    if (selectedIndex != motionLayout.childCount - index - 1) {
+                        setAlpha(view.id, 0.0F)
+                        constrainWidth(
+                            view.id,
+                            TypedValue.applyDimension(
+                                    TypedValue.COMPLEX_UNIT_DIP,
+                                    1F,
+                                    motionLayout.context.resources.displayMetrics,
+                                )
+                                .toInt(),
+                        )
+                    } else {
+                        connect(view.id, ConstraintSet.END, motionLayout.id, ConstraintSet.END)
+                        setAlpha(view.id, 1.0F)
+                        constrainWidth(
+                            view.id,
+                            motionLayout.context.resources.getDimensionPixelSize(
+                                R.dimen.volume_dialog_ringer_drawer_button_size
+                            ),
+                        )
+                    }
                     constrainHeight(
-                        button.id,
+                        view.id,
                         motionLayout.context.resources.getDimensionPixelSize(
                             R.dimen.volume_dialog_ringer_drawer_button_size
                         ),
                     )
                 }
-                constrainWidth(
-                    button.id,
-                    motionLayout.context.resources.getDimensionPixelSize(
-                        R.dimen.volume_dialog_ringer_drawer_button_size
-                    ),
-                )
+
+                ORIENTATION_PORTRAIT -> {
+                    setButtonPositionPortraitConstraints(motionLayout, index, view)
+                    if (selectedIndex != motionLayout.childCount - index - 1) {
+                        setAlpha(view.id, 0.0F)
+                        constrainHeight(
+                            view.id,
+                            TypedValue.applyDimension(
+                                    TypedValue.COMPLEX_UNIT_DIP,
+                                    1F,
+                                    motionLayout.context.resources.displayMetrics,
+                                )
+                                .toInt(),
+                        )
+                    } else {
+                        setAlpha(view.id, 1.0F)
+                        constrainHeight(
+                            view.id,
+                            motionLayout.context.resources.getDimensionPixelSize(
+                                R.dimen.volume_dialog_ringer_drawer_button_size
+                            ),
+                        )
+                    }
+                    constrainWidth(
+                        view.id,
+                        motionLayout.context.resources.getDimensionPixelSize(
+                            R.dimen.volume_dialog_ringer_drawer_button_size
+                        ),
+                    )
+                }
             }
+        } else {
+            constrainWidth(
+                view.id,
+                motionLayout.context.resources.getDimensionPixelSize(R.dimen.volume_dialog_width),
+            )
+            connect(view.id, ConstraintSet.BOTTOM, motionLayout.id, ConstraintSet.BOTTOM)
+            connect(
+                view.id,
+                ConstraintSet.START,
+                motionLayout.getChildAt(motionLayout.childCount - selectedIndex - 1).id,
+                ConstraintSet.START,
+            )
+            connect(view.id, ConstraintSet.END, motionLayout.id, ConstraintSet.END)
+            connect(
+                view.id,
+                ConstraintSet.TOP,
+                motionLayout.getChildAt(motionLayout.childCount - selectedIndex - 1).id,
+                ConstraintSet.TOP,
+            )
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponent.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponent.kt
index 88af210..940c79c 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponent.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/dagger/VolumeDialogSliderComponent.kt
@@ -19,7 +19,6 @@
 import com.android.systemui.volume.dialog.sliders.domain.model.VolumeDialogSliderType
 import com.android.systemui.volume.dialog.sliders.ui.VolumeDialogOverscrollViewBinder
 import com.android.systemui.volume.dialog.sliders.ui.VolumeDialogSliderHapticsViewBinder
-import com.android.systemui.volume.dialog.sliders.ui.VolumeDialogSliderTouchesViewBinder
 import com.android.systemui.volume.dialog.sliders.ui.VolumeDialogSliderViewBinder
 import dagger.BindsInstance
 import dagger.Subcomponent
@@ -34,8 +33,6 @@
 
     fun sliderViewBinder(): VolumeDialogSliderViewBinder
 
-    fun sliderTouchesViewBinder(): VolumeDialogSliderTouchesViewBinder
-
     fun sliderHapticsViewBinder(): VolumeDialogSliderHapticsViewBinder
 
     fun overscrollViewBinder(): VolumeDialogOverscrollViewBinder
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractor.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractor.kt
index 04dc80c..3988acb 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractor.kt
@@ -16,7 +16,9 @@
 
 package com.android.systemui.volume.dialog.sliders.domain.interactor
 
+import com.android.settingslib.volume.shared.model.AudioStream
 import com.android.systemui.plugins.VolumeDialogController
+import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor
 import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
 import com.android.systemui.volume.dialog.domain.interactor.VolumeDialogStateInteractor
 import com.android.systemui.volume.dialog.shared.model.VolumeDialogStreamModel
@@ -27,6 +29,8 @@
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.SharingStarted
 import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
 import kotlinx.coroutines.flow.mapNotNull
 import kotlinx.coroutines.flow.stateIn
 
@@ -39,8 +43,17 @@
     @VolumeDialog private val coroutineScope: CoroutineScope,
     volumeDialogStateInteractor: VolumeDialogStateInteractor,
     private val volumeDialogController: VolumeDialogController,
+    zenModeInteractor: ZenModeInteractor,
 ) {
 
+    val isDisabledByZenMode: Flow<Boolean> =
+        if (sliderType is VolumeDialogSliderType.Stream) {
+            zenModeInteractor.activeModesBlockingStream(AudioStream(sliderType.audioStream)).map {
+                it.mainMode != null
+            }
+        } else {
+            flowOf(false)
+        }
     val slider: Flow<VolumeDialogStreamModel> =
         volumeDialogStateInteractor.volumeDialogState
             .mapNotNull {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderTouchesViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderTouchesViewBinder.kt
deleted file mode 100644
index 4ecac7a..0000000
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderTouchesViewBinder.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.volume.dialog.sliders.ui
-
-import android.annotation.SuppressLint
-import android.view.View
-import com.android.systemui.res.R
-import com.android.systemui.volume.dialog.sliders.dagger.VolumeDialogSliderScope
-import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSliderInputEventsViewModel
-import com.google.android.material.slider.Slider
-import javax.inject.Inject
-
-@VolumeDialogSliderScope
-class VolumeDialogSliderTouchesViewBinder
-@Inject
-constructor(private val viewModel: VolumeDialogSliderInputEventsViewModel) {
-
-    @SuppressLint("ClickableViewAccessibility")
-    fun bind(view: View) {
-        with(view.requireViewById<Slider>(R.id.volume_dialog_slider)) {
-            setOnTouchListener { _, event ->
-                viewModel.onTouchEvent(event)
-                false
-            }
-        }
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
index 67ffb06..3b964fd 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt
@@ -23,6 +23,7 @@
 import androidx.dynamicanimation.animation.SpringForce
 import com.android.systemui.res.R
 import com.android.systemui.volume.dialog.sliders.dagger.VolumeDialogSliderScope
+import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSliderInputEventsViewModel
 import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSliderStateModel
 import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSliderViewModel
 import com.google.android.material.slider.Slider
@@ -35,7 +36,10 @@
 @VolumeDialogSliderScope
 class VolumeDialogSliderViewBinder
 @Inject
-constructor(private val viewModel: VolumeDialogSliderViewModel) {
+constructor(
+    private val viewModel: VolumeDialogSliderViewModel,
+    private val inputViewModel: VolumeDialogSliderInputEventsViewModel,
+) {
 
     private val sliderValueProperty =
         object : FloatPropertyCompat<Slider>("value") {
@@ -51,16 +55,21 @@
             dampingRatio = SpringForce.DAMPING_RATIO_NO_BOUNCY
         }
 
+    @SuppressLint("ClickableViewAccessibility")
     fun CoroutineScope.bind(view: View) {
         var isInitialUpdate = true
         val sliderView: Slider = view.requireViewById(R.id.volume_dialog_slider)
         val animation = SpringAnimation(sliderView, sliderValueProperty)
         animation.spring = springForce
-
+        sliderView.setOnTouchListener { _, event ->
+            inputViewModel.onTouchEvent(event)
+            false
+        }
         sliderView.addOnChangeListener { _, value, fromUser ->
             viewModel.setStreamVolume(value.roundToInt(), fromUser)
         }
 
+        viewModel.isDisabledByZenMode.onEach { sliderView.isEnabled = !it }.launchIn(this)
         viewModel.state
             .onEach {
                 sliderView.setModel(it, animation, isInitialUpdate)
@@ -82,7 +91,7 @@
         // coerce the current value to the new value range before animating it. This prevents
         // animating from the value that is outside of current [valueFrom, valueTo].
         value = value.coerceIn(valueFrom, valueTo)
-        setTrackIconActiveStart(model.iconRes)
+        trackIconActiveStart = model.icon
         if (isInitialUpdate) {
             value = model.value
         } else {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt
index f066b56..75d427a 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSlidersViewBinder.kt
@@ -71,7 +71,6 @@
         viewsToAnimate: Array<View>,
     ) {
         with(component.sliderViewBinder()) { bind(sliderContainer) }
-        with(component.sliderTouchesViewBinder()) { bind(sliderContainer) }
         with(component.sliderHapticsViewBinder()) { bind(sliderContainer) }
         with(component.overscrollViewBinder()) { bind(sliderContainer, viewsToAnimate) }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProvider.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProvider.kt
index 5c39b6f..daf4c82 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderIconProvider.kt
@@ -16,13 +16,16 @@
 
 package com.android.systemui.volume.dialog.sliders.ui.viewmodel
 
+import android.annotation.SuppressLint
+import android.content.Context
+import android.graphics.drawable.Drawable
 import android.media.AudioManager
 import androidx.annotation.DrawableRes
-import com.android.settingslib.notification.domain.interactor.NotificationsSoundPolicyInteractor
 import com.android.settingslib.volume.domain.interactor.AudioVolumeInteractor
 import com.android.settingslib.volume.shared.model.AudioStream
 import com.android.settingslib.volume.shared.model.RingerMode
 import com.android.systemui.res.R
+import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor
 import javax.inject.Inject
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.combine
@@ -31,11 +34,12 @@
 class VolumeDialogSliderIconProvider
 @Inject
 constructor(
-    private val notificationsSoundPolicyInteractor: NotificationsSoundPolicyInteractor,
+    private val context: Context,
+    private val zenModeInteractor: ZenModeInteractor,
     private val audioVolumeInteractor: AudioVolumeInteractor,
 ) {
 
-    @DrawableRes
+    @SuppressLint("UseCompatLoadingForDrawables")
     fun getStreamIcon(
         stream: Int,
         level: Int,
@@ -43,54 +47,71 @@
         levelMax: Int,
         isMuted: Boolean,
         isRoutedToBluetooth: Boolean,
-    ): Flow<Int> {
+    ): Flow<Drawable> {
         return combine(
-            notificationsSoundPolicyInteractor.isZenMuted(AudioStream(stream)),
+            zenModeInteractor.activeModesBlockingStream(AudioStream(stream)),
             ringerModeForStream(stream),
-        ) { isZenMuted, ringerMode ->
-            val isStreamOffline = level == 0 || isMuted
-            if (isZenMuted) {
-                // TODO(b/372466264) use icon for the corresponding zenmode
-                return@combine com.android.internal.R.drawable.ic_qs_dnd
-            }
-            when (ringerMode?.value) {
-                AudioManager.RINGER_MODE_VIBRATE ->
-                    return@combine R.drawable.ic_volume_ringer_vibrate
-                AudioManager.RINGER_MODE_SILENT -> return@combine R.drawable.ic_ring_volume_off
-            }
-            if (isRoutedToBluetooth) {
-                return@combine if (stream == AudioManager.STREAM_VOICE_CALL) {
-                    R.drawable.ic_volume_bt_sco
-                } else {
-                    if (isStreamOffline) {
-                        R.drawable.ic_volume_media_bt_mute
-                    } else {
-                        R.drawable.ic_volume_media_bt
-                    }
-                }
-            }
-
-            return@combine if (isStreamOffline) {
-                getMutedIconForStream(stream) ?: getIconForStream(stream)
+        ) { activeModesBlockingStream, ringerMode ->
+            if (activeModesBlockingStream.mainMode?.icon != null) {
+                return@combine activeModesBlockingStream.mainMode.icon.drawable
             } else {
-                if (level < (levelMax + levelMin) / 2) {
-                    // This icon is different on TV
-                    R.drawable.ic_volume_media_low
-                } else {
-                    getIconForStream(stream)
-                }
+                context.getDrawable(
+                    getIconRes(
+                        stream,
+                        level,
+                        levelMin,
+                        levelMax,
+                        isMuted,
+                        isRoutedToBluetooth,
+                        ringerMode,
+                    )
+                )!!
             }
         }
     }
 
     @DrawableRes
-    private fun getMutedIconForStream(stream: Int): Int? {
-        return when (stream) {
-            AudioManager.STREAM_MUSIC -> R.drawable.ic_volume_media_mute
-            AudioManager.STREAM_NOTIFICATION -> R.drawable.ic_volume_ringer_mute
-            AudioManager.STREAM_ALARM -> R.drawable.ic_volume_alarm_mute
-            AudioManager.STREAM_SYSTEM -> R.drawable.ic_volume_system_mute
-            else -> null
+    private fun getIconRes(
+        stream: Int,
+        level: Int,
+        levelMin: Int,
+        levelMax: Int,
+        isMuted: Boolean,
+        isRoutedToBluetooth: Boolean,
+        ringerMode: RingerMode?,
+    ): Int {
+        val isStreamOffline = level == 0 || isMuted
+        when (ringerMode?.value) {
+            AudioManager.RINGER_MODE_VIBRATE -> return R.drawable.ic_volume_ringer_vibrate
+            AudioManager.RINGER_MODE_SILENT -> return R.drawable.ic_ring_volume_off
+        }
+        if (isRoutedToBluetooth) {
+            return if (stream == AudioManager.STREAM_VOICE_CALL) {
+                R.drawable.ic_volume_bt_sco
+            } else {
+                if (isStreamOffline) {
+                    R.drawable.ic_volume_media_bt_mute
+                } else {
+                    R.drawable.ic_volume_media_bt
+                }
+            }
+        }
+
+        return if (isStreamOffline) {
+            when (stream) {
+                AudioManager.STREAM_MUSIC -> R.drawable.ic_volume_media_mute
+                AudioManager.STREAM_NOTIFICATION -> R.drawable.ic_volume_ringer_mute
+                AudioManager.STREAM_ALARM -> R.drawable.ic_volume_alarm_mute
+                AudioManager.STREAM_SYSTEM -> R.drawable.ic_volume_system_mute
+                else -> null
+            } ?: getIconForStream(stream)
+        } else {
+            if (level < (levelMax + levelMin) / 2) {
+                // This icon is different on TV
+                R.drawable.ic_volume_media_low
+            } else {
+                getIconForStream(stream)
+            }
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderStateModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderStateModel.kt
index 5750c04..8df9e78 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderStateModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderStateModel.kt
@@ -16,21 +16,21 @@
 
 package com.android.systemui.volume.dialog.sliders.ui.viewmodel
 
-import androidx.annotation.DrawableRes
+import android.graphics.drawable.Drawable
 import com.android.systemui.volume.dialog.shared.model.VolumeDialogStreamModel
 
 data class VolumeDialogSliderStateModel(
     val minValue: Float,
     val maxValue: Float,
     val value: Float,
-    @DrawableRes val iconRes: Int,
+    val icon: Drawable,
 )
 
-fun VolumeDialogStreamModel.toStateModel(@DrawableRes iconRes: Int): VolumeDialogSliderStateModel {
+fun VolumeDialogStreamModel.toStateModel(icon: Drawable): VolumeDialogSliderStateModel {
     return VolumeDialogSliderStateModel(
         minValue = levelMin.toFloat(),
         value = level.toFloat(),
         maxValue = levelMax.toFloat(),
-        iconRes = iconRes,
+        icon = icon,
     )
 }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt
index 6d8457b..d999910 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/viewmodel/VolumeDialogSliderViewModel.kt
@@ -66,12 +66,14 @@
     private val model: Flow<VolumeDialogStreamModel> =
         interactor.slider
             .filter {
-                val lastVolumeUpdateTime = userVolumeUpdates.value?.timestampMillis ?: 0
+                val currentVolumeUpdate = userVolumeUpdates.value ?: return@filter true
+                val lastVolumeUpdateTime = currentVolumeUpdate.timestampMillis
                 getTimestampMillis() - lastVolumeUpdateTime > VOLUME_UPDATE_GRACE_PERIOD
             }
             .stateIn(coroutineScope, SharingStarted.Eagerly, null)
             .filterNotNull()
 
+    val isDisabledByZenMode: Flow<Boolean> = interactor.isDisabledByZenMode
     val state: Flow<VolumeDialogSliderStateModel> =
         model
             .flatMapLatest { streamModel ->
@@ -81,7 +83,7 @@
                             level = level,
                             levelMin = levelMin,
                             levelMax = levelMax,
-                            isMuted = muted,
+                            isMuted = muteSupported && muted,
                             isRoutedToBluetooth = routedToBluetooth,
                         )
                     }
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/VolumeDialogResources.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/VolumeDialogResources.kt
deleted file mode 100644
index e5cf62b..0000000
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/VolumeDialogResources.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.systemui.volume.dialog.ui
-
-import android.content.Context
-import android.content.res.Resources
-import com.android.systemui.dagger.qualifiers.UiBackground
-import com.android.systemui.res.R
-import com.android.systemui.statusbar.policy.ConfigurationController
-import com.android.systemui.statusbar.policy.onConfigChanged
-import com.android.systemui.volume.dialog.dagger.scope.VolumeDialog
-import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
-import javax.inject.Inject
-import kotlin.coroutines.CoroutineContext
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.SharingStarted
-import kotlinx.coroutines.flow.filterNotNull
-import kotlinx.coroutines.flow.flowOn
-import kotlinx.coroutines.flow.map
-import kotlinx.coroutines.flow.onStart
-import kotlinx.coroutines.flow.stateIn
-
-/**
- * Provides cached resources [Flow]s that update when the configuration changes.
- *
- * Consume or use [kotlinx.coroutines.flow.first] to get the value.
- */
-@VolumeDialogScope
-class VolumeDialogResources
-@Inject
-constructor(
-    @VolumeDialog private val coroutineScope: CoroutineScope,
-    @UiBackground private val uiBackgroundContext: CoroutineContext,
-    private val context: Context,
-    private val configurationController: ConfigurationController,
-) {
-
-    val dialogShowDurationMillis: Flow<Long> = configurationResource {
-        getInteger(R.integer.config_dialogShowAnimationDurationMs).toLong()
-    }
-
-    val dialogHideDurationMillis: Flow<Long> = configurationResource {
-        getInteger(R.integer.config_dialogHideAnimationDurationMs).toLong()
-    }
-
-    private fun <T> configurationResource(get: Resources.() -> T): Flow<T> =
-        configurationController.onConfigChanged
-            .map { context.resources.get() }
-            .onStart { emit(context.resources.get()) }
-            .flowOn(uiBackgroundContext)
-            .stateIn(coroutineScope, SharingStarted.Eagerly, null)
-            .filterNotNull()
-}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt
index a3166a9..46d7d5f 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/binder/VolumeDialogViewBinder.kt
@@ -17,23 +17,28 @@
 package com.android.systemui.volume.dialog.ui.binder
 
 import android.app.Dialog
+import android.content.res.Resources
 import android.graphics.Rect
 import android.graphics.Region
 import android.view.View
 import android.view.ViewGroup
 import android.view.ViewTreeObserver
 import android.view.ViewTreeObserver.InternalInsetsInfo
+import android.view.WindowInsets
 import androidx.constraintlayout.motion.widget.MotionLayout
+import androidx.core.view.updatePadding
 import com.android.internal.view.RotationPolicy
+import com.android.systemui.common.ui.view.onApplyWindowInsets
+import com.android.systemui.dagger.qualifiers.Main
 import com.android.systemui.res.R
 import com.android.systemui.util.children
+import com.android.systemui.util.kotlin.awaitCancellationThenDispose
 import com.android.systemui.volume.SystemUIInterpolators
 import com.android.systemui.volume.dialog.dagger.scope.VolumeDialogScope
 import com.android.systemui.volume.dialog.ringer.ui.binder.VolumeDialogRingerViewBinder
 import com.android.systemui.volume.dialog.settings.ui.binder.VolumeDialogSettingsButtonViewBinder
 import com.android.systemui.volume.dialog.shared.model.VolumeDialogVisibilityModel
 import com.android.systemui.volume.dialog.sliders.ui.VolumeDialogSlidersViewBinder
-import com.android.systemui.volume.dialog.ui.VolumeDialogResources
 import com.android.systemui.volume.dialog.ui.utils.JankListenerFactory
 import com.android.systemui.volume.dialog.ui.utils.suspendAnimate
 import com.android.systemui.volume.dialog.ui.viewmodel.VolumeDialogViewModel
@@ -42,7 +47,7 @@
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.mapLatest
 import kotlinx.coroutines.flow.onEach
@@ -56,7 +61,7 @@
 class VolumeDialogViewBinder
 @Inject
 constructor(
-    private val volumeResources: VolumeDialogResources,
+    @Main resources: Resources,
     private val viewModel: VolumeDialogViewModel,
     private val jankListenerFactory: JankListenerFactory,
     private val tracer: VolumeTracer,
@@ -65,7 +70,14 @@
     private val settingsButtonViewBinder: VolumeDialogSettingsButtonViewBinder,
 ) {
 
+    private val dialogShowAnimationDurationMs =
+        resources.getInteger(R.integer.config_dialogShowAnimationDurationMs).toLong()
+    private val dialogHideAnimationDurationMs =
+        resources.getInteger(R.integer.config_dialogHideAnimationDurationMs).toLong()
+
     fun CoroutineScope.bind(dialog: Dialog) {
+        val insets: MutableStateFlow<WindowInsets> =
+            MutableStateFlow(WindowInsets.Builder().build())
         // Root view of the Volume Dialog.
         val root: MotionLayout = dialog.requireViewById(R.id.volume_dialog_root)
         root.alpha = 0f
@@ -83,6 +95,22 @@
 
         launch { root.viewTreeObserver.computeInternalInsetsListener(root) }
 
+        launch {
+            root
+                .onApplyWindowInsets { v, newInsets ->
+                    val insetsValues = newInsets.getInsets(WindowInsets.Type.displayCutout())
+                    v.updatePadding(
+                        left = insetsValues.left,
+                        top = insetsValues.top,
+                        right = insetsValues.right,
+                        bottom = insetsValues.bottom,
+                    )
+                    insets.value = newInsets
+                    WindowInsets.CONSUMED
+                }
+                .awaitCancellationThenDispose()
+        }
+
         with(volumeDialogRingerViewBinder) { bind(root) }
         with(slidersViewBinder) { bind(root) }
         with(settingsButtonViewBinder) { bind(root) }
@@ -98,13 +126,15 @@
                 when (it) {
                     is VolumeDialogVisibilityModel.Visible -> {
                         tracer.traceVisibilityEnd(it)
-                        calculateTranslationX(view)?.let(view::setTranslationX)
-                        view.animateShow(volumeResources.dialogShowDurationMillis.first())
+                        view.animateShow(
+                            duration = dialogShowAnimationDurationMs,
+                            translationX = calculateTranslationX(view),
+                        )
                     }
                     is VolumeDialogVisibilityModel.Dismissed -> {
                         tracer.traceVisibilityEnd(it)
                         view.animateHide(
-                            duration = volumeResources.dialogHideDurationMillis.first(),
+                            duration = dialogHideAnimationDurationMs,
                             translationX = calculateTranslationX(view),
                         )
                         dialog.dismiss()
@@ -129,24 +159,15 @@
         }
     }
 
-    private suspend fun View.animateShow(duration: Long) {
+    private suspend fun View.animateShow(duration: Long, translationX: Float?) {
+        translationX?.let { setTranslationX(translationX) }
+        alpha = 0f
         animate()
             .alpha(1f)
             .translationX(0f)
             .setDuration(duration)
             .setInterpolator(SystemUIInterpolators.LogDecelerateInterpolator())
             .suspendAnimate(jankListenerFactory.show(this, duration))
-        /* TODO(b/369993851)
-        .withEndAction(Runnable {
-            if (!Prefs.getBoolean(mContext, Prefs.Key.TOUCHED_RINGER_TOGGLE, false)) {
-                if (mRingerIcon != null) {
-                    mRingerIcon.postOnAnimationDelayed(
-                        getSinglePressFor(mRingerIcon), 1500
-                    )
-                }
-            }
-        })
-         */
     }
 
     private suspend fun View.animateHide(duration: Long, translationX: Float?) {
@@ -155,22 +176,7 @@
                 .alpha(0f)
                 .setDuration(duration)
                 .setInterpolator(SystemUIInterpolators.LogAccelerateInterpolator())
-        /*  TODO(b/369993851)
-        .withEndAction(
-            Runnable {
-                mHandler.postDelayed(
-                    Runnable {
-                        hideRingerDrawer()
-
-                    },
-                    50
-                )
-            }
-        )
-         */
-        if (translationX != null) {
-            animator.translationX(translationX)
-        }
+        translationX?.let { animator.translationX(it) }
         animator.suspendAnimate(jankListenerFactory.dismiss(this, duration))
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogViewModel.kt
index b20dffb..7a6ede4 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/ui/viewmodel/VolumeDialogViewModel.kt
@@ -44,9 +44,9 @@
 @Inject
 constructor(
     private val context: Context,
-    private val dialogVisibilityInteractor: VolumeDialogVisibilityInteractor,
+    dialogVisibilityInteractor: VolumeDialogVisibilityInteractor,
     volumeDialogSlidersInteractor: VolumeDialogSlidersInteractor,
-    private val volumeDialogStateInteractor: VolumeDialogStateInteractor,
+    volumeDialogStateInteractor: VolumeDialogStateInteractor,
     devicePostureController: DevicePostureController,
     configurationController: ConfigurationController,
 ) {
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 cec3d1e..5b8d9b0 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/volume/slider/ui/viewmodel/AudioStreamSliderViewModel.kt
@@ -18,9 +18,6 @@
 
 import android.content.Context
 import android.media.AudioManager
-import android.media.AudioManager.STREAM_ALARM
-import android.media.AudioManager.STREAM_MUSIC
-import android.media.AudioManager.STREAM_NOTIFICATION
 import android.util.Log
 import com.android.app.tracing.coroutines.launchTraced as launch
 import com.android.internal.logging.UiEventLogger
@@ -34,8 +31,6 @@
 import com.android.systemui.modes.shared.ModesUiIcons
 import com.android.systemui.res.R
 import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor
-import com.android.systemui.statusbar.policy.domain.model.ActiveZenModes
-import com.android.systemui.util.kotlin.combine
 import com.android.systemui.volume.panel.shared.VolumePanelLogger
 import com.android.systemui.volume.panel.ui.VolumePanelUiEvent
 import dagger.assisted.Assisted
@@ -43,12 +38,15 @@
 import dagger.assisted.AssistedInject
 import kotlin.math.roundToInt
 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.combine
 import kotlinx.coroutines.flow.filterNotNull
+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
 
@@ -101,48 +99,16 @@
         )
 
     override val slider: StateFlow<SliderState> =
-        if (ModesUiIcons.isEnabled) {
-            combine(
-                    audioVolumeInteractor.getAudioStream(audioStream),
-                    audioVolumeInteractor.canChangeVolume(audioStream),
-                    audioVolumeInteractor.ringerMode,
-                    zenModeInteractor.activeModesBlockingEverything,
-                    zenModeInteractor.activeModesBlockingAlarms,
-                    zenModeInteractor.activeModesBlockingMedia,
-                ) {
-                    model,
-                    isEnabled,
-                    ringerMode,
-                    modesBlockingEverything,
-                    modesBlockingAlarms,
-                    modesBlockingMedia ->
-                    volumePanelLogger.onVolumeUpdateReceived(audioStream, model.volume)
-                    model.toState(
-                        isEnabled,
-                        ringerMode,
-                        getStreamDisabledMessage(
-                            modesBlockingEverything,
-                            modesBlockingAlarms,
-                            modesBlockingMedia,
-                        ),
-                    )
-                }
-                .stateIn(coroutineScope, SharingStarted.Eagerly, SliderState.Empty)
-        } else {
-            combine(
-                    audioVolumeInteractor.getAudioStream(audioStream),
-                    audioVolumeInteractor.canChangeVolume(audioStream),
-                    audioVolumeInteractor.ringerMode,
-                ) { model, isEnabled, ringerMode ->
-                    volumePanelLogger.onVolumeUpdateReceived(audioStream, model.volume)
-                    model.toState(
-                        isEnabled,
-                        ringerMode,
-                        getStreamDisabledMessageWithoutModes(audioStream),
-                    )
-                }
-                .stateIn(coroutineScope, SharingStarted.Eagerly, SliderState.Empty)
-        }
+        combine(
+                audioVolumeInteractor.getAudioStream(audioStream),
+                audioVolumeInteractor.canChangeVolume(audioStream),
+                audioVolumeInteractor.ringerMode,
+                streamDisabledMessage(),
+            ) { model, isEnabled, ringerMode, streamDisabledMessage ->
+                volumePanelLogger.onVolumeUpdateReceived(audioStream, model.volume)
+                model.toState(isEnabled, ringerMode, streamDisabledMessage)
+            }
+            .stateIn(coroutineScope, SharingStarted.Eagerly, SliderState.Empty)
 
     init {
         volumeChanges
@@ -229,40 +195,32 @@
         )
     }
 
-    private fun getStreamDisabledMessage(
-        blockingEverything: ActiveZenModes,
-        blockingAlarms: ActiveZenModes,
-        blockingMedia: ActiveZenModes,
-    ): String {
-        // TODO: b/372213356 - Figure out the correct messages for VOICE_CALL and RING.
-        //  In fact, VOICE_CALL should not be affected by interruption filtering at all.
-        return if (audioStream.value == STREAM_NOTIFICATION) {
-            context.getString(R.string.stream_notification_unavailable)
-        } else {
-            val blockingModeName =
-                when {
-                    blockingEverything.mainMode != null -> blockingEverything.mainMode.name
-                    audioStream.value == STREAM_ALARM -> blockingAlarms.mainMode?.name
-                    audioStream.value == STREAM_MUSIC -> blockingMedia.mainMode?.name
-                    else -> null
-                }
-
-            if (blockingModeName != null) {
-                context.getString(R.string.stream_unavailable_by_modes, blockingModeName)
+    // TODO: b/372213356 - Figure out the correct messages for VOICE_CALL and RING.
+    //  In fact, VOICE_CALL should not be affected by interruption filtering at all.
+    private fun streamDisabledMessage(): Flow<String> {
+        return if (ModesUiIcons.isEnabled) {
+            if (audioStream.value == AudioManager.STREAM_NOTIFICATION) {
+                flowOf(context.getString(R.string.stream_notification_unavailable))
             } else {
-                // Should not actually be visible, but as a catch-all.
-                context.getString(R.string.stream_unavailable_by_unknown)
+                if (zenModeInteractor.canBeBlockedByZenMode(audioStream)) {
+                    zenModeInteractor.activeModesBlockingStream(audioStream).map { blockingZenModes
+                        ->
+                        blockingZenModes.mainMode?.name?.let {
+                            context.getString(R.string.stream_unavailable_by_modes, it)
+                        } ?: context.getString(R.string.stream_unavailable_by_unknown)
+                    }
+                } else {
+                    flowOf(context.getString(R.string.stream_unavailable_by_unknown))
+                }
             }
-        }
-    }
-
-    private fun getStreamDisabledMessageWithoutModes(audioStream: AudioStream): String {
-        // TODO: b/372213356 - Figure out the correct messages for VOICE_CALL and RING.
-        //  In fact, VOICE_CALL should not be affected by interruption filtering at all.
-        return if (audioStream.value == STREAM_NOTIFICATION) {
-            context.getString(R.string.stream_notification_unavailable)
         } else {
-            context.getString(R.string.stream_alarm_unavailable)
+            flowOf(
+                if (audioStream.value == AudioManager.STREAM_NOTIFICATION) {
+                    context.getString(R.string.stream_notification_unavailable)
+                } else {
+                    context.getString(R.string.stream_alarm_unavailable)
+                }
+            )
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java b/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java
index 389b6fb..411e06e 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/controller/QuickAccessWalletController.java
@@ -229,6 +229,35 @@
     }
 
     /**
+     * Starts the QuickAccessWallet Gesture UI (the app is launched by a hardware gesture).
+     *
+     *
+     * The Wallet target activity is defined as the {@link android.app.PendingIntent} returned by
+     * {@link QuickAccessWalletClient#getGestureTargetActivityPendingIntent} if that is not null.
+     * If that is null, then the method {@link QuickAccessWalletController#startQuickAccessUiIntent}
+     * as defined below is called, which starts the QuickAccessWallet UI.
+     *
+     * @param activityStarter an {@link ActivityStarter} to launch the Intent or PendingIntent.
+     * @param animationController an {@link ActivityTransitionAnimator.Controller} to provide a
+     *                            smooth animation for the activity launch.
+     */
+    public void startGestureUiIntent(ActivityStarter activityStarter,
+            ActivityTransitionAnimator.Controller animationController){
+        mQuickAccessWalletClient.getGestureTargetActivityPendingIntent(
+                mExecutor,
+                gesturePendingIntent -> {
+                    if (gesturePendingIntent != null) {
+                        activityStarter.startPendingIntentMaybeDismissingKeyguard(
+                                gesturePendingIntent, null, null);
+                        return;
+                    }
+
+                    startQuickAccessUiIntent(activityStarter, animationController, true);
+                }
+        );
+    }
+
+    /**
      * Starts the QuickAccessWallet UI: either the app's designated UI, or the built-in Wallet UI.
      *
      * If the service has configured itself so that
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
index 4abbbac..047b78e 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
@@ -28,9 +28,11 @@
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.flags.Flags
+import com.android.systemui.flags.fakeFeatureFlagsClassic
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
+import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
 import com.android.systemui.keyguard.shared.model.Edge
 import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
 import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
@@ -159,14 +161,12 @@
         dndModeId = MANUAL_DND_INACTIVE.id
         zenModeRepository.addMode(MANUAL_DND_INACTIVE)
 
-        repository = FakeKeyguardRepository()
+        repository = kosmos.fakeKeyguardRepository
 
-        val withDeps = KeyguardInteractorFactory.create(repository = repository)
-
-        withDeps.featureFlags.apply { set(Flags.REGION_SAMPLING, false) }
+        kosmos.fakeFeatureFlagsClassic.set(Flags.REGION_SAMPLING, false)
         underTest =
             ClockEventController(
-                withDeps.keyguardInteractor,
+                kosmos.keyguardInteractor,
                 keyguardTransitionInteractor,
                 broadcastDispatcher,
                 batteryController,
@@ -177,7 +177,7 @@
                 mainExecutor,
                 bgExecutor,
                 clockBuffers,
-                withDeps.featureFlags,
+                kosmos.fakeFeatureFlagsClassic,
                 zenModeController,
                 kosmos.zenModeInteractor,
                 userTracker,
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 51a7b5f..bc9d4c7 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
@@ -42,6 +42,7 @@
 import com.android.app.viewcapture.ViewCaptureAwareWindowManager;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.settingslib.bluetooth.HearingAidDeviceManager;
 import com.android.systemui.Dependency;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
@@ -95,6 +96,8 @@
     private Lazy<ViewCapture> mLazyViewCapture;
     @Mock
     private NavigationModeController mNavigationModeController;
+    @Mock
+    private HearingAidDeviceManager mHearingAidDeviceManager;
 
     @Before
     public void setUp() throws Exception {
@@ -170,7 +173,7 @@
         mController = setUpController();
         mController.mFloatingMenu = new MenuViewLayerController(mContextWrapper, mWindowManager,
                 mViewCaptureAwareWindowManager, mAccessibilityManager, mSecureSettings,
-                mNavigationModeController);
+                mNavigationModeController, mHearingAidDeviceManager);
         captureKeyguardUpdateMonitorCallback();
         mKeyguardCallback.onUserUnlocked();
 
@@ -198,7 +201,7 @@
         mController = setUpController();
         mController.mFloatingMenu = new MenuViewLayerController(mContextWrapper, mWindowManager,
                 mViewCaptureAwareWindowManager, mAccessibilityManager, mSecureSettings,
-                mNavigationModeController);
+                mNavigationModeController, mHearingAidDeviceManager);
         captureKeyguardUpdateMonitorCallback();
 
         mKeyguardCallback.onUserSwitching(fakeUserId);
@@ -213,7 +216,7 @@
         mController = setUpController();
         mController.mFloatingMenu = new MenuViewLayerController(mContextWrapper, mWindowManager,
                 mViewCaptureAwareWindowManager, mAccessibilityManager, mSecureSettings,
-                mNavigationModeController);
+                mNavigationModeController, mHearingAidDeviceManager);
         captureKeyguardUpdateMonitorCallback();
         mKeyguardCallback.onUserUnlocked();
         mKeyguardCallback.onKeyguardVisibilityChanged(true);
@@ -368,9 +371,9 @@
         final AccessibilityFloatingMenuController controller =
                 new AccessibilityFloatingMenuController(mContextWrapper, windowManager,
                         viewCaptureAwareWindowManager, displayManager, mAccessibilityManager,
-                        mTargetsObserver, mModeObserver, mKeyguardUpdateMonitor, mSecureSettings,
-                        displayTracker, mNavigationModeController, new Handler(
-                                mTestableLooper.getLooper()));
+                        mTargetsObserver, mModeObserver, mHearingAidDeviceManager,
+                        mKeyguardUpdateMonitor, mSecureSettings, displayTracker,
+                        mNavigationModeController, new Handler(mTestableLooper.getLooper()));
         controller.init();
 
         return controller;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java
index dddaabb..856c379 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/floatingmenu/MenuAnimationControllerTest.java
@@ -26,7 +26,6 @@
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 
 import android.graphics.PointF;
-
 import android.testing.TestableLooper;
 import android.view.View;
 import android.view.ViewPropertyAnimator;
@@ -40,6 +39,7 @@
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
 
+import com.android.settingslib.bluetooth.HearingAidDeviceManager;
 import com.android.systemui.Prefs;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.accessibility.utils.TestUtils;
@@ -74,6 +74,8 @@
 
     @Mock
     private AccessibilityManager mAccessibilityManager;
+    @Mock
+    private HearingAidDeviceManager mHearingAidDeviceManager;
 
     @Before
     public void setUp() throws Exception {
@@ -82,7 +84,7 @@
                 stubWindowManager);
         final SecureSettings secureSettings = TestUtils.mockSecureSettings();
         final MenuViewModel stubMenuViewModel = new MenuViewModel(mContext, mAccessibilityManager,
-                secureSettings);
+                secureSettings, mHearingAidDeviceManager);
 
         mMenuView = spy(new MenuView(mContext, stubMenuViewModel, stubMenuViewAppearance,
                 secureSettings));
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 400b3b3..33cfb38 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
@@ -77,6 +77,7 @@
 import com.android.internal.accessibility.common.ShortcutConstants;
 import com.android.internal.accessibility.dialog.AccessibilityTarget;
 import com.android.internal.messages.nano.SystemMessageProto;
+import com.android.settingslib.bluetooth.HearingAidDeviceManager;
 import com.android.systemui.Flags;
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.SysuiTestableContext;
@@ -140,6 +141,8 @@
     @Mock
     private AccessibilityManager mStubAccessibilityManager;
     @Mock
+    private HearingAidDeviceManager mHearingAidDeviceManager;
+    @Mock
     private PackageManager mMockPackageManager;
     private final SecureSettings mSecureSettings = TestUtils.mockSecureSettings();
 
@@ -160,7 +163,7 @@
         doReturn(mWindowMetrics).when(mStubWindowManager).getCurrentWindowMetrics();
 
         mMenuViewModel = new MenuViewModel(
-                mSpyContext, mStubAccessibilityManager, mSecureSettings);
+                mSpyContext, mStubAccessibilityManager, mSecureSettings, mHearingAidDeviceManager);
         MenuViewAppearance menuViewAppearance = new MenuViewAppearance(
                 mSpyContext, mStubWindowManager);
         mMenuView = spy(
@@ -419,9 +422,10 @@
     @Test
     @EnableFlags(Flags.FLAG_FLOATING_MENU_DRAG_TO_EDIT)
     public void onDismissAction_incrementsTexMetricDismiss() {
-        mMenuViewModel.onTargetFeaturesChanged(
-                List.of(new TestAccessibilityTarget(mSpyContext, 1234),
-                        new TestAccessibilityTarget(mSpyContext, 5678)));
+        List<AccessibilityTarget> testTargets = new ArrayList<>();
+        testTargets.add(new TestAccessibilityTarget(mSpyContext, 1234));
+        testTargets.add(new TestAccessibilityTarget(mSpyContext, 5678));
+        mMenuViewModel.onTargetFeaturesChanged(testTargets);
 
         mMenuViewLayer.dispatchAccessibilityAction(R.id.action_remove_menu);
 
@@ -431,9 +435,10 @@
     @Test
     @EnableFlags(Flags.FLAG_FLOATING_MENU_DRAG_TO_EDIT)
     public void onEditAction_incrementsTexMetricEdit() {
-        mMenuViewModel.onTargetFeaturesChanged(
-                List.of(new TestAccessibilityTarget(mSpyContext, 1234),
-                        new TestAccessibilityTarget(mSpyContext, 5678)));
+        List<AccessibilityTarget> testTargets = new ArrayList<>();
+        testTargets.add(new TestAccessibilityTarget(mSpyContext, 1234));
+        testTargets.add(new TestAccessibilityTarget(mSpyContext, 5678));
+        mMenuViewModel.onTargetFeaturesChanged(testTargets);
 
         mMenuViewLayer.dispatchAccessibilityAction(R.id.action_edit);
 
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 387cc08..1320223 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,7 +33,6 @@
 import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
 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.bouncerSceneContentViewModelFactory
 import com.android.systemui.flags.Flags
 import com.android.systemui.flags.fakeFeatureFlagsClassic
diff --git a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardListenerTest.java b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardListenerTest.java
index d8d53e0..0c7989d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardListenerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/clipboardoverlay/ClipboardListenerTest.java
@@ -32,6 +32,7 @@
 import android.content.ClipData;
 import android.content.ClipDescription;
 import android.content.ClipboardManager;
+import android.content.pm.UserInfo;
 import android.os.Build;
 import android.os.PersistableBundle;
 import android.os.UserHandle;
@@ -45,6 +46,8 @@
 import com.android.internal.logging.UiEventLogger;
 import com.android.systemui.Flags;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.settings.FakeUserTracker;
+import com.android.systemui.user.utils.FakeUserScopedService;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -56,6 +59,7 @@
 import org.mockito.Spy;
 
 import java.util.ArrayList;
+import java.util.List;
 
 import javax.inject.Provider;
 
@@ -68,6 +72,10 @@
     @Mock
     private KeyguardManager mKeyguardManager;
     @Mock
+    private ClipboardManager mClipboardManagerSecondaryUser;
+    @Mock
+    private KeyguardManager mKeyguardManagerSecondaryUser;
+    @Mock
     private ClipboardOverlayController mOverlayController;
     @Mock
     private ClipboardToast mClipboardToast;
@@ -76,9 +84,6 @@
     @Mock
     private ClipboardOverlaySuppressionController mClipboardOverlaySuppressionController;
 
-    private ClipData mSampleClipData;
-    private String mSampleSource = "Example source";
-
     @Captor
     private ArgumentCaptor<Runnable> mRunnableCaptor;
     @Captor
@@ -89,6 +94,20 @@
     @Spy
     private Provider<ClipboardOverlayController> mOverlayControllerProvider;
 
+    private final FakeUserScopedService<ClipboardManager> mUserScopedClipboardManager =
+            new FakeUserScopedService<>(mClipboardManager);
+    private final FakeUserScopedService<KeyguardManager> mUserScopedKeyguardManager =
+            new FakeUserScopedService<>(mKeyguardManager);
+    private final FakeUserTracker mUserTracker = new FakeUserTracker();
+
+    private final List<UserInfo> mUserInfos = List.of(
+            new UserInfo(0, "system", 0), new UserInfo(50, "secondary", 0));
+    private final ClipData mSampleClipData = new ClipData("Test", new String[]{"text/plain"},
+            new ClipData.Item("Test Item"));
+    private final ClipData mSecondaryClipData = new ClipData(
+            "Test secondary", new String[]{"text/plain"}, new ClipData.Item("Secondary Item"));
+    private final String mSampleSource = "Example source";
+
     private ClipboardListener mClipboardListener;
 
 
@@ -97,30 +116,38 @@
         mOverlayControllerProvider = () -> mOverlayController;
 
         MockitoAnnotations.initMocks(this);
-        when(mClipboardManager.hasPrimaryClip()).thenReturn(true);
+
         Settings.Secure.putInt(
                 mContext.getContentResolver(), SETTINGS_SECURE_USER_SETUP_COMPLETE, 1);
 
-        mSampleClipData = new ClipData("Test", new String[]{"text/plain"},
-                new ClipData.Item("Test Item"));
-        when(mClipboardManager.getPrimaryClip()).thenReturn(mSampleClipData);
-        when(mClipboardManager.getPrimaryClipSource()).thenReturn(mSampleSource);
+        mUserTracker.set(mUserInfos, 0);
+        UserHandle user0 = mUserInfos.get(0).getUserHandle();
+        UserHandle user1 = mUserInfos.get(1).getUserHandle();
+        mUserScopedKeyguardManager.addImplementation(user0, mKeyguardManager);
+        mUserScopedKeyguardManager.addImplementation(user1, mKeyguardManagerSecondaryUser);
+        setupClipboardManager(mClipboardManager, user0, mSampleClipData);
+        setupClipboardManager(mClipboardManagerSecondaryUser, user1, mSecondaryClipData);
 
         mClipboardListener = new ClipboardListener(
                 getContext(),
                 mOverlayControllerProvider,
                 mClipboardToast,
-                user -> {
-                    if (UserHandle.CURRENT.equals(user)) {
-                        return mClipboardManager;
-                    }
-                    return null;
-                },
-                mKeyguardManager,
+                mUserTracker,
+                mUserScopedClipboardManager,
+                mUserScopedKeyguardManager,
                 mUiEventLogger,
+                getContext().getMainExecutor(),
                 mClipboardOverlaySuppressionController);
     }
 
+    private void setupClipboardManager(
+            ClipboardManager clipboardManager, UserHandle user, ClipData clipData) {
+        when(clipboardManager.hasPrimaryClip()).thenReturn(true);
+        when(clipboardManager.getPrimaryClip()).thenReturn(clipData);
+        when(clipboardManager.getPrimaryClipSource()).thenReturn(mSampleSource);
+        mUserScopedClipboardManager.addImplementation(user, clipboardManager);
+    }
+
 
     @Test
     public void test_initialization() {
@@ -160,6 +187,76 @@
     }
 
     @Test
+    @DisableFlags(Flags.FLAG_CLIPBOARD_OVERLAY_MULTIUSER)
+    public void test_noSwitchUserWithFlagOff() {
+        mClipboardListener.start();
+
+        mClipboardListener.onPrimaryClipChanged();
+        mUserTracker.set(mUserInfos, 1);
+        mUserTracker.onUserChanged(mUserInfos.get(1).id);
+        mClipboardListener.onPrimaryClipChanged();
+
+        verify(mKeyguardManager, times(2)).isDeviceLocked();
+        verify(mClipboardManager, times(2)).hasPrimaryClip();
+        verify(mOverlayController, times(2)).setClipData(mSampleClipData, mSampleSource);
+        verifyNoMoreInteractions(mClipboardManagerSecondaryUser);
+        verifyNoMoreInteractions(mKeyguardManagerSecondaryUser);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_CLIPBOARD_OVERLAY_MULTIUSER)
+    public void test_switchUserSwitchesClipboard() {
+        mClipboardListener.start();
+
+        mClipboardListener.onPrimaryClipChanged();
+        verify(mClipboardManager).hasPrimaryClip();
+        verify(mOverlayController).setClipData(mSampleClipData, mSampleSource);
+
+        mUserTracker.set(mUserInfos, 1);
+        mUserTracker.onUserChanged(mUserInfos.get(1).id);
+        mClipboardListener.onPrimaryClipChanged();
+
+        verify(mClipboardManagerSecondaryUser).hasPrimaryClip();
+        verify(mOverlayController).setClipData(mSecondaryClipData, mSampleSource);
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_CLIPBOARD_OVERLAY_MULTIUSER)
+    @EnableFlags(Flags.FLAG_CLIPBOARD_NONINTERACTIVE_ON_LOCKSCREEN)
+    public void test_deviceLockedForSecondaryUser_withoutMultiuser_showsOverlay() {
+        when(mKeyguardManager.isDeviceLocked()).thenReturn(false);
+        when(mKeyguardManagerSecondaryUser.isDeviceLocked()).thenReturn(true);
+
+        mClipboardListener.start();
+        mUserTracker.set(mUserInfos, 1);
+        mUserTracker.onUserChanged(mUserInfos.get(1).id);
+        mClipboardListener.onPrimaryClipChanged();
+
+        verify(mUiEventLogger, times(1)).log(
+                ClipboardOverlayEvent.CLIPBOARD_OVERLAY_ENTERED, 0, mSampleSource);
+        verify(mOverlayController).setClipData(mSampleClipData, mSampleSource);
+        verifyNoMoreInteractions(mClipboardToast);
+    }
+
+    @Test
+    @EnableFlags({Flags.FLAG_CLIPBOARD_OVERLAY_MULTIUSER,
+            Flags.FLAG_CLIPBOARD_NONINTERACTIVE_ON_LOCKSCREEN})
+    public void test_deviceLockedForSecondaryUser_showsToast() {
+        when(mKeyguardManager.isDeviceLocked()).thenReturn(false);
+        when(mKeyguardManagerSecondaryUser.isDeviceLocked()).thenReturn(true);
+
+        mClipboardListener.start();
+        mUserTracker.set(mUserInfos, 1);
+        mUserTracker.onUserChanged(mUserInfos.get(1).id);
+        mClipboardListener.onPrimaryClipChanged();
+
+        verify(mUiEventLogger, times(1)).log(
+                ClipboardOverlayEvent.CLIPBOARD_TOAST_SHOWN, 0, mSampleSource);
+        verify(mClipboardToast, times(1)).showCopiedToast();
+        verifyNoMoreInteractions(mOverlayControllerProvider);
+    }
+
+    @Test
     @DisableFlags(Flags.FLAG_OVERRIDE_SUPPRESS_OVERLAY_CONDITION)
     public void test_shouldSuppressOverlay() {
         // Regardless of the package or emulator, nothing should be suppressed without the flag
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt
similarity index 94%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt
index 7a3089f..77c40a1 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.keyguard.data.repository
 
+import android.animation.AnimationHandler
 import android.animation.Animator
 import android.animation.ValueAnimator
 import android.platform.test.annotations.EnableFlags
@@ -36,6 +37,7 @@
 import com.android.systemui.keyguard.shared.model.TransitionModeOnCanceled
 import com.android.systemui.keyguard.shared.model.TransitionState
 import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.keyguard.util.FrameCallbackProvider
 import com.android.systemui.keyguard.util.KeyguardTransitionRunner
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
@@ -52,9 +54,12 @@
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.launch
+import kotlinx.coroutines.runBlocking
 import kotlinx.coroutines.test.advanceTimeBy
 import kotlinx.coroutines.test.runCurrent
 import kotlinx.coroutines.test.runTest
+import kotlinx.coroutines.withContext
+import org.junit.After
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
@@ -70,13 +75,29 @@
 
     private lateinit var underTest: KeyguardTransitionRepository
     private lateinit var runner: KeyguardTransitionRunner
+    private lateinit var callbackProvider: FrameCallbackProvider
 
     private val animatorListener = mock<Animator.AnimatorListener>()
 
     @Before
     fun setUp() {
         underTest = KeyguardTransitionRepositoryImpl(Dispatchers.Main)
-        runner = KeyguardTransitionRunner(underTest)
+        runBlocking {
+            callbackProvider = FrameCallbackProvider(testScope.backgroundScope)
+            withContext(Dispatchers.Main) {
+                // AnimationHandler uses ThreadLocal storage, and ValueAnimators MUST start from
+                // main thread
+                AnimationHandler.getInstance().setProvider(callbackProvider)
+            }
+            runner = KeyguardTransitionRunner(callbackProvider.frames, underTest)
+        }
+    }
+
+    @After
+    fun tearDown() {
+        runBlocking {
+            withContext(Dispatchers.Main) { AnimationHandler.getInstance().setProvider(null) }
+        }
     }
 
     @Test
@@ -84,13 +105,11 @@
         testScope.runTest {
             val steps = mutableListOf<TransitionStep>()
             val job = underTest.transition(AOD, LOCKSCREEN).onEach { steps.add(it) }.launchIn(this)
-
             runner.startTransition(
                 this,
                 TransitionInfo(OWNER_NAME, AOD, LOCKSCREEN, getAnimator()),
                 maxFrames = 100,
             )
-
             assertSteps(steps, listWithStep(BigDecimal(.1)), AOD, LOCKSCREEN)
             job.cancel()
         }
@@ -119,12 +138,12 @@
                 ),
             )
 
-            val firstTransitionSteps = listWithStep(step = BigDecimal(.1), stop = BigDecimal(.1))
-            assertSteps(steps.subList(0, 4), firstTransitionSteps, AOD, LOCKSCREEN)
+            val firstTransitionSteps = listWithStep(step = BigDecimal(.1), stop = BigDecimal(.2))
+            assertSteps(steps.subList(0, 5), firstTransitionSteps, AOD, LOCKSCREEN)
 
-            // Second transition starts from .1 (LAST_VALUE)
-            val secondTransitionSteps = listWithStep(step = BigDecimal(.1), start = BigDecimal(.1))
-            assertSteps(steps.subList(4, steps.size), secondTransitionSteps, LOCKSCREEN, AOD)
+            // Second transition starts from .2 (LAST_VALUE)
+            val secondTransitionSteps = listWithStep(step = BigDecimal(.1), start = BigDecimal(.2))
+            assertSteps(steps.subList(5, steps.size), secondTransitionSteps, LOCKSCREEN, AOD)
 
             job.cancel()
             job2.cancel()
@@ -154,12 +173,12 @@
                 ),
             )
 
-            val firstTransitionSteps = listWithStep(step = BigDecimal(.1), stop = BigDecimal(.1))
-            assertSteps(steps.subList(0, 4), firstTransitionSteps, AOD, LOCKSCREEN)
+            val firstTransitionSteps = listWithStep(step = BigDecimal(.1), stop = BigDecimal(.2))
+            assertSteps(steps.subList(0, 5), firstTransitionSteps, AOD, LOCKSCREEN)
 
             // Second transition starts from 0 (RESET)
             val secondTransitionSteps = listWithStep(start = BigDecimal(0), step = BigDecimal(.1))
-            assertSteps(steps.subList(4, steps.size), secondTransitionSteps, LOCKSCREEN, AOD)
+            assertSteps(steps.subList(5, steps.size), secondTransitionSteps, LOCKSCREEN, AOD)
 
             job.cancel()
             job2.cancel()
@@ -173,7 +192,7 @@
             runner.startTransition(
                 this,
                 TransitionInfo(OWNER_NAME, AOD, LOCKSCREEN, getAnimator()),
-                maxFrames = 3,
+                maxFrames = 2,
             )
 
             // Now start 2nd transition, which will interrupt the first
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaSwitchingControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaSwitchingControllerTest.java
index 7ba797c..86063ac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaSwitchingControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaSwitchingControllerTest.java
@@ -39,7 +39,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
-import android.graphics.PorterDuffColorFilter;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Icon;
 import android.media.AudioDeviceAttributes;
@@ -1286,13 +1285,6 @@
     }
 
     @Test
-    public void setColorFilter_setColorFilterToDrawable() {
-        mMediaSwitchingController.setColorFilter(mDrawable, true);
-
-        verify(mDrawable).setColorFilter(any(PorterDuffColorFilter.class));
-    }
-
-    @Test
     public void resetGroupMediaDevices_clearGroupDevices() {
         final MediaDevice selectedMediaDevice1 = mock(MediaDevice.class);
         final MediaDevice selectedMediaDevice2 = mock(MediaDevice.class);
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/power/PowerNotificationWarningsTest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
similarity index 100%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/power/PowerNotificationWarningsTest.java
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentControllerTest.java
similarity index 79%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentControllerTest.java
index 782b248..e4a49530 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentControllerTest.java
@@ -9,8 +9,8 @@
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
 import static com.android.settingslib.wifi.WifiUtils.getHotspotIconResource;
-import static com.android.systemui.qs.tiles.dialog.InternetDialogController.TOAST_PARAMS_HORIZONTAL_WEIGHT;
-import static com.android.systemui.qs.tiles.dialog.InternetDialogController.TOAST_PARAMS_VERTICAL_WEIGHT;
+import static com.android.systemui.qs.tiles.dialog.InternetDetailsContentController.TOAST_PARAMS_HORIZONTAL_WEIGHT;
+import static com.android.systemui.qs.tiles.dialog.InternetDetailsContentController.TOAST_PARAMS_VERTICAL_WEIGHT;
 import static com.android.wifitrackerlib.WifiEntry.WIFI_LEVEL_MAX;
 import static com.android.wifitrackerlib.WifiEntry.WIFI_LEVEL_MIN;
 import static com.android.wifitrackerlib.WifiEntry.WIFI_LEVEL_UNREACHABLE;
@@ -105,7 +105,7 @@
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 @TestableLooper.RunWithLooper(setAsMainLooper = true)
-public class InternetDialogDelegateControllerTest extends SysuiTestCase {
+public class InternetDetailsContentControllerTest extends SysuiTestCase {
 
     private static final int SUB_ID = 1;
     private static final int SUB_ID2 = 2;
@@ -160,7 +160,7 @@
     @Mock
     private WifiUtils.InternetIconInjector mWifiIconInjector;
     @Mock
-    InternetDialogController.InternetDialogCallback mInternetDialogCallback;
+    InternetDetailsContentController.InternetDialogCallback mInternetDialogCallback;
     @Mock
     private ViewCaptureAwareWindowManager mWindowManager;
     @Mock
@@ -189,7 +189,7 @@
     private FakeFeatureFlags mFlags = new FakeFeatureFlags();
 
     private TestableResources mTestableResources;
-    private InternetDialogController mInternetDialogController;
+    private InternetDetailsContentController mInternetDetailsContentController;
     private FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock());
     private List<WifiEntry> mAccessPoints = new ArrayList<>();
     private List<WifiEntry> mWifiEntries = new ArrayList<>();
@@ -229,7 +229,7 @@
         when(mSystemUIToast.getInAnimation()).thenReturn(mAnimator);
         when(mWifiStateWorker.isWifiEnabled()).thenReturn(true);
 
-        mInternetDialogController = new InternetDialogController(mContext,
+        mInternetDetailsContentController = new InternetDetailsContentController(mContext,
                 mock(UiEventLogger.class), mock(ActivityStarter.class), mAccessPointController,
                 mSubscriptionManager, mTelephonyManager, mWifiManager,
                 mConnectivityManager, mHandler, mExecutor, mBroadcastDispatcher,
@@ -238,11 +238,11 @@
                 mCarrierConfigTracker, mLocationController, mDialogTransitionAnimator,
                 mWifiStateWorker, mFlags);
         mSubscriptionManager.addOnSubscriptionsChangedListener(mExecutor,
-                mInternetDialogController.mOnSubscriptionsChangedListener);
-        mInternetDialogController.onStart(mInternetDialogCallback, true);
-        mInternetDialogController.onAccessPointsChanged(mAccessPoints);
-        mInternetDialogController.mActivityStarter = mActivityStarter;
-        mInternetDialogController.mWifiIconInjector = mWifiIconInjector;
+                mInternetDetailsContentController.mOnSubscriptionsChangedListener);
+        mInternetDetailsContentController.onStart(mInternetDialogCallback, true);
+        mInternetDetailsContentController.onAccessPointsChanged(mAccessPoints);
+        mInternetDetailsContentController.mActivityStarter = mActivityStarter;
+        mInternetDetailsContentController.mWifiIconInjector = mWifiIconInjector;
         mFlags.set(Flags.QS_SECONDARY_DATA_SUB_INFO, false);
         mFlags.set(Flags.SHARE_WIFI_QS_BUTTON, false);
 
@@ -260,7 +260,7 @@
 
     @Test
     public void connectCarrierNetwork_mergedCarrierEntryCanConnect_connectAndCreateSysUiToast() {
-        InternetDialogController spyController = spy(mInternetDialogController);
+        InternetDetailsContentController spyController = spy(mInternetDetailsContentController);
         when(spyController.isMobileDataEnabled()).thenReturn(true);
         when(mKeyguardStateController.isUnlocked()).thenReturn(true);
         when(mConnectivityManager.getActiveNetwork()).thenReturn(mNetwork);
@@ -282,7 +282,7 @@
 
     @Test
     public void connectCarrierNetwork_mergedCarrierEntryCanConnect_doNothingWhenSettingsOff() {
-        InternetDialogController spyController = spy(mInternetDialogController);
+        InternetDetailsContentController spyController = spy(mInternetDetailsContentController);
         when(spyController.isMobileDataEnabled()).thenReturn(false);
         mTestableResources.addOverride(R.string.wifi_wont_autoconnect_for_now,
             TOAST_MESSAGE_STRING);
@@ -296,7 +296,7 @@
 
     @Test
     public void connectCarrierNetwork_mergedCarrierEntryCanConnect_doNothingWhenKeyguardLocked() {
-        InternetDialogController spyController = spy(mInternetDialogController);
+        InternetDetailsContentController spyController = spy(mInternetDetailsContentController);
         when(spyController.isMobileDataEnabled()).thenReturn(true);
         when(mKeyguardStateController.isUnlocked()).thenReturn(false);
 
@@ -311,8 +311,6 @@
 
     @Test
     public void connectCarrierNetwork_mergedCarrierEntryCanConnect_doNothingWhenMobileIsPrimary() {
-        InternetDialogController spyController = spy(mInternetDialogController);
-        when(spyController.isMobileDataEnabled()).thenReturn(true);
         when(mKeyguardStateController.isUnlocked()).thenReturn(true);
         when(mConnectivityManager.getActiveNetwork()).thenReturn(mNetwork);
         when(mConnectivityManager.getNetworkCapabilities(mNetwork))
@@ -322,7 +320,7 @@
 
         mTestableResources.addOverride(R.string.wifi_wont_autoconnect_for_now,
             TOAST_MESSAGE_STRING);
-        mInternetDialogController.connectCarrierNetwork();
+        mInternetDetailsContentController.connectCarrierNetwork();
 
         verify(mMergedCarrierEntry, never()).connect(null /* callback */, false /* showToast */);
         verify(mToastFactory, never()).createToast(any(), any(), anyString(), anyString(), anyInt(),
@@ -333,7 +331,7 @@
     public void makeOverlayToast_withGravityFlags_addViewWithLayoutParams() {
         mTestableResources.addOverride(TOAST_MESSAGE_STRING_ID, TOAST_MESSAGE_STRING);
 
-        mInternetDialogController.makeOverlayToast(TOAST_MESSAGE_STRING_ID);
+        mInternetDetailsContentController.makeOverlayToast(TOAST_MESSAGE_STRING_ID);
 
         ArgumentCaptor<WindowManager.LayoutParams> paramsCaptor = ArgumentCaptor.forClass(
             WindowManager.LayoutParams.class);
@@ -349,7 +347,7 @@
     public void makeOverlayToast_withAnimation_verifyAnimatorStart() {
         mTestableResources.addOverride(TOAST_MESSAGE_STRING_ID, TOAST_MESSAGE_STRING);
 
-        mInternetDialogController.makeOverlayToast(TOAST_MESSAGE_STRING_ID);
+        mInternetDetailsContentController.makeOverlayToast(TOAST_MESSAGE_STRING_ID);
 
         verify(mAnimator).start();
     }
@@ -358,7 +356,7 @@
     public void getDialogTitleText_withAirplaneModeOn_returnAirplaneMode() {
         fakeAirplaneModeEnabled(true);
 
-        assertTrue(TextUtils.equals(mInternetDialogController.getDialogTitleText(),
+        assertTrue(TextUtils.equals(mInternetDetailsContentController.getDialogTitleText(),
                 getResourcesString("airplane_mode")));
     }
 
@@ -366,7 +364,7 @@
     public void getDialogTitleText_withAirplaneModeOff_returnInternet() {
         fakeAirplaneModeEnabled(false);
 
-        assertTrue(TextUtils.equals(mInternetDialogController.getDialogTitleText(),
+        assertTrue(TextUtils.equals(mInternetDetailsContentController.getDialogTitleText(),
                 getResourcesString("quick_settings_internet_label")));
     }
 
@@ -375,13 +373,13 @@
         fakeAirplaneModeEnabled(true);
         when(mWifiStateWorker.isWifiEnabled()).thenReturn(false);
 
-        assertThat(mInternetDialogController.getSubtitleText(false))
+        assertThat(mInternetDetailsContentController.getSubtitleText(false))
                 .isEqualTo(getResourcesString("wifi_is_off"));
 
         // if the Wi-Fi disallow config, then don't return Wi-Fi related string.
-        mInternetDialogController.mCanConfigWifi = false;
+        mInternetDetailsContentController.mCanConfigWifi = false;
 
-        assertThat(mInternetDialogController.getSubtitleText(false))
+        assertThat(mInternetDetailsContentController.getSubtitleText(false))
                 .isNotEqualTo(getResourcesString("wifi_is_off"));
     }
 
@@ -390,13 +388,13 @@
         fakeAirplaneModeEnabled(false);
         when(mWifiStateWorker.isWifiEnabled()).thenReturn(false);
 
-        assertThat(mInternetDialogController.getSubtitleText(false))
+        assertThat(mInternetDetailsContentController.getSubtitleText(false))
                 .isEqualTo(getResourcesString("wifi_is_off"));
 
         // if the Wi-Fi disallow config, then don't return Wi-Fi related string.
-        mInternetDialogController.mCanConfigWifi = false;
+        mInternetDetailsContentController.mCanConfigWifi = false;
 
-        assertThat(mInternetDialogController.getSubtitleText(false))
+        assertThat(mInternetDetailsContentController.getSubtitleText(false))
                 .isNotEqualTo(getResourcesString("wifi_is_off"));
     }
 
@@ -404,15 +402,15 @@
     public void getSubtitleText_withNoWifiEntry_returnSearchWifi() {
         fakeAirplaneModeEnabled(false);
         when(mWifiStateWorker.isWifiEnabled()).thenReturn(true);
-        mInternetDialogController.onAccessPointsChanged(null /* accessPoints */);
+        mInternetDetailsContentController.onAccessPointsChanged(null /* accessPoints */);
 
-        assertThat(mInternetDialogController.getSubtitleText(true))
+        assertThat(mInternetDetailsContentController.getSubtitleText(true))
                 .isEqualTo(getResourcesString("wifi_empty_list_wifi_on"));
 
         // if the Wi-Fi disallow config, then don't return Wi-Fi related string.
-        mInternetDialogController.mCanConfigWifi = false;
+        mInternetDetailsContentController.mCanConfigWifi = false;
 
-        assertThat(mInternetDialogController.getSubtitleText(true))
+        assertThat(mInternetDetailsContentController.getSubtitleText(true))
                 .isNotEqualTo(getResourcesString("wifi_empty_list_wifi_on"));
     }
 
@@ -422,13 +420,13 @@
         fakeAirplaneModeEnabled(false);
         when(mWifiStateWorker.isWifiEnabled()).thenReturn(true);
 
-        assertThat(mInternetDialogController.getSubtitleText(false))
+        assertThat(mInternetDetailsContentController.getSubtitleText(false))
                 .isEqualTo(getResourcesString("tap_a_network_to_connect"));
 
         // if the Wi-Fi disallow config, then don't return Wi-Fi related string.
-        mInternetDialogController.mCanConfigWifi = false;
+        mInternetDetailsContentController.mCanConfigWifi = false;
 
-        assertThat(mInternetDialogController.getSubtitleText(false))
+        assertThat(mInternetDetailsContentController.getSubtitleText(false))
                 .isNotEqualTo(getResourcesString("tap_a_network_to_connect"));
     }
 
@@ -438,14 +436,14 @@
         when(mWifiStateWorker.isWifiEnabled()).thenReturn(true);
         when(mKeyguardStateController.isUnlocked()).thenReturn(false);
 
-        assertTrue(TextUtils.equals(mInternetDialogController.getSubtitleText(false),
+        assertTrue(TextUtils.equals(mInternetDetailsContentController.getSubtitleText(false),
                 getResourcesString("unlock_to_view_networks")));
     }
 
     @Test
     public void getSubtitleText_withNoService_returnNoNetworksAvailable() {
         mFlags.set(Flags.QS_SECONDARY_DATA_SUB_INFO, true);
-        InternetDialogController spyController = spy(mInternetDialogController);
+        InternetDetailsContentController spyController = spy(mInternetDetailsContentController);
         fakeAirplaneModeEnabled(false);
         when(mWifiStateWorker.isWifiEnabled()).thenReturn(true);
         spyController.onAccessPointsChanged(null /* accessPoints */);
@@ -466,7 +464,7 @@
 
     @Test
     public void getSubtitleText_withNoService_returnNoNetworksAvailable_flagOff() {
-        InternetDialogController spyController = spy(mInternetDialogController);
+        InternetDetailsContentController spyController = spy(mInternetDetailsContentController);
         fakeAirplaneModeEnabled(false);
         when(mWifiStateWorker.isWifiEnabled()).thenReturn(true);
         spyController.onAccessPointsChanged(null /* accessPoints */);
@@ -488,22 +486,22 @@
     public void getSubtitleText_withMobileDataDisabled_returnNoOtherAvailable() {
         fakeAirplaneModeEnabled(false);
         when(mWifiStateWorker.isWifiEnabled()).thenReturn(true);
-        mInternetDialogController.onAccessPointsChanged(null /* accessPoints */);
-        InternetDialogController spyController = spy(mInternetDialogController);
+        mInternetDetailsContentController.onAccessPointsChanged(null /* accessPoints */);
+        InternetDetailsContentController spyController = spy(mInternetDetailsContentController);
 
         doReturn(ServiceState.STATE_IN_SERVICE).when(mServiceState).getState();
         spyController.mSubIdServiceState.put(SUB_ID, mServiceState);
 
-        assertThat(mInternetDialogController.getSubtitleText(false))
+        assertThat(mInternetDetailsContentController.getSubtitleText(false))
                 .isEqualTo(getResourcesString("non_carrier_network_unavailable"));
 
         // if the Wi-Fi disallow config, then don't return Wi-Fi related string.
-        mInternetDialogController.mCanConfigWifi = false;
+        mInternetDetailsContentController.mCanConfigWifi = false;
 
         when(spyController.isMobileDataEnabled()).thenReturn(false);
 
 
-        assertThat(mInternetDialogController.getSubtitleText(false))
+        assertThat(mInternetDetailsContentController.getSubtitleText(false))
                 .isNotEqualTo(getResourcesString("non_carrier_network_unavailable"));
     }
 
@@ -511,21 +509,22 @@
     public void getSubtitleText_withCarrierNetworkActiveOnly_returnNoOtherAvailable() {
         fakeAirplaneModeEnabled(false);
         when(mWifiStateWorker.isWifiEnabled()).thenReturn(true);
-        mInternetDialogController.onAccessPointsChanged(null /* accessPoints */);
+        mInternetDetailsContentController.onAccessPointsChanged(null /* accessPoints */);
         when(mMergedCarrierEntry.isDefaultNetwork()).thenReturn(true);
 
-        assertThat(mInternetDialogController.getSubtitleText(false))
+        assertThat(mInternetDetailsContentController.getSubtitleText(false))
                 .isEqualTo(getResourcesString("non_carrier_network_unavailable"));
     }
 
     @Test
     public void getWifiDetailsSettingsIntent_withNoKey_returnNull() {
-        assertThat(mInternetDialogController.getWifiDetailsSettingsIntent(null)).isNull();
+        assertThat(mInternetDetailsContentController.getWifiDetailsSettingsIntent(null)).isNull();
     }
 
     @Test
     public void getWifiDetailsSettingsIntent_withKey_returnIntent() {
-        assertThat(mInternetDialogController.getWifiDetailsSettingsIntent("test_key")).isNotNull();
+        assertThat(mInternetDetailsContentController.getWifiDetailsSettingsIntent(
+                "test_key")).isNotNull();
     }
 
     @Test
@@ -533,7 +532,7 @@
         final Drawable drawable = mock(Drawable.class);
         when(mWifiIconInjector.getIcon(anyBoolean(), anyInt())).thenReturn(drawable);
 
-        mInternetDialogController.getInternetWifiDrawable(mConnectedEntry);
+        mInternetDetailsContentController.getInternetWifiDrawable(mConnectedEntry);
 
         verify(mWifiIconInjector).getIcon(eq(false), anyInt());
         verify(drawable).setTint(mContext.getColor(R.color.connected_network_primary_color));
@@ -543,7 +542,7 @@
     public void getWifiDrawable_withWifiLevelUnreachable_returnNull() {
         when(mConnectedEntry.getLevel()).thenReturn(WIFI_LEVEL_UNREACHABLE);
 
-        assertThat(mInternetDialogController.getWifiDrawable(mConnectedEntry)).isNull();
+        assertThat(mInternetDetailsContentController.getWifiDrawable(mConnectedEntry)).isNull();
     }
 
     @Test
@@ -553,19 +552,21 @@
         Drawable hotspotDrawable = mock(Drawable.class);
         mTestableResources.addOverride(getHotspotIconResource(DEVICE_TYPE_PHONE), hotspotDrawable);
 
-        assertThat(mInternetDialogController.getWifiDrawable(entry)).isEqualTo(hotspotDrawable);
+        assertThat(mInternetDetailsContentController.getWifiDrawable(entry)).isEqualTo(
+                hotspotDrawable);
     }
 
     @Test
     public void startActivityForDialog_always_startActivityWithoutDismissShade() {
-        mInternetDialogController.startActivityForDialog(mock(Intent.class));
+        mInternetDetailsContentController.startActivityForDialog(mock(Intent.class));
 
         verify(mActivityStarter).startActivity(any(Intent.class), eq(false) /* dismissShade */);
     }
 
     @Test
     public void launchWifiDetailsSetting_withNoWifiEntryKey_doNothing() {
-        mInternetDialogController.launchWifiDetailsSetting(null /* key */, mDialogLaunchView);
+        mInternetDetailsContentController.launchWifiDetailsSetting(null /* key */,
+                mDialogLaunchView);
 
         verify(mActivityStarter, never())
                 .postStartActivityDismissingKeyguard(any(Intent.class), anyInt());
@@ -573,7 +574,8 @@
 
     @Test
     public void launchWifiDetailsSetting_withWifiEntryKey_startActivity() {
-        mInternetDialogController.launchWifiDetailsSetting("wifi_entry_key", mDialogLaunchView);
+        mInternetDetailsContentController.launchWifiDetailsSetting("wifi_entry_key",
+                mDialogLaunchView);
 
         verify(mActivityStarter).postStartActivityDismissingKeyguard(any(Intent.class), anyInt(),
                 any());
@@ -583,22 +585,22 @@
     public void isDeviceLocked_keyguardIsUnlocked_returnFalse() {
         when(mKeyguardStateController.isUnlocked()).thenReturn(true);
 
-        assertThat(mInternetDialogController.isDeviceLocked()).isFalse();
+        assertThat(mInternetDetailsContentController.isDeviceLocked()).isFalse();
     }
 
     @Test
     public void isDeviceLocked_keyguardIsLocked_returnTrue() {
         when(mKeyguardStateController.isUnlocked()).thenReturn(false);
 
-        assertThat(mInternetDialogController.isDeviceLocked()).isTrue();
+        assertThat(mInternetDetailsContentController.isDeviceLocked()).isTrue();
     }
 
     @Test
     public void onAccessPointsChanged_canNotConfigWifi_doNothing() {
         reset(mInternetDialogCallback);
-        mInternetDialogController.mCanConfigWifi = false;
+        mInternetDetailsContentController.mCanConfigWifi = false;
 
-        mInternetDialogController.onAccessPointsChanged(null /* accessPoints */);
+        mInternetDetailsContentController.onAccessPointsChanged(null /* accessPoints */);
 
         verify(mInternetDialogCallback, never()).onAccessPointsChanged(any(), any(), anyBoolean());
     }
@@ -607,7 +609,7 @@
     public void onAccessPointsChanged_nullAccessPoints_callbackBothNull() {
         reset(mInternetDialogCallback);
 
-        mInternetDialogController.onAccessPointsChanged(null /* accessPoints */);
+        mInternetDetailsContentController.onAccessPointsChanged(null /* accessPoints */);
 
         verify(mInternetDialogCallback).onAccessPointsChanged(null /* wifiEntries */,
                 null /* connectedEntry */, false /* hasMoreEntry */);
@@ -619,7 +621,7 @@
         mAccessPoints.clear();
         mAccessPoints.add(mConnectedEntry);
 
-        mInternetDialogController.onAccessPointsChanged(mAccessPoints);
+        mInternetDetailsContentController.onAccessPointsChanged(mAccessPoints);
 
         mWifiEntries.clear();
         verify(mInternetDialogCallback).onAccessPointsChanged(mWifiEntries, mConnectedEntry,
@@ -632,7 +634,7 @@
         mAccessPoints.clear();
         mAccessPoints.add(mWifiEntry1);
 
-        mInternetDialogController.onAccessPointsChanged(mAccessPoints);
+        mInternetDetailsContentController.onAccessPointsChanged(mAccessPoints);
 
         mWifiEntries.clear();
         mWifiEntries.add(mWifiEntry1);
@@ -647,7 +649,7 @@
         mAccessPoints.add(mConnectedEntry);
         mAccessPoints.add(mWifiEntry1);
 
-        mInternetDialogController.onAccessPointsChanged(mAccessPoints);
+        mInternetDetailsContentController.onAccessPointsChanged(mAccessPoints);
 
         mWifiEntries.clear();
         mWifiEntries.add(mWifiEntry1);
@@ -663,7 +665,7 @@
         mAccessPoints.add(mWifiEntry1);
         mAccessPoints.add(mWifiEntry2);
 
-        mInternetDialogController.onAccessPointsChanged(mAccessPoints);
+        mInternetDetailsContentController.onAccessPointsChanged(mAccessPoints);
 
         mWifiEntries.clear();
         mWifiEntries.add(mWifiEntry1);
@@ -681,7 +683,7 @@
         mAccessPoints.add(mWifiEntry2);
         mAccessPoints.add(mWifiEntry3);
 
-        mInternetDialogController.onAccessPointsChanged(mAccessPoints);
+        mInternetDetailsContentController.onAccessPointsChanged(mAccessPoints);
 
         mWifiEntries.clear();
         mWifiEntries.add(mWifiEntry1);
@@ -699,7 +701,7 @@
         mAccessPoints.add(mWifiEntry3);
         mAccessPoints.add(mWifiEntry4);
 
-        mInternetDialogController.onAccessPointsChanged(mAccessPoints);
+        mInternetDetailsContentController.onAccessPointsChanged(mAccessPoints);
 
         mWifiEntries.clear();
         mWifiEntries.add(mWifiEntry1);
@@ -718,7 +720,7 @@
         when(mWifiEntry1.hasInternetAccess()).thenReturn(false);
         mAccessPoints.add(mWifiEntry1);
 
-        mInternetDialogController.onAccessPointsChanged(mAccessPoints);
+        mInternetDetailsContentController.onAccessPointsChanged(mAccessPoints);
 
         mWifiEntries.clear();
         mWifiEntries.add(mWifiEntry1);
@@ -735,9 +737,10 @@
         when(mWifiEntry1.hasInternetAccess()).thenReturn(false);
         mAccessPoints.add(mWifiEntry1);
 
-        mInternetDialogController.onAccessPointsChanged(mAccessPoints);
+        mInternetDetailsContentController.onAccessPointsChanged(mAccessPoints);
 
-        verify(mWifiEntry1).setListener(mInternetDialogController.mConnectedWifiInternetMonitor);
+        verify(mWifiEntry1).setListener(
+                mInternetDetailsContentController.mConnectedWifiInternetMonitor);
     }
 
     @Test
@@ -746,8 +749,9 @@
         when(mWifiEntry1.getConnectedState()).thenReturn(WifiEntry.CONNECTED_STATE_CONNECTED);
         when(mWifiEntry1.isDefaultNetwork()).thenReturn(true);
         when(mWifiEntry1.hasInternetAccess()).thenReturn(false);
-        InternetDialogController.ConnectedWifiInternetMonitor mConnectedWifiInternetMonitor =
-                mInternetDialogController.mConnectedWifiInternetMonitor;
+        InternetDetailsContentController.ConnectedWifiInternetMonitor
+                mConnectedWifiInternetMonitor =
+                mInternetDetailsContentController.mConnectedWifiInternetMonitor;
         mConnectedWifiInternetMonitor.registerCallbackIfNeed(mWifiEntry1);
 
         // When the hasInternetAccess() changed to true, and call back the onUpdated() function.
@@ -762,7 +766,7 @@
         reset(mInternetDialogCallback);
         when(mWifiStateWorker.isWifiEnabled()).thenReturn(false);
 
-        mInternetDialogController.onWifiScan(true);
+        mInternetDetailsContentController.onWifiScan(true);
 
         verify(mInternetDialogCallback).onWifiScan(false);
     }
@@ -772,7 +776,7 @@
         reset(mInternetDialogCallback);
         when(mKeyguardStateController.isUnlocked()).thenReturn(false);
 
-        mInternetDialogController.onWifiScan(true);
+        mInternetDetailsContentController.onWifiScan(true);
 
         verify(mInternetDialogCallback).onWifiScan(false);
     }
@@ -781,7 +785,7 @@
     public void onWifiScan_onWifiScanFalse_callbackOnWifiScanFalse() {
         reset(mInternetDialogCallback);
 
-        mInternetDialogController.onWifiScan(false);
+        mInternetDetailsContentController.onWifiScan(false);
 
         verify(mInternetDialogCallback).onWifiScan(false);
     }
@@ -790,7 +794,7 @@
     public void onWifiScan_onWifiScanTrue_callbackOnWifiScanTrue() {
         reset(mInternetDialogCallback);
 
-        mInternetDialogController.onWifiScan(true);
+        mInternetDetailsContentController.onWifiScan(true);
 
         verify(mInternetDialogCallback).onWifiScan(true);
     }
@@ -800,7 +804,7 @@
         when(mCarrierConfigTracker.getCarrierProvisionsWifiMergedNetworksBool(SUB_ID))
                 .thenReturn(true);
 
-        mInternetDialogController.setMergedCarrierWifiEnabledIfNeed(SUB_ID, true);
+        mInternetDetailsContentController.setMergedCarrierWifiEnabledIfNeed(SUB_ID, true);
 
         verify(mMergedCarrierEntry, never()).setEnabled(anyBoolean());
     }
@@ -811,7 +815,7 @@
                 .thenReturn(false);
         when(mAccessPointController.getMergedCarrierEntry()).thenReturn(null);
 
-        mInternetDialogController.setMergedCarrierWifiEnabledIfNeed(SUB_ID, true);
+        mInternetDetailsContentController.setMergedCarrierWifiEnabledIfNeed(SUB_ID, true);
     }
 
     @Test
@@ -819,11 +823,11 @@
         when(mCarrierConfigTracker.getCarrierProvisionsWifiMergedNetworksBool(SUB_ID))
                 .thenReturn(false);
 
-        mInternetDialogController.setMergedCarrierWifiEnabledIfNeed(SUB_ID, true);
+        mInternetDetailsContentController.setMergedCarrierWifiEnabledIfNeed(SUB_ID, true);
 
         verify(mMergedCarrierEntry).setEnabled(true);
 
-        mInternetDialogController.setMergedCarrierWifiEnabledIfNeed(SUB_ID, false);
+        mInternetDetailsContentController.setMergedCarrierWifiEnabledIfNeed(SUB_ID, false);
 
         verify(mMergedCarrierEntry).setEnabled(false);
     }
@@ -833,11 +837,11 @@
         when(mLocationController.isLocationEnabled()).thenReturn(false);
         when(mWifiManager.isScanAlwaysAvailable()).thenReturn(false);
 
-        assertThat(mInternetDialogController.isWifiScanEnabled()).isFalse();
+        assertThat(mInternetDetailsContentController.isWifiScanEnabled()).isFalse();
 
         when(mWifiManager.isScanAlwaysAvailable()).thenReturn(true);
 
-        assertThat(mInternetDialogController.isWifiScanEnabled()).isFalse();
+        assertThat(mInternetDetailsContentController.isWifiScanEnabled()).isFalse();
     }
 
     @Test
@@ -845,17 +849,17 @@
         when(mLocationController.isLocationEnabled()).thenReturn(true);
         when(mWifiManager.isScanAlwaysAvailable()).thenReturn(false);
 
-        assertThat(mInternetDialogController.isWifiScanEnabled()).isFalse();
+        assertThat(mInternetDetailsContentController.isWifiScanEnabled()).isFalse();
 
         when(mWifiManager.isScanAlwaysAvailable()).thenReturn(true);
 
-        assertThat(mInternetDialogController.isWifiScanEnabled()).isTrue();
+        assertThat(mInternetDetailsContentController.isWifiScanEnabled()).isTrue();
     }
 
     @Test
     public void getSignalStrengthIcon_differentSubId() {
         mFlags.set(Flags.QS_SECONDARY_DATA_SUB_INFO, true);
-        InternetDialogController spyController = spy(mInternetDialogController);
+        InternetDetailsContentController spyController = spy(mInternetDetailsContentController);
         Drawable icons = spyController.getSignalStrengthIcon(SUB_ID, mContext, 1, 1, 0, false);
         Drawable icons2 = spyController.getSignalStrengthIcon(SUB_ID2, mContext, 1, 1, 0, false);
 
@@ -870,12 +874,12 @@
         doReturn(SUB_ID2).when(info).getSubscriptionId();
         when(mSubscriptionManager.getActiveSubscriptionInfo(anyInt())).thenReturn(info);
 
-        int subId = mInternetDialogController.getActiveAutoSwitchNonDdsSubId();
+        int subId = mInternetDetailsContentController.getActiveAutoSwitchNonDdsSubId();
         assertThat(subId).isEqualTo(SUB_ID2);
 
         // active on CBRS
         doReturn(true).when(info).isOpportunistic();
-        subId = mInternetDialogController.getActiveAutoSwitchNonDdsSubId();
+        subId = mInternetDetailsContentController.getActiveAutoSwitchNonDdsSubId();
         assertThat(subId).isEqualTo(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
 
         // active on DDS
@@ -883,7 +887,7 @@
         doReturn(SUB_ID).when(info).getSubscriptionId();
         when(mSubscriptionManager.getActiveSubscriptionInfo(anyInt())).thenReturn(info);
 
-        subId = mInternetDialogController.getActiveAutoSwitchNonDdsSubId();
+        subId = mInternetDetailsContentController.getActiveAutoSwitchNonDdsSubId();
         assertThat(subId).isEqualTo(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
     }
 
@@ -894,7 +898,7 @@
         doReturn(SUB_ID2).when(info).getSubscriptionId();
         when(mSubscriptionManager.getActiveSubscriptionInfo(anyInt())).thenReturn(info);
 
-        int subId = mInternetDialogController.getActiveAutoSwitchNonDdsSubId();
+        int subId = mInternetDetailsContentController.getActiveAutoSwitchNonDdsSubId();
         assertThat(subId).isEqualTo(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
     }
 
@@ -908,22 +912,24 @@
         doReturn(false).when(info).isOpportunistic();
         when(mSubscriptionManager.getActiveSubscriptionInfo(anyInt())).thenReturn(info);
 
-        mInternetDialogController.getActiveAutoSwitchNonDdsSubId();
+        mInternetDetailsContentController.getActiveAutoSwitchNonDdsSubId();
 
         // 1st time is onStart(), 2nd time is getActiveAutoSwitchNonDdsSubId()
         verify(mTelephonyManager, times(2)).registerTelephonyCallback(any(), any());
-        assertThat(mInternetDialogController.mSubIdTelephonyCallbackMap.size()).isEqualTo(2);
+        assertThat(mInternetDetailsContentController.mSubIdTelephonyCallbackMap.size()).isEqualTo(
+                2);
 
         // Adds non DDS subId again
         doReturn(SUB_ID2).when(info).getSubscriptionId();
         doReturn(false).when(info).isOpportunistic();
         when(mSubscriptionManager.getActiveSubscriptionInfo(anyInt())).thenReturn(info);
 
-        mInternetDialogController.getActiveAutoSwitchNonDdsSubId();
+        mInternetDetailsContentController.getActiveAutoSwitchNonDdsSubId();
 
         // Does not add due to cached subInfo in mSubIdTelephonyCallbackMap.
         verify(mTelephonyManager, times(2)).registerTelephonyCallback(any(), any());
-        assertThat(mInternetDialogController.mSubIdTelephonyCallbackMap.size()).isEqualTo(2);
+        assertThat(mInternetDetailsContentController.mSubIdTelephonyCallbackMap.size()).isEqualTo(
+                2);
     }
 
     @Test
@@ -936,7 +942,7 @@
         when(SubscriptionManager.getResourcesForSubId(any(), eq(SUB_ID))).thenReturn(res1);
         when(SubscriptionManager.getResourcesForSubId(any(), eq(SUB_ID2))).thenReturn(res2);
 
-        InternetDialogController spyController = spy(mInternetDialogController);
+        InternetDetailsContentController spyController = spy(mInternetDetailsContentController);
         Map<Integer, TelephonyDisplayInfo> mSubIdTelephonyDisplayInfoMap =
                 spyController.mSubIdTelephonyDisplayInfoMap;
         TelephonyDisplayInfo info1 = new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_EDGE,
@@ -961,7 +967,7 @@
 
     @Test
     public void getMobileNetworkSummary_flagOff() {
-        InternetDialogController spyController = spy(mInternetDialogController);
+        InternetDetailsContentController spyController = spy(mInternetDetailsContentController);
         doReturn(true).when(spyController).isMobileDataEnabled();
         doReturn(true).when(spyController).activeNetworkIsCellular();
         String dds = spyController.getMobileNetworkSummary(SUB_ID);
@@ -972,7 +978,7 @@
     @Test
     public void launchMobileNetworkSettings_validSubId() {
         mFlags.set(Flags.QS_SECONDARY_DATA_SUB_INFO, true);
-        InternetDialogController spyController = spy(mInternetDialogController);
+        InternetDetailsContentController spyController = spy(mInternetDetailsContentController);
         doReturn(SUB_ID2).when(spyController).getActiveAutoSwitchNonDdsSubId();
         spyController.launchMobileNetworkSettings(mDialogLaunchView);
 
@@ -983,7 +989,7 @@
     @Test
     public void launchMobileNetworkSettings_invalidSubId() {
         mFlags.set(Flags.QS_SECONDARY_DATA_SUB_INFO, true);
-        InternetDialogController spyController = spy(mInternetDialogController);
+        InternetDetailsContentController spyController = spy(mInternetDetailsContentController);
         doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
                 .when(spyController).getActiveAutoSwitchNonDdsSubId();
         spyController.launchMobileNetworkSettings(mDialogLaunchView);
@@ -995,7 +1001,7 @@
     @Test
     public void setAutoDataSwitchMobileDataPolicy() {
         mFlags.set(Flags.QS_SECONDARY_DATA_SUB_INFO, true);
-        mInternetDialogController.setAutoDataSwitchMobileDataPolicy(SUB_ID, true);
+        mInternetDetailsContentController.setAutoDataSwitchMobileDataPolicy(SUB_ID, true);
 
         verify(mTelephonyManager).setMobileDataPolicyEnabled(eq(
                 TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH), eq(true));
@@ -1006,9 +1012,9 @@
         // Fake mobile data level as SIGNAL_STRENGTH_POOR(1)
         when(mSignalStrength.getLevel()).thenReturn(SIGNAL_STRENGTH_POOR);
         // Fake carrier network level as WIFI_LEVEL_MAX(4)
-        when(mInternetDialogController.getCarrierNetworkLevel()).thenReturn(WIFI_LEVEL_MAX);
+        when(mInternetDetailsContentController.getCarrierNetworkLevel()).thenReturn(WIFI_LEVEL_MAX);
 
-        InternetDialogController spyController = spy(mInternetDialogController);
+        InternetDetailsContentController spyController = spy(mInternetDetailsContentController);
         spyController.getSignalStrengthDrawableWithLevel(false /* isCarrierNetworkActive */, 0);
 
         verify(spyController).getSignalStrengthIcon(eq(0), any(), eq(SIGNAL_STRENGTH_POOR),
@@ -1020,9 +1026,9 @@
         // Fake mobile data level as SIGNAL_STRENGTH_POOR(1)
         when(mSignalStrength.getLevel()).thenReturn(SIGNAL_STRENGTH_POOR);
         // Fake carrier network level as WIFI_LEVEL_MAX(4)
-        when(mInternetDialogController.getCarrierNetworkLevel()).thenReturn(WIFI_LEVEL_MAX);
+        when(mInternetDetailsContentController.getCarrierNetworkLevel()).thenReturn(WIFI_LEVEL_MAX);
 
-        InternetDialogController spyController = spy(mInternetDialogController);
+        InternetDetailsContentController spyController = spy(mInternetDetailsContentController);
         spyController.getSignalStrengthDrawableWithLevel(true /* isCarrierNetworkActive */, 0);
 
         verify(spyController).getSignalStrengthIcon(eq(0), any(), eq(WIFI_LEVEL_MAX),
@@ -1033,14 +1039,16 @@
     public void getCarrierNetworkLevel_mergedCarrierEntryIsNull_returnMinLevel() {
         when(mAccessPointController.getMergedCarrierEntry()).thenReturn(null);
 
-        assertThat(mInternetDialogController.getCarrierNetworkLevel()).isEqualTo(WIFI_LEVEL_MIN);
+        assertThat(mInternetDetailsContentController.getCarrierNetworkLevel()).isEqualTo(
+                WIFI_LEVEL_MIN);
     }
 
     @Test
     public void getCarrierNetworkLevel_getUnreachableLevel_returnMinLevel() {
         when(mMergedCarrierEntry.getLevel()).thenReturn(WIFI_LEVEL_UNREACHABLE);
 
-        assertThat(mInternetDialogController.getCarrierNetworkLevel()).isEqualTo(WIFI_LEVEL_MIN);
+        assertThat(mInternetDetailsContentController.getCarrierNetworkLevel()).isEqualTo(
+                WIFI_LEVEL_MIN);
     }
 
     @Test
@@ -1048,7 +1056,7 @@
         for (int level = WIFI_LEVEL_MIN; level <= WIFI_LEVEL_MAX; level++) {
             when(mMergedCarrierEntry.getLevel()).thenReturn(level);
 
-            assertThat(mInternetDialogController.getCarrierNetworkLevel()).isEqualTo(level);
+            assertThat(mInternetDetailsContentController.getCarrierNetworkLevel()).isEqualTo(level);
         }
     }
 
@@ -1057,7 +1065,7 @@
         Resources res = mock(Resources.class);
         doReturn("Carrier network changing").when(res).getString(anyInt());
         when(SubscriptionManager.getResourcesForSubId(any(), eq(SUB_ID))).thenReturn(res);
-        InternetDialogController spyController = spy(mInternetDialogController);
+        InternetDetailsContentController spyController = spy(mInternetDetailsContentController);
         Map<Integer, TelephonyDisplayInfo> mSubIdTelephonyDisplayInfoMap =
                 spyController.mSubIdTelephonyDisplayInfoMap;
         TelephonyDisplayInfo info = new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_LTE,
@@ -1076,14 +1084,14 @@
     public void getConfiguratorQrCodeGeneratorIntentOrNull_wifiNotShareable_returnNull() {
         mFlags.set(Flags.SHARE_WIFI_QS_BUTTON, true);
         when(mConnectedEntry.canShare()).thenReturn(false);
-        assertThat(mInternetDialogController.getConfiguratorQrCodeGeneratorIntentOrNull(
+        assertThat(mInternetDetailsContentController.getConfiguratorQrCodeGeneratorIntentOrNull(
                 mConnectedEntry)).isNull();
     }
     @Test
     public void getConfiguratorQrCodeGeneratorIntentOrNull_flagOff_returnNull() {
         mFlags.set(Flags.SHARE_WIFI_QS_BUTTON, false);
         when(mConnectedEntry.canShare()).thenReturn(true);
-        assertThat(mInternetDialogController.getConfiguratorQrCodeGeneratorIntentOrNull(
+        assertThat(mInternetDetailsContentController.getConfiguratorQrCodeGeneratorIntentOrNull(
                 mConnectedEntry)).isNull();
     }
 
@@ -1092,7 +1100,7 @@
         mFlags.set(Flags.SHARE_WIFI_QS_BUTTON, true);
         when(mConnectedEntry.canShare()).thenReturn(true);
         when(mConnectedEntry.getWifiConfiguration()).thenReturn(null);
-        assertThat(mInternetDialogController.getConfiguratorQrCodeGeneratorIntentOrNull(
+        assertThat(mInternetDetailsContentController.getConfiguratorQrCodeGeneratorIntentOrNull(
                 mConnectedEntry)).isNull();
     }
 
@@ -1101,30 +1109,34 @@
         mFlags.set(Flags.SHARE_WIFI_QS_BUTTON, true);
         when(mConnectedEntry.canShare()).thenReturn(true);
         when(mConnectedEntry.getWifiConfiguration()).thenReturn(mWifiConfiguration);
-        assertThat(mInternetDialogController.getConfiguratorQrCodeGeneratorIntentOrNull(
+        assertThat(mInternetDetailsContentController.getConfiguratorQrCodeGeneratorIntentOrNull(
                 mConnectedEntry)).isNotNull();
     }
 
     @Test
     public void onStop_cleanUp() {
         doReturn(SUB_ID).when(mTelephonyManager).getSubscriptionId();
-        assertThat(mInternetDialogController.mSubIdTelephonyManagerMap.get(SUB_ID)).isEqualTo(
+        assertThat(
+                mInternetDetailsContentController.mSubIdTelephonyManagerMap.get(SUB_ID)).isEqualTo(
                 mTelephonyManager);
-        assertThat(mInternetDialogController.mSubIdTelephonyCallbackMap.get(SUB_ID)).isNotNull();
-        assertThat(mInternetDialogController.mCallback).isNotNull();
+        assertThat(mInternetDetailsContentController.mSubIdTelephonyCallbackMap.get(
+                SUB_ID)).isNotNull();
+        assertThat(mInternetDetailsContentController.mCallback).isNotNull();
 
-        mInternetDialogController.onStop();
+        mInternetDetailsContentController.onStop();
 
         verify(mTelephonyManager).unregisterTelephonyCallback(any(TelephonyCallback.class));
-        assertThat(mInternetDialogController.mSubIdTelephonyDisplayInfoMap.isEmpty()).isTrue();
-        assertThat(mInternetDialogController.mSubIdTelephonyManagerMap.isEmpty()).isTrue();
-        assertThat(mInternetDialogController.mSubIdTelephonyCallbackMap.isEmpty()).isTrue();
-        verify(mSubscriptionManager).removeOnSubscriptionsChangedListener(mInternetDialogController
-                .mOnSubscriptionsChangedListener);
-        verify(mAccessPointController).removeAccessPointCallback(mInternetDialogController);
+        assertThat(
+                mInternetDetailsContentController.mSubIdTelephonyDisplayInfoMap.isEmpty()).isTrue();
+        assertThat(mInternetDetailsContentController.mSubIdTelephonyManagerMap.isEmpty()).isTrue();
+        assertThat(mInternetDetailsContentController.mSubIdTelephonyCallbackMap.isEmpty()).isTrue();
+        verify(mSubscriptionManager).removeOnSubscriptionsChangedListener(
+                mInternetDetailsContentController
+                        .mOnSubscriptionsChangedListener);
+        verify(mAccessPointController).removeAccessPointCallback(mInternetDetailsContentController);
         verify(mConnectivityManager).unregisterNetworkCallback(
                 any(ConnectivityManager.NetworkCallback.class));
-        assertThat(mInternetDialogController.mCallback).isNull();
+        assertThat(mInternetDetailsContentController.mCallback).isNull();
     }
 
     @Test
@@ -1132,16 +1144,16 @@
         when(SubscriptionManager.getDefaultDataSubscriptionId())
                 .thenReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID);
 
-        mInternetDialogController.mOnSubscriptionsChangedListener.onSubscriptionsChanged();
+        mInternetDetailsContentController.mOnSubscriptionsChangedListener.onSubscriptionsChanged();
 
-        assertThat(mInternetDialogController.hasActiveSubIdOnDds()).isFalse();
+        assertThat(mInternetDetailsContentController.hasActiveSubIdOnDds()).isFalse();
     }
 
     @Test
     public void hasActiveSubIdOnDds_activeDds_returnTrue() {
-        mInternetDialogController.mOnSubscriptionsChangedListener.onSubscriptionsChanged();
+        mInternetDetailsContentController.mOnSubscriptionsChangedListener.onSubscriptionsChanged();
 
-        assertThat(mInternetDialogController.hasActiveSubIdOnDds()).isTrue();
+        assertThat(mInternetDetailsContentController.hasActiveSubIdOnDds()).isTrue();
     }
 
     @Test
@@ -1153,9 +1165,9 @@
         when(info.getProfileClass()).thenReturn(PROFILE_CLASS_PROVISIONING);
         when(mSubscriptionManager.getActiveSubscriptionInfo(SUB_ID)).thenReturn(info);
 
-        mInternetDialogController.mOnSubscriptionsChangedListener.onSubscriptionsChanged();
+        mInternetDetailsContentController.mOnSubscriptionsChangedListener.onSubscriptionsChanged();
 
-        assertThat(mInternetDialogController.hasActiveSubIdOnDds()).isFalse();
+        assertThat(mInternetDetailsContentController.hasActiveSubIdOnDds()).isFalse();
     }
 
     @Test
@@ -1167,9 +1179,9 @@
         when(info.isOnlyNonTerrestrialNetwork()).thenReturn(true);
         when(mSubscriptionManager.getActiveSubscriptionInfo(SUB_ID)).thenReturn(info);
 
-        mInternetDialogController.mOnSubscriptionsChangedListener.onSubscriptionsChanged();
+        mInternetDetailsContentController.mOnSubscriptionsChangedListener.onSubscriptionsChanged();
 
-        assertFalse(mInternetDialogController.hasActiveSubIdOnDds());
+        assertFalse(mInternetDetailsContentController.hasActiveSubIdOnDds());
     }
 
     @Test
@@ -1181,9 +1193,9 @@
         when(info.isOnlyNonTerrestrialNetwork()).thenReturn(false);
         when(mSubscriptionManager.getActiveSubscriptionInfo(SUB_ID)).thenReturn(info);
 
-        mInternetDialogController.mOnSubscriptionsChangedListener.onSubscriptionsChanged();
+        mInternetDetailsContentController.mOnSubscriptionsChangedListener.onSubscriptionsChanged();
 
-        assertTrue(mInternetDialogController.hasActiveSubIdOnDds());
+        assertTrue(mInternetDetailsContentController.hasActiveSubIdOnDds());
     }
 
     private String getResourcesString(String name) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentManagerTest.kt
new file mode 100644
index 0000000..a192446
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDetailsContentManagerTest.kt
@@ -0,0 +1,819 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.dialog
+
+import android.content.Intent
+import android.os.Handler
+import android.os.fakeExecutorHandler
+import android.telephony.SubscriptionManager
+import android.telephony.TelephonyManager
+import android.telephony.telephonyManager
+import android.testing.TestableLooper.RunWithLooper
+import android.view.LayoutInflater
+import android.view.View
+import android.widget.Button
+import android.widget.LinearLayout
+import android.widget.Switch
+import android.widget.TextView
+import androidx.recyclerview.widget.RecyclerView
+import androidx.test.annotation.UiThreadTest
+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.UiEventLogger
+import com.android.settingslib.wifi.WifiEnterpriseRestrictionUtils
+import com.android.systemui.Flags
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.animation.DialogTransitionAnimator
+import com.android.systemui.flags.setFlagValue
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.res.R
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.time.FakeSystemClock
+import com.android.wifitrackerlib.WifiEntry
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.CoroutineScope
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers
+import org.mockito.MockitoSession
+import org.mockito.kotlin.clearInvocations
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+@RunWithLooper(setAsMainLooper = true)
+@UiThreadTest
+class InternetDetailsContentManagerTest : SysuiTestCase() {
+    private val kosmos = Kosmos()
+    private val handler: Handler = kosmos.fakeExecutorHandler
+    private val scope: CoroutineScope = mock<CoroutineScope>()
+    private val telephonyManager: TelephonyManager = kosmos.telephonyManager
+    private val internetWifiEntry: WifiEntry = mock<WifiEntry>()
+    private val wifiEntries: List<WifiEntry> = mock<List<WifiEntry>>()
+    private val internetAdapter = mock<InternetAdapter>()
+    private val internetDetailsContentController: InternetDetailsContentController =
+        mock<InternetDetailsContentController>()
+    private val keyguard: KeyguardStateController = mock<KeyguardStateController>()
+    private val dialogTransitionAnimator: DialogTransitionAnimator =
+        mock<DialogTransitionAnimator>()
+    private val bgExecutor = FakeExecutor(FakeSystemClock())
+    private lateinit var internetDetailsContentManager: InternetDetailsContentManager
+    private var subTitle: View? = null
+    private var ethernet: LinearLayout? = null
+    private var mobileDataLayout: LinearLayout? = null
+    private var mobileToggleSwitch: Switch? = null
+    private var wifiToggle: LinearLayout? = null
+    private var wifiToggleSwitch: Switch? = null
+    private var wifiToggleSummary: TextView? = null
+    private var connectedWifi: LinearLayout? = null
+    private var wifiList: RecyclerView? = null
+    private var seeAll: LinearLayout? = null
+    private var wifiScanNotify: LinearLayout? = null
+    private var airplaneModeSummaryText: TextView? = null
+    private var mockitoSession: MockitoSession? = null
+    private var sharedWifiButton: Button? = null
+    private lateinit var contentView: View
+
+    @Before
+    fun setUp() {
+        // TODO: b/377388104 enable this flag after integrating with details view.
+        mSetFlagsRule.setFlagValue(Flags.FLAG_QS_TILE_DETAILED_VIEW, false)
+        whenever(telephonyManager.createForSubscriptionId(ArgumentMatchers.anyInt()))
+            .thenReturn(telephonyManager)
+        whenever(internetWifiEntry.title).thenReturn(WIFI_TITLE)
+        whenever(internetWifiEntry.getSummary(false)).thenReturn(WIFI_SUMMARY)
+        whenever(internetWifiEntry.isDefaultNetwork).thenReturn(true)
+        whenever(internetWifiEntry.hasInternetAccess()).thenReturn(true)
+        whenever(wifiEntries.size).thenReturn(1)
+        whenever(internetDetailsContentController.getDialogTitleText()).thenReturn(TITLE)
+        whenever(internetDetailsContentController.getMobileNetworkTitle(ArgumentMatchers.anyInt()))
+            .thenReturn(MOBILE_NETWORK_TITLE)
+        whenever(
+                internetDetailsContentController.getMobileNetworkSummary(ArgumentMatchers.anyInt())
+            )
+            .thenReturn(MOBILE_NETWORK_SUMMARY)
+        whenever(internetDetailsContentController.isWifiEnabled).thenReturn(true)
+        whenever(internetDetailsContentController.activeAutoSwitchNonDdsSubId)
+            .thenReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
+        mockitoSession =
+            ExtendedMockito.mockitoSession()
+                .spyStatic(WifiEnterpriseRestrictionUtils::class.java)
+                .startMocking()
+        whenever(WifiEnterpriseRestrictionUtils.isChangeWifiStateAllowed(mContext)).thenReturn(true)
+        createView()
+    }
+
+    private fun createView() {
+        contentView =
+            LayoutInflater.from(mContext).inflate(R.layout.internet_connectivity_dialog, null)
+        internetDetailsContentManager =
+            InternetDetailsContentManager(
+                internetDetailsContentController,
+                canConfigMobileData = true,
+                canConfigWifi = true,
+                coroutineScope = scope,
+                context = mContext,
+                internetDialog = null,
+                uiEventLogger = mock<UiEventLogger>(),
+                dialogTransitionAnimator = dialogTransitionAnimator,
+                handler = handler,
+                backgroundExecutor = bgExecutor,
+                keyguard = keyguard,
+            )
+
+        internetDetailsContentManager.bind(contentView)
+        internetDetailsContentManager.adapter = internetAdapter
+        internetDetailsContentManager.connectedWifiEntry = internetWifiEntry
+        internetDetailsContentManager.wifiEntriesCount = wifiEntries.size
+
+        subTitle = contentView.requireViewById(R.id.internet_dialog_subtitle)
+        ethernet = contentView.requireViewById(R.id.ethernet_layout)
+        mobileDataLayout = contentView.requireViewById(R.id.mobile_network_layout)
+        mobileToggleSwitch = contentView.requireViewById(R.id.mobile_toggle)
+        wifiToggle = contentView.requireViewById(R.id.turn_on_wifi_layout)
+        wifiToggleSwitch = contentView.requireViewById(R.id.wifi_toggle)
+        wifiToggleSummary = contentView.requireViewById(R.id.wifi_toggle_summary)
+        connectedWifi = contentView.requireViewById(R.id.wifi_connected_layout)
+        wifiList = contentView.requireViewById(R.id.wifi_list_layout)
+        seeAll = contentView.requireViewById(R.id.see_all_layout)
+        wifiScanNotify = contentView.requireViewById(R.id.wifi_scan_notify_layout)
+        airplaneModeSummaryText = contentView.requireViewById(R.id.airplane_mode_summary)
+        sharedWifiButton = contentView.requireViewById(R.id.share_wifi_button)
+    }
+
+    @After
+    fun tearDown() {
+        internetDetailsContentManager.unBind()
+        mockitoSession!!.finishMocking()
+    }
+
+    @Test
+    fun createView_setAccessibilityPaneTitleToQuickSettings() {
+        assertThat(contentView.accessibilityPaneTitle)
+            .isEqualTo(mContext.getText(R.string.accessibility_desc_quick_settings))
+    }
+
+    @Test
+    fun hideWifiViews_WifiViewsGone() {
+        internetDetailsContentManager.hideWifiViews()
+
+        assertThat(internetDetailsContentManager.isProgressBarVisible).isFalse()
+        assertThat(wifiToggle!!.visibility).isEqualTo(View.GONE)
+        assertThat(connectedWifi!!.visibility).isEqualTo(View.GONE)
+        assertThat(wifiList!!.visibility).isEqualTo(View.GONE)
+        assertThat(seeAll!!.visibility).isEqualTo(View.GONE)
+    }
+
+    @Test
+    fun updateContent_withApmOn_internetDialogSubTitleGone() {
+        whenever(internetDetailsContentController.isAirplaneModeEnabled).thenReturn(true)
+        internetDetailsContentManager.updateContent(true)
+        bgExecutor.runAllReady()
+
+        internetDetailsContentManager.internetContentData.observe(
+            internetDetailsContentManager.lifecycleOwner!!
+        ) {
+            assertThat(subTitle!!.visibility).isEqualTo(View.VISIBLE)
+        }
+    }
+
+    @Test
+    fun updateContent_withApmOff_internetDialogSubTitleVisible() {
+        whenever(internetDetailsContentController.isAirplaneModeEnabled).thenReturn(false)
+        internetDetailsContentManager.updateContent(true)
+        bgExecutor.runAllReady()
+
+        internetDetailsContentManager.internetContentData.observe(
+            internetDetailsContentManager.lifecycleOwner!!
+        ) {
+            assertThat(subTitle!!.visibility).isEqualTo(View.VISIBLE)
+        }
+    }
+
+    @Test
+    fun updateContent_apmOffAndHasEthernet_showEthernet() {
+        whenever(internetDetailsContentController.isAirplaneModeEnabled).thenReturn(false)
+        whenever(internetDetailsContentController.hasEthernet()).thenReturn(true)
+        internetDetailsContentManager.updateContent(true)
+        bgExecutor.runAllReady()
+
+        internetDetailsContentManager.internetContentData.observe(
+            internetDetailsContentManager.lifecycleOwner!!
+        ) {
+            assertThat(ethernet!!.visibility).isEqualTo(View.VISIBLE)
+        }
+    }
+
+    @Test
+    fun updateContent_apmOffAndNoEthernet_hideEthernet() {
+        whenever(internetDetailsContentController.isAirplaneModeEnabled).thenReturn(false)
+        whenever(internetDetailsContentController.hasEthernet()).thenReturn(false)
+        internetDetailsContentManager.updateContent(true)
+        bgExecutor.runAllReady()
+
+        internetDetailsContentManager.internetContentData.observe(
+            internetDetailsContentManager.lifecycleOwner!!
+        ) {
+            assertThat(ethernet!!.visibility).isEqualTo(View.GONE)
+        }
+    }
+
+    @Test
+    fun updateContent_apmOnAndHasEthernet_showEthernet() {
+        whenever(internetDetailsContentController.isAirplaneModeEnabled).thenReturn(true)
+        whenever(internetDetailsContentController.hasEthernet()).thenReturn(true)
+        internetDetailsContentManager.updateContent(true)
+        bgExecutor.runAllReady()
+
+        internetDetailsContentManager.internetContentData.observe(
+            internetDetailsContentManager.lifecycleOwner!!
+        ) {
+            assertThat(ethernet!!.visibility).isEqualTo(View.VISIBLE)
+        }
+    }
+
+    @Test
+    fun updateContent_apmOnAndNoEthernet_hideEthernet() {
+        whenever(internetDetailsContentController.isAirplaneModeEnabled).thenReturn(true)
+        whenever(internetDetailsContentController.hasEthernet()).thenReturn(false)
+        internetDetailsContentManager.updateContent(true)
+        bgExecutor.runAllReady()
+
+        internetDetailsContentManager.internetContentData.observe(
+            internetDetailsContentManager.lifecycleOwner!!
+        ) {
+            assertThat(ethernet!!.visibility).isEqualTo(View.GONE)
+        }
+    }
+
+    @Test
+    fun updateContent_apmOffAndNotCarrierNetwork_mobileDataLayoutGone() {
+        // Mobile network should be gone if the list of active subscriptionId is null.
+        whenever(internetDetailsContentController.isCarrierNetworkActive).thenReturn(false)
+        whenever(internetDetailsContentController.isAirplaneModeEnabled).thenReturn(false)
+        whenever(internetDetailsContentController.hasActiveSubIdOnDds()).thenReturn(false)
+        internetDetailsContentManager.updateContent(true)
+        bgExecutor.runAllReady()
+
+        internetDetailsContentManager.internetContentData.observe(
+            internetDetailsContentManager.lifecycleOwner!!
+        ) {
+            assertThat(mobileDataLayout!!.visibility).isEqualTo(View.GONE)
+        }
+    }
+
+    @Test
+    fun updateContent_apmOnWithCarrierNetworkAndWifiStatus_mobileDataLayoutVisible() {
+        // Carrier network should be visible if airplane mode ON and Wi-Fi is ON.
+        whenever(internetDetailsContentController.isCarrierNetworkActive).thenReturn(true)
+        whenever(internetDetailsContentController.isAirplaneModeEnabled).thenReturn(true)
+        whenever(internetDetailsContentController.isWifiEnabled).thenReturn(true)
+        internetDetailsContentManager.updateContent(true)
+        bgExecutor.runAllReady()
+
+        internetDetailsContentManager.internetContentData.observe(
+            internetDetailsContentManager.lifecycleOwner!!
+        ) {
+            assertThat(mobileDataLayout!!.visibility).isEqualTo(View.VISIBLE)
+        }
+    }
+
+    @Test
+    fun updateContent_apmOnWithCarrierNetworkAndWifiStatus_mobileDataLayoutGone() {
+        // Carrier network should be gone if airplane mode ON and Wi-Fi is off.
+        whenever(internetDetailsContentController.isCarrierNetworkActive).thenReturn(true)
+        whenever(internetDetailsContentController.isAirplaneModeEnabled).thenReturn(true)
+        whenever(internetDetailsContentController.isWifiEnabled).thenReturn(false)
+        internetDetailsContentManager.updateContent(true)
+        bgExecutor.runAllReady()
+
+        internetDetailsContentManager.internetContentData.observe(
+            internetDetailsContentManager.lifecycleOwner!!
+        ) {
+            assertThat(mobileDataLayout!!.visibility).isEqualTo(View.GONE)
+        }
+    }
+
+    @Test
+    fun updateContent_apmOnAndNoCarrierNetwork_mobileDataLayoutGone() {
+        whenever(internetDetailsContentController.isCarrierNetworkActive).thenReturn(false)
+        whenever(internetDetailsContentController.isAirplaneModeEnabled).thenReturn(true)
+        internetDetailsContentManager.updateContent(true)
+        bgExecutor.runAllReady()
+
+        internetDetailsContentManager.internetContentData.observe(
+            internetDetailsContentManager.lifecycleOwner!!
+        ) {
+            assertThat(mobileDataLayout!!.visibility).isEqualTo(View.GONE)
+        }
+    }
+
+    @Test
+    fun updateContent_apmOnAndWifiOnHasCarrierNetwork_showAirplaneSummary() {
+        whenever(internetDetailsContentController.isCarrierNetworkActive).thenReturn(true)
+        whenever(internetDetailsContentController.isAirplaneModeEnabled).thenReturn(true)
+        internetDetailsContentManager.connectedWifiEntry = null
+        whenever(internetDetailsContentController.activeNetworkIsCellular()).thenReturn(false)
+        internetDetailsContentManager.updateContent(true)
+        bgExecutor.runAllReady()
+
+        internetDetailsContentManager.internetContentData.observe(
+            internetDetailsContentManager.lifecycleOwner!!
+        ) {
+            assertThat(mobileDataLayout!!.visibility).isEqualTo(View.VISIBLE)
+            assertThat(airplaneModeSummaryText!!.visibility).isEqualTo(View.VISIBLE)
+        }
+    }
+
+    @Test
+    fun updateContent_apmOffAndWifiOnHasCarrierNetwork_notShowApmSummary() {
+        whenever(internetDetailsContentController.isCarrierNetworkActive).thenReturn(true)
+        whenever(internetDetailsContentController.isAirplaneModeEnabled).thenReturn(false)
+        internetDetailsContentManager.connectedWifiEntry = null
+        whenever(internetDetailsContentController.activeNetworkIsCellular()).thenReturn(false)
+        internetDetailsContentManager.updateContent(true)
+        bgExecutor.runAllReady()
+
+        internetDetailsContentManager.internetContentData.observe(
+            internetDetailsContentManager.lifecycleOwner!!
+        ) {
+            assertThat(airplaneModeSummaryText!!.visibility).isEqualTo(View.GONE)
+        }
+    }
+
+    @Test
+    fun updateContent_apmOffAndHasCarrierNetwork_notShowApmSummary() {
+        whenever(internetDetailsContentController.isCarrierNetworkActive).thenReturn(true)
+        whenever(internetDetailsContentController.isAirplaneModeEnabled).thenReturn(false)
+        internetDetailsContentManager.updateContent(true)
+        bgExecutor.runAllReady()
+
+        internetDetailsContentManager.internetContentData.observe(
+            internetDetailsContentManager.lifecycleOwner!!
+        ) {
+            assertThat(airplaneModeSummaryText!!.visibility).isEqualTo(View.GONE)
+        }
+    }
+
+    @Test
+    fun updateContent_apmOnAndNoCarrierNetwork_notShowApmSummary() {
+        whenever(internetDetailsContentController.isCarrierNetworkActive).thenReturn(false)
+        whenever(internetDetailsContentController.isAirplaneModeEnabled).thenReturn(true)
+        internetDetailsContentManager.updateContent(true)
+        bgExecutor.runAllReady()
+
+        internetDetailsContentManager.internetContentData.observe(
+            internetDetailsContentManager.lifecycleOwner!!
+        ) {
+            assertThat(airplaneModeSummaryText!!.visibility).isEqualTo(View.GONE)
+        }
+    }
+
+    @Test
+    fun updateContent_mobileDataIsEnabled_checkMobileDataSwitch() {
+        whenever(internetDetailsContentController.hasActiveSubIdOnDds()).thenReturn(true)
+        whenever(internetDetailsContentController.isCarrierNetworkActive).thenReturn(true)
+        whenever(internetDetailsContentController.isMobileDataEnabled).thenReturn(true)
+        mobileToggleSwitch!!.isChecked = false
+        internetDetailsContentManager.updateContent(true)
+        bgExecutor.runAllReady()
+
+        internetDetailsContentManager.internetContentData.observe(
+            internetDetailsContentManager.lifecycleOwner!!
+        ) {
+            assertThat(mobileToggleSwitch!!.isChecked).isTrue()
+        }
+    }
+
+    @Test
+    fun updateContent_mobileDataIsNotChanged_checkMobileDataSwitch() {
+        whenever(internetDetailsContentController.hasActiveSubIdOnDds()).thenReturn(true)
+        whenever(internetDetailsContentController.isCarrierNetworkActive).thenReturn(true)
+        whenever(internetDetailsContentController.isMobileDataEnabled).thenReturn(false)
+        mobileToggleSwitch!!.isChecked = false
+        internetDetailsContentManager.updateContent(true)
+        bgExecutor.runAllReady()
+
+        internetDetailsContentManager.internetContentData.observe(
+            internetDetailsContentManager.lifecycleOwner!!
+        ) {
+            assertThat(mobileToggleSwitch!!.isChecked).isFalse()
+        }
+    }
+
+    @Test
+    fun updateContent_wifiOnAndHasInternetWifi_showConnectedWifi() {
+        whenever(internetDetailsContentController.activeAutoSwitchNonDdsSubId).thenReturn(1)
+        whenever(internetDetailsContentController.hasActiveSubIdOnDds()).thenReturn(true)
+
+        // The preconditions WiFi ON and Internet WiFi are already in setUp()
+        whenever(internetDetailsContentController.activeNetworkIsCellular()).thenReturn(false)
+
+        internetDetailsContentManager.updateContent(true)
+        bgExecutor.runAllReady()
+
+        internetDetailsContentManager.internetContentData.observe(
+            internetDetailsContentManager.lifecycleOwner!!
+        ) {
+            assertThat(connectedWifi!!.visibility).isEqualTo(View.VISIBLE)
+            val secondaryLayout =
+                contentView.requireViewById<LinearLayout>(R.id.secondary_mobile_network_layout)
+            assertThat(secondaryLayout.visibility).isEqualTo(View.GONE)
+        }
+    }
+
+    @Test
+    fun updateContent_wifiOnAndNoConnectedWifi_hideConnectedWifi() {
+        // The precondition WiFi ON is already in setUp()
+        internetDetailsContentManager.connectedWifiEntry = null
+        whenever(internetDetailsContentController.activeNetworkIsCellular()).thenReturn(false)
+
+        internetDetailsContentManager.updateContent(false)
+        bgExecutor.runAllReady()
+
+        internetDetailsContentManager.internetContentData.observe(
+            internetDetailsContentManager.lifecycleOwner!!
+        ) {
+            assertThat(connectedWifi!!.visibility).isEqualTo(View.GONE)
+        }
+    }
+
+    @Test
+    fun updateContent_wifiOnAndNoWifiEntry_showWifiListAndSeeAllArea() {
+        // The precondition WiFi ON is already in setUp()
+        internetDetailsContentManager.connectedWifiEntry = null
+        internetDetailsContentManager.wifiEntriesCount = 0
+        internetDetailsContentManager.updateContent(false)
+        bgExecutor.runAllReady()
+
+        internetDetailsContentManager.internetContentData.observe(
+            internetDetailsContentManager.lifecycleOwner!!
+        ) {
+            assertThat(connectedWifi!!.visibility).isEqualTo(View.GONE)
+            // Show a blank block to fix the details content height even if there is no WiFi list
+            assertThat(wifiList!!.visibility).isEqualTo(View.VISIBLE)
+            verify(internetAdapter).setMaxEntriesCount(3)
+            assertThat(seeAll!!.visibility).isEqualTo(View.INVISIBLE)
+        }
+    }
+
+    @Test
+    fun updateContent_wifiOnAndOneWifiEntry_showWifiListAndSeeAllArea() {
+        // The precondition WiFi ON is already in setUp()
+        internetDetailsContentManager.connectedWifiEntry = null
+        internetDetailsContentManager.wifiEntriesCount = 1
+        internetDetailsContentManager.updateContent(false)
+        bgExecutor.runAllReady()
+
+        internetDetailsContentManager.internetContentData.observe(
+            internetDetailsContentManager.lifecycleOwner!!
+        ) {
+            assertThat(connectedWifi!!.visibility).isEqualTo(View.GONE)
+            // Show a blank block to fix the details content height even if there is no WiFi list
+            assertThat(wifiList!!.visibility).isEqualTo(View.VISIBLE)
+            verify(internetAdapter).setMaxEntriesCount(3)
+            assertThat(seeAll!!.visibility).isEqualTo(View.INVISIBLE)
+        }
+    }
+
+    @Test
+    fun updateContent_wifiOnAndHasConnectedWifi_showAllWifiAndSeeAllArea() {
+        // The preconditions WiFi ON and WiFi entries are already in setUp()
+        internetDetailsContentManager.wifiEntriesCount = 0
+        internetDetailsContentManager.updateContent(false)
+        bgExecutor.runAllReady()
+
+        internetDetailsContentManager.internetContentData.observe(
+            internetDetailsContentManager.lifecycleOwner!!
+        ) {
+            assertThat(connectedWifi!!.visibility).isEqualTo(View.VISIBLE)
+            // Show a blank block to fix the details content height even if there is no WiFi list
+            assertThat(wifiList!!.visibility).isEqualTo(View.VISIBLE)
+            verify(internetAdapter).setMaxEntriesCount(2)
+            assertThat(seeAll!!.visibility).isEqualTo(View.INVISIBLE)
+        }
+    }
+
+    @Test
+    fun updateContent_wifiOnAndHasMaxWifiList_showWifiListAndSeeAll() {
+        // The preconditions WiFi ON and WiFi entries are already in setUp()
+        internetDetailsContentManager.connectedWifiEntry = null
+        internetDetailsContentManager.wifiEntriesCount =
+            InternetDetailsContentController.MAX_WIFI_ENTRY_COUNT
+        internetDetailsContentManager.hasMoreWifiEntries = true
+        internetDetailsContentManager.updateContent(false)
+        bgExecutor.runAllReady()
+
+        internetDetailsContentManager.internetContentData.observe(
+            internetDetailsContentManager.lifecycleOwner!!
+        ) {
+            assertThat(connectedWifi!!.visibility).isEqualTo(View.GONE)
+            assertThat(wifiList!!.visibility).isEqualTo(View.VISIBLE)
+            verify(internetAdapter).setMaxEntriesCount(3)
+            assertThat(seeAll!!.visibility).isEqualTo(View.VISIBLE)
+        }
+    }
+
+    @Test
+    fun updateContent_wifiOnAndHasBothWifiEntry_showBothWifiEntryAndSeeAll() {
+        // The preconditions WiFi ON and WiFi entries are already in setUp()
+        internetDetailsContentManager.wifiEntriesCount =
+            InternetDetailsContentController.MAX_WIFI_ENTRY_COUNT - 1
+        internetDetailsContentManager.hasMoreWifiEntries = true
+        internetDetailsContentManager.updateContent(false)
+        bgExecutor.runAllReady()
+
+        internetDetailsContentManager.internetContentData.observe(
+            internetDetailsContentManager.lifecycleOwner!!
+        ) {
+            assertThat(connectedWifi!!.visibility).isEqualTo(View.VISIBLE)
+            assertThat(wifiList!!.visibility).isEqualTo(View.VISIBLE)
+            verify(internetAdapter).setMaxEntriesCount(2)
+            assertThat(seeAll!!.visibility).isEqualTo(View.VISIBLE)
+        }
+    }
+
+    @Test
+    fun updateContent_deviceLockedAndNoConnectedWifi_showWifiToggle() {
+        // The preconditions WiFi entries are already in setUp()
+        whenever(internetDetailsContentController.isDeviceLocked).thenReturn(true)
+        internetDetailsContentManager.connectedWifiEntry = null
+        internetDetailsContentManager.updateContent(false)
+        bgExecutor.runAllReady()
+
+        internetDetailsContentManager.internetContentData.observe(
+            internetDetailsContentManager.lifecycleOwner!!
+        ) {
+            // Show WiFi Toggle without background
+            assertThat(wifiToggle!!.visibility).isEqualTo(View.VISIBLE)
+            assertThat(wifiToggle!!.background).isNull()
+            // Hide Wi-Fi networks and See all
+            assertThat(connectedWifi!!.visibility).isEqualTo(View.GONE)
+            assertThat(wifiList!!.visibility).isEqualTo(View.GONE)
+            assertThat(seeAll!!.visibility).isEqualTo(View.GONE)
+        }
+    }
+
+    @Test
+    fun updateContent_deviceLockedAndHasConnectedWifi_showWifiToggleWithBackground() {
+        // The preconditions WiFi ON and WiFi entries are already in setUp()
+        whenever(internetDetailsContentController.isDeviceLocked).thenReturn(true)
+        internetDetailsContentManager.updateContent(false)
+        bgExecutor.runAllReady()
+
+        internetDetailsContentManager.internetContentData.observe(
+            internetDetailsContentManager.lifecycleOwner!!
+        ) {
+            // Show WiFi Toggle with highlight background
+            assertThat(wifiToggle!!.visibility).isEqualTo(View.VISIBLE)
+            assertThat(wifiToggle!!.background).isNotNull()
+            // Hide Wi-Fi networks and See all
+            assertThat(connectedWifi!!.visibility).isEqualTo(View.GONE)
+            assertThat(wifiList!!.visibility).isEqualTo(View.GONE)
+            assertThat(seeAll!!.visibility).isEqualTo(View.GONE)
+        }
+    }
+
+    @Test
+    fun updateContent_disallowChangeWifiState_disableWifiSwitch() {
+        whenever(WifiEnterpriseRestrictionUtils.isChangeWifiStateAllowed(mContext))
+            .thenReturn(false)
+        createView()
+        internetDetailsContentManager.updateContent(false)
+        bgExecutor.runAllReady()
+
+        internetDetailsContentManager.internetContentData.observe(
+            internetDetailsContentManager.lifecycleOwner!!
+        ) {
+            // Disable Wi-Fi switch and show restriction message in summary.
+            assertThat(wifiToggleSwitch!!.isEnabled).isFalse()
+            assertThat(wifiToggleSummary!!.visibility).isEqualTo(View.VISIBLE)
+            assertThat(wifiToggleSummary!!.text.length).isNotEqualTo(0)
+        }
+    }
+
+    @Test
+    fun updateContent_allowChangeWifiState_enableWifiSwitch() {
+        whenever(WifiEnterpriseRestrictionUtils.isChangeWifiStateAllowed(mContext)).thenReturn(true)
+        createView()
+        internetDetailsContentManager.updateContent(false)
+        bgExecutor.runAllReady()
+
+        internetDetailsContentManager.internetContentData.observe(
+            internetDetailsContentManager.lifecycleOwner!!
+        ) {
+            // Enable Wi-Fi switch and hide restriction message in summary.
+            assertThat(wifiToggleSwitch!!.isEnabled).isTrue()
+            assertThat(wifiToggleSummary!!.visibility).isEqualTo(View.GONE)
+        }
+    }
+
+    @Test
+    fun updateContent_showSecondaryDataSub() {
+        whenever(internetDetailsContentController.activeAutoSwitchNonDdsSubId).thenReturn(1)
+        whenever(internetDetailsContentController.hasActiveSubIdOnDds()).thenReturn(true)
+        whenever(internetDetailsContentController.isAirplaneModeEnabled).thenReturn(false)
+
+        clearInvocations(internetDetailsContentController)
+        internetDetailsContentManager.updateContent(true)
+        bgExecutor.runAllReady()
+
+        internetDetailsContentManager.internetContentData.observe(
+            internetDetailsContentManager.lifecycleOwner!!
+        ) {
+            val primaryLayout =
+                contentView.requireViewById<LinearLayout>(R.id.mobile_network_layout)
+            val secondaryLayout =
+                contentView.requireViewById<LinearLayout>(R.id.secondary_mobile_network_layout)
+
+            verify(internetDetailsContentController).getMobileNetworkSummary(1)
+            assertThat(primaryLayout.background).isNotEqualTo(secondaryLayout.background)
+        }
+    }
+
+    @Test
+    fun updateContent_wifiOn_hideWifiScanNotify() {
+        // The preconditions WiFi ON and WiFi entries are already in setUp()
+        internetDetailsContentManager.updateContent(false)
+        bgExecutor.runAllReady()
+
+        internetDetailsContentManager.internetContentData.observe(
+            internetDetailsContentManager.lifecycleOwner!!
+        ) {
+            assertThat(wifiScanNotify!!.visibility).isEqualTo(View.GONE)
+        }
+
+        assertThat(wifiScanNotify!!.visibility).isEqualTo(View.GONE)
+    }
+
+    @Test
+    fun updateContent_wifiOffAndWifiScanOff_hideWifiScanNotify() {
+        whenever(internetDetailsContentController.isWifiEnabled).thenReturn(false)
+        whenever(internetDetailsContentController.isWifiScanEnabled).thenReturn(false)
+        internetDetailsContentManager.updateContent(false)
+        bgExecutor.runAllReady()
+
+        internetDetailsContentManager.internetContentData.observe(
+            internetDetailsContentManager.lifecycleOwner!!
+        ) {
+            assertThat(wifiScanNotify!!.visibility).isEqualTo(View.GONE)
+        }
+
+        assertThat(wifiScanNotify!!.visibility).isEqualTo(View.GONE)
+    }
+
+    @Test
+    fun updateContent_wifiOffAndWifiScanOnAndDeviceLocked_hideWifiScanNotify() {
+        whenever(internetDetailsContentController.isWifiEnabled).thenReturn(false)
+        whenever(internetDetailsContentController.isWifiScanEnabled).thenReturn(true)
+        whenever(internetDetailsContentController.isDeviceLocked).thenReturn(true)
+        internetDetailsContentManager.updateContent(false)
+        bgExecutor.runAllReady()
+
+        internetDetailsContentManager.internetContentData.observe(
+            internetDetailsContentManager.lifecycleOwner!!
+        ) {
+            assertThat(wifiScanNotify!!.visibility).isEqualTo(View.GONE)
+        }
+
+        assertThat(wifiScanNotify!!.visibility).isEqualTo(View.GONE)
+    }
+
+    @Test
+    fun updateContent_wifiOffAndWifiScanOnAndDeviceUnlocked_showWifiScanNotify() {
+        whenever(internetDetailsContentController.isWifiEnabled).thenReturn(false)
+        whenever(internetDetailsContentController.isWifiScanEnabled).thenReturn(true)
+        whenever(internetDetailsContentController.isDeviceLocked).thenReturn(false)
+        internetDetailsContentManager.updateContent(false)
+        bgExecutor.runAllReady()
+
+        internetDetailsContentManager.internetContentData.observe(
+            internetDetailsContentManager.lifecycleOwner!!
+        ) {
+            assertThat(wifiScanNotify!!.visibility).isEqualTo(View.VISIBLE)
+            val wifiScanNotifyText =
+                contentView.requireViewById<TextView>(R.id.wifi_scan_notify_text)
+            assertThat(wifiScanNotifyText.text.length).isNotEqualTo(0)
+            assertThat(wifiScanNotifyText.movementMethod).isNotNull()
+        }
+    }
+
+    @Test
+    fun updateContent_wifiIsDisabled_uncheckWifiSwitch() {
+        whenever(internetDetailsContentController.isWifiEnabled).thenReturn(false)
+        wifiToggleSwitch!!.isChecked = true
+        internetDetailsContentManager.updateContent(false)
+        bgExecutor.runAllReady()
+
+        internetDetailsContentManager.internetContentData.observe(
+            internetDetailsContentManager.lifecycleOwner!!
+        ) {
+            assertThat(wifiToggleSwitch!!.isChecked).isFalse()
+        }
+    }
+
+    @Test
+    @Throws(Exception::class)
+    fun updateContent_wifiIsEnabled_checkWifiSwitch() {
+        whenever(internetDetailsContentController.isWifiEnabled).thenReturn(true)
+        wifiToggleSwitch!!.isChecked = false
+        internetDetailsContentManager.updateContent(false)
+        bgExecutor.runAllReady()
+
+        internetDetailsContentManager.internetContentData.observe(
+            internetDetailsContentManager.lifecycleOwner!!
+        ) {
+            assertThat(wifiToggleSwitch!!.isChecked).isTrue()
+        }
+    }
+
+    @Test
+    fun onClickSeeMoreButton_clickSeeAll_verifyLaunchNetworkSetting() {
+        seeAll!!.performClick()
+
+        verify(internetDetailsContentController)
+            .launchNetworkSetting(contentView.requireViewById(R.id.see_all_layout))
+    }
+
+    @Test
+    fun onWifiScan_isScanTrue_setProgressBarVisibleTrue() {
+        internetDetailsContentManager.isProgressBarVisible = false
+
+        internetDetailsContentManager.internetDetailsCallback.onWifiScan(true)
+
+        assertThat(internetDetailsContentManager.isProgressBarVisible).isTrue()
+    }
+
+    @Test
+    fun onWifiScan_isScanFalse_setProgressBarVisibleFalse() {
+        internetDetailsContentManager.isProgressBarVisible = true
+
+        internetDetailsContentManager.internetDetailsCallback.onWifiScan(false)
+
+        assertThat(internetDetailsContentManager.isProgressBarVisible).isFalse()
+    }
+
+    @Test
+    fun updateContent_shareWifiIntentNull_hideButton() {
+        whenever(
+                internetDetailsContentController.getConfiguratorQrCodeGeneratorIntentOrNull(
+                    ArgumentMatchers.any()
+                )
+            )
+            .thenReturn(null)
+        internetDetailsContentManager.updateContent(false)
+        bgExecutor.runAllReady()
+
+        internetDetailsContentManager.internetContentData.observe(
+            internetDetailsContentManager.lifecycleOwner!!
+        ) {
+            assertThat(sharedWifiButton?.visibility).isEqualTo(View.GONE)
+        }
+    }
+
+    @Test
+    fun updateContent_shareWifiShareable_showButton() {
+        whenever(
+                internetDetailsContentController.getConfiguratorQrCodeGeneratorIntentOrNull(
+                    ArgumentMatchers.any()
+                )
+            )
+            .thenReturn(Intent())
+        internetDetailsContentManager.updateContent(false)
+        bgExecutor.runAllReady()
+
+        internetDetailsContentManager.internetContentData.observe(
+            internetDetailsContentManager.lifecycleOwner!!
+        ) {
+            assertThat(sharedWifiButton?.visibility).isEqualTo(View.VISIBLE)
+        }
+    }
+
+    companion object {
+        private const val TITLE = "Internet"
+        private const val MOBILE_NETWORK_TITLE = "Mobile Title"
+        private const val MOBILE_NETWORK_SUMMARY = "Mobile Summary"
+        private const val WIFI_TITLE = "Connected Wi-Fi Title"
+        private const val WIFI_SUMMARY = "Connected Wi-Fi Summary"
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateLegacyTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateLegacyTest.java
new file mode 100644
index 0000000..8c2bdab
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateLegacyTest.java
@@ -0,0 +1,875 @@
+package com.android.systemui.qs.tiles.dialog;
+
+import static com.android.systemui.qs.tiles.dialog.InternetDetailsContentController.MAX_WIFI_ENTRY_COUNT;
+
+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;
+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.content.Intent;
+import android.os.Handler;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.testing.TestableLooper;
+import android.view.View;
+import android.view.Window;
+import android.widget.LinearLayout;
+import android.widget.Switch;
+import android.widget.TextView;
+
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.test.annotation.UiThreadTest;
+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.UiEventLogger;
+import com.android.settingslib.wifi.WifiEnterpriseRestrictionUtils;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.animation.DialogTransitionAnimator;
+import com.android.systemui.res.R;
+import com.android.systemui.shade.domain.interactor.FakeShadeDialogContextInteractor;
+import com.android.systemui.statusbar.phone.SystemUIDialog;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
+import com.android.wifitrackerlib.WifiEntry;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.MockitoSession;
+
+import java.util.List;
+
+import kotlinx.coroutines.CoroutineScope;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@UiThreadTest
+public class InternetDialogDelegateLegacyTest extends SysuiTestCase {
+
+    private static final String MOBILE_NETWORK_TITLE = "Mobile Title";
+    private static final String MOBILE_NETWORK_SUMMARY = "Mobile Summary";
+    private static final String WIFI_TITLE = "Connected Wi-Fi Title";
+    private static final String WIFI_SUMMARY = "Connected Wi-Fi Summary";
+
+    @Mock
+    private Handler mHandler;
+    @Mock
+    CoroutineScope mScope;
+    @Mock
+    private TelephonyManager mTelephonyManager;
+    @Mock
+    private WifiEntry mInternetWifiEntry;
+    @Mock
+    private List<WifiEntry> mWifiEntries;
+    @Mock
+    private InternetAdapter mInternetAdapter;
+    @Mock
+    private InternetDetailsContentController mInternetDetailsContentController;
+    @Mock
+    private KeyguardStateController mKeyguard;
+    @Mock
+    private DialogTransitionAnimator mDialogTransitionAnimator;
+    @Mock
+    private SystemUIDialog.Factory mSystemUIDialogFactory;
+    @Mock
+    private SystemUIDialog mSystemUIDialog;
+    @Mock
+    private Window mWindow;
+
+    private FakeExecutor mBgExecutor = new FakeExecutor(new FakeSystemClock());
+    private InternetDialogDelegateLegacy mInternetDialogDelegateLegacy;
+    private View mDialogView;
+    private View mSubTitle;
+    private LinearLayout mEthernet;
+    private LinearLayout mMobileDataLayout;
+    private Switch mMobileToggleSwitch;
+    private LinearLayout mWifiToggle;
+    private Switch mWifiToggleSwitch;
+    private TextView mWifiToggleSummary;
+    private LinearLayout mConnectedWifi;
+    private RecyclerView mWifiList;
+    private LinearLayout mSeeAll;
+    private LinearLayout mWifiScanNotify;
+    private TextView mAirplaneModeSummaryText;
+
+    private MockitoSession mMockitoSession;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        doReturn(mTelephonyManager).when(mTelephonyManager).createForSubscriptionId(anyInt());
+        when(mInternetWifiEntry.getTitle()).thenReturn(WIFI_TITLE);
+        when(mInternetWifiEntry.getSummary(false)).thenReturn(WIFI_SUMMARY);
+        when(mInternetWifiEntry.isDefaultNetwork()).thenReturn(true);
+        when(mInternetWifiEntry.hasInternetAccess()).thenReturn(true);
+        when(mWifiEntries.size()).thenReturn(1);
+
+        when(mInternetDetailsContentController.getMobileNetworkTitle(anyInt()))
+                .thenReturn(MOBILE_NETWORK_TITLE);
+        when(mInternetDetailsContentController.getMobileNetworkSummary(anyInt()))
+                .thenReturn(MOBILE_NETWORK_SUMMARY);
+        when(mInternetDetailsContentController.isWifiEnabled()).thenReturn(true);
+        when(mInternetDetailsContentController.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), eq(mContext)))
+                .thenReturn(mSystemUIDialog);
+        when(mSystemUIDialog.getContext()).thenReturn(mContext);
+        when(mSystemUIDialog.getWindow()).thenReturn(mWindow);
+        createInternetDialog();
+    }
+
+    private void createInternetDialog() {
+        mInternetDialogDelegateLegacy = new InternetDialogDelegateLegacy(
+                mContext,
+                mock(InternetDialogManager.class),
+                mInternetDetailsContentController,
+                true,
+                true,
+                true,
+                mScope,
+                mock(UiEventLogger.class),
+                mDialogTransitionAnimator,
+                mHandler,
+                mBgExecutor,
+                mKeyguard,
+                mSystemUIDialogFactory,
+                new FakeShadeDialogContextInteractor(mContext));
+        mInternetDialogDelegateLegacy.createDialog();
+        mInternetDialogDelegateLegacy.onCreate(mSystemUIDialog, null);
+        mInternetDialogDelegateLegacy.mAdapter = mInternetAdapter;
+        mInternetDialogDelegateLegacy.mConnectedWifiEntry = mInternetWifiEntry;
+        mInternetDialogDelegateLegacy.mWifiEntriesCount = mWifiEntries.size();
+
+        mDialogView = mInternetDialogDelegateLegacy.mDialogView;
+        mSubTitle = mDialogView.requireViewById(R.id.internet_dialog_subtitle);
+        mEthernet = mDialogView.requireViewById(R.id.ethernet_layout);
+        mMobileDataLayout = mDialogView.requireViewById(R.id.mobile_network_layout);
+        mMobileToggleSwitch = mDialogView.requireViewById(R.id.mobile_toggle);
+        mWifiToggle = mDialogView.requireViewById(R.id.turn_on_wifi_layout);
+        mWifiToggleSwitch = mDialogView.requireViewById(R.id.wifi_toggle);
+        mWifiToggleSummary = mDialogView.requireViewById(R.id.wifi_toggle_summary);
+        mConnectedWifi = mDialogView.requireViewById(R.id.wifi_connected_layout);
+        mWifiList = mDialogView.requireViewById(R.id.wifi_list_layout);
+        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);
+        mInternetDialogDelegateLegacy.onStart(mSystemUIDialog);
+    }
+
+    @After
+    public void tearDown() {
+        mInternetDialogDelegateLegacy.onStop(mSystemUIDialog);
+        mInternetDialogDelegateLegacy.dismissDialog();
+        mMockitoSession.finishMocking();
+    }
+
+    @Test
+    public void createInternetDialog_setAccessibilityPaneTitleToQuickSettings() {
+        assertThat(mDialogView.getAccessibilityPaneTitle())
+                .isEqualTo(mContext.getText(R.string.accessibility_desc_quick_settings));
+    }
+
+    @Test
+    public void hideWifiViews_WifiViewsGone() {
+        mInternetDialogDelegateLegacy.hideWifiViews();
+
+        assertThat(mInternetDialogDelegateLegacy.mIsProgressBarVisible).isFalse();
+        assertThat(mWifiToggle.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mWifiList.getVisibility()).isEqualTo(View.GONE);
+        assertThat(mSeeAll.getVisibility()).isEqualTo(View.GONE);
+    }
+
+    @Test
+    public void updateDialog_withApmOn_internetDialogSubTitleGone() {
+        when(mInternetDetailsContentController.isAirplaneModeEnabled()).thenReturn(true);
+        mInternetDialogDelegateLegacy.updateDialog(true);
+        mBgExecutor.runAllReady();
+
+        mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+                mInternetDialogDelegateLegacy.mLifecycleOwner, i -> {
+                    assertThat(mSubTitle.getVisibility()).isEqualTo(View.VISIBLE);
+                });
+    }
+
+    @Test
+    public void updateDialog_withApmOff_internetDialogSubTitleVisible() {
+        when(mInternetDetailsContentController.isAirplaneModeEnabled()).thenReturn(false);
+        mInternetDialogDelegateLegacy.updateDialog(true);
+        mBgExecutor.runAllReady();
+
+        mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+                mInternetDialogDelegateLegacy.mLifecycleOwner, i -> {
+                    assertThat(mSubTitle.getVisibility()).isEqualTo(View.VISIBLE);
+                });
+    }
+
+    @Test
+    public void updateDialog_apmOffAndHasEthernet_showEthernet() {
+        when(mInternetDetailsContentController.isAirplaneModeEnabled()).thenReturn(false);
+        when(mInternetDetailsContentController.hasEthernet()).thenReturn(true);
+        mInternetDialogDelegateLegacy.updateDialog(true);
+        mBgExecutor.runAllReady();
+
+        mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+                mInternetDialogDelegateLegacy.mLifecycleOwner, i -> {
+                    assertThat(mEthernet.getVisibility()).isEqualTo(View.VISIBLE);
+                });
+    }
+
+    @Test
+    public void updateDialog_apmOffAndNoEthernet_hideEthernet() {
+        when(mInternetDetailsContentController.isAirplaneModeEnabled()).thenReturn(false);
+        when(mInternetDetailsContentController.hasEthernet()).thenReturn(false);
+        mInternetDialogDelegateLegacy.updateDialog(true);
+        mBgExecutor.runAllReady();
+
+        mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+                mInternetDialogDelegateLegacy.mLifecycleOwner, i -> {
+                    assertThat(mEthernet.getVisibility()).isEqualTo(View.GONE);
+                });
+    }
+
+    @Test
+    public void updateDialog_apmOnAndHasEthernet_showEthernet() {
+        when(mInternetDetailsContentController.isAirplaneModeEnabled()).thenReturn(true);
+        when(mInternetDetailsContentController.hasEthernet()).thenReturn(true);
+        mInternetDialogDelegateLegacy.updateDialog(true);
+        mBgExecutor.runAllReady();
+
+        mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+                mInternetDialogDelegateLegacy.mLifecycleOwner, i -> {
+                    assertThat(mEthernet.getVisibility()).isEqualTo(View.VISIBLE);
+                });
+    }
+
+    @Test
+    public void updateDialog_apmOnAndNoEthernet_hideEthernet() {
+        when(mInternetDetailsContentController.isAirplaneModeEnabled()).thenReturn(true);
+        when(mInternetDetailsContentController.hasEthernet()).thenReturn(false);
+        mInternetDialogDelegateLegacy.updateDialog(true);
+        mBgExecutor.runAllReady();
+
+        mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+                mInternetDialogDelegateLegacy.mLifecycleOwner, i -> {
+                    assertThat(mEthernet.getVisibility()).isEqualTo(View.GONE);
+                });
+    }
+
+    @Test
+    public void updateDialog_apmOffAndNotCarrierNetwork_mobileDataLayoutGone() {
+        // Mobile network should be gone if the list of active subscriptionId is null.
+        when(mInternetDetailsContentController.isCarrierNetworkActive()).thenReturn(false);
+        when(mInternetDetailsContentController.isAirplaneModeEnabled()).thenReturn(false);
+        when(mInternetDetailsContentController.hasActiveSubIdOnDds()).thenReturn(false);
+        mInternetDialogDelegateLegacy.updateDialog(true);
+        mBgExecutor.runAllReady();
+
+        mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+                mInternetDialogDelegateLegacy.mLifecycleOwner, i -> {
+                    assertThat(mMobileDataLayout.getVisibility()).isEqualTo(View.GONE);
+                });
+    }
+
+    @Test
+    public void updateDialog_apmOnWithCarrierNetworkAndWifiStatus_mobileDataLayoutVisible() {
+        // Carrier network should be visible if airplane mode ON and Wi-Fi is ON.
+        when(mInternetDetailsContentController.isCarrierNetworkActive()).thenReturn(true);
+        when(mInternetDetailsContentController.isAirplaneModeEnabled()).thenReturn(true);
+        when(mInternetDetailsContentController.isWifiEnabled()).thenReturn(true);
+        mInternetDialogDelegateLegacy.updateDialog(true);
+        mBgExecutor.runAllReady();
+
+        mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+                mInternetDialogDelegateLegacy.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(mInternetDetailsContentController.isCarrierNetworkActive()).thenReturn(true);
+        when(mInternetDetailsContentController.isAirplaneModeEnabled()).thenReturn(true);
+        when(mInternetDetailsContentController.isWifiEnabled()).thenReturn(false);
+        mInternetDialogDelegateLegacy.updateDialog(true);
+        mBgExecutor.runAllReady();
+
+        mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+                mInternetDialogDelegateLegacy.mLifecycleOwner, i -> {
+                    assertThat(mMobileDataLayout.getVisibility()).isEqualTo(View.GONE);
+                });
+    }
+
+    @Test
+    public void updateDialog_apmOnAndNoCarrierNetwork_mobileDataLayoutGone() {
+        when(mInternetDetailsContentController.isCarrierNetworkActive()).thenReturn(false);
+        when(mInternetDetailsContentController.isAirplaneModeEnabled()).thenReturn(true);
+        mInternetDialogDelegateLegacy.updateDialog(true);
+        mBgExecutor.runAllReady();
+
+        mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+                mInternetDialogDelegateLegacy.mLifecycleOwner, i -> {
+                    assertThat(mMobileDataLayout.getVisibility()).isEqualTo(View.GONE);
+                });
+    }
+
+    @Test
+    public void updateDialog_apmOnAndWifiOnHasCarrierNetwork_showAirplaneSummary() {
+        when(mInternetDetailsContentController.isCarrierNetworkActive()).thenReturn(true);
+        when(mInternetDetailsContentController.isAirplaneModeEnabled()).thenReturn(true);
+        mInternetDialogDelegateLegacy.mConnectedWifiEntry = null;
+        doReturn(false).when(mInternetDetailsContentController).activeNetworkIsCellular();
+        mInternetDialogDelegateLegacy.updateDialog(true);
+        mBgExecutor.runAllReady();
+
+        mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+                mInternetDialogDelegateLegacy.mLifecycleOwner, i -> {
+                    assertThat(mMobileDataLayout.getVisibility()).isEqualTo(View.VISIBLE);
+                    assertThat(mAirplaneModeSummaryText.getVisibility()).isEqualTo(View.VISIBLE);
+                });
+    }
+
+    @Test
+    public void updateDialog_apmOffAndWifiOnHasCarrierNetwork_notShowApmSummary() {
+        when(mInternetDetailsContentController.isCarrierNetworkActive()).thenReturn(true);
+        when(mInternetDetailsContentController.isAirplaneModeEnabled()).thenReturn(false);
+        mInternetDialogDelegateLegacy.mConnectedWifiEntry = null;
+        doReturn(false).when(mInternetDetailsContentController).activeNetworkIsCellular();
+        mInternetDialogDelegateLegacy.updateDialog(true);
+        mBgExecutor.runAllReady();
+
+        mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+                mInternetDialogDelegateLegacy.mLifecycleOwner, i -> {
+                    assertThat(mAirplaneModeSummaryText.getVisibility()).isEqualTo(View.GONE);
+                });
+    }
+
+    @Test
+    public void updateDialog_apmOffAndHasCarrierNetwork_notShowApmSummary() {
+        when(mInternetDetailsContentController.isCarrierNetworkActive()).thenReturn(true);
+        when(mInternetDetailsContentController.isAirplaneModeEnabled()).thenReturn(false);
+        mInternetDialogDelegateLegacy.updateDialog(true);
+        mBgExecutor.runAllReady();
+
+        mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+                mInternetDialogDelegateLegacy.mLifecycleOwner, i -> {
+                    assertThat(mAirplaneModeSummaryText.getVisibility()).isEqualTo(View.GONE);
+                });
+    }
+
+    @Test
+    public void updateDialog_apmOnAndNoCarrierNetwork_notShowApmSummary() {
+        when(mInternetDetailsContentController.isCarrierNetworkActive()).thenReturn(false);
+        when(mInternetDetailsContentController.isAirplaneModeEnabled()).thenReturn(true);
+        mInternetDialogDelegateLegacy.updateDialog(true);
+        mBgExecutor.runAllReady();
+
+        mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+                mInternetDialogDelegateLegacy.mLifecycleOwner, i -> {
+                    assertThat(mAirplaneModeSummaryText.getVisibility()).isEqualTo(View.GONE);
+                });
+    }
+
+    @Test
+    public void updateDialog_mobileDataIsEnabled_checkMobileDataSwitch() {
+        doReturn(true).when(mInternetDetailsContentController).hasActiveSubIdOnDds();
+        when(mInternetDetailsContentController.isCarrierNetworkActive()).thenReturn(true);
+        when(mInternetDetailsContentController.isMobileDataEnabled()).thenReturn(true);
+        mMobileToggleSwitch.setChecked(false);
+        mInternetDialogDelegateLegacy.updateDialog(true);
+        mBgExecutor.runAllReady();
+
+        mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+                mInternetDialogDelegateLegacy.mLifecycleOwner, i -> {
+                    assertThat(mMobileToggleSwitch.isChecked()).isTrue();
+                });
+    }
+
+    @Test
+    public void updateDialog_mobileDataIsNotChanged_checkMobileDataSwitch() {
+        doReturn(true).when(mInternetDetailsContentController).hasActiveSubIdOnDds();
+        when(mInternetDetailsContentController.isCarrierNetworkActive()).thenReturn(true);
+        when(mInternetDetailsContentController.isMobileDataEnabled()).thenReturn(false);
+        mMobileToggleSwitch.setChecked(false);
+        mInternetDialogDelegateLegacy.updateDialog(true);
+        mBgExecutor.runAllReady();
+
+        mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+                mInternetDialogDelegateLegacy.mLifecycleOwner, i -> {
+                    assertThat(mMobileToggleSwitch.isChecked()).isFalse();
+                });
+    }
+
+    @Test
+    public void updateDialog_wifiOnAndHasInternetWifi_showConnectedWifi() {
+        when(mInternetDetailsContentController.getActiveAutoSwitchNonDdsSubId()).thenReturn(1);
+        doReturn(true).when(mInternetDetailsContentController).hasActiveSubIdOnDds();
+        // The preconditions WiFi ON and Internet WiFi are already in setUp()
+        doReturn(false).when(mInternetDetailsContentController).activeNetworkIsCellular();
+
+        mInternetDialogDelegateLegacy.updateDialog(true);
+        mBgExecutor.runAllReady();
+
+        mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+                mInternetDialogDelegateLegacy.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
+    public void updateDialog_wifiOnAndNoConnectedWifi_hideConnectedWifi() {
+        // The precondition WiFi ON is already in setUp()
+        mInternetDialogDelegateLegacy.mConnectedWifiEntry = null;
+        doReturn(false).when(mInternetDetailsContentController).activeNetworkIsCellular();
+        mInternetDialogDelegateLegacy.updateDialog(false);
+        mBgExecutor.runAllReady();
+
+        mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+                mInternetDialogDelegateLegacy.mLifecycleOwner, i -> {
+                    assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
+                });
+    }
+
+    @Test
+    public void updateDialog_wifiOnAndNoWifiEntry_showWifiListAndSeeAllArea() {
+        // The precondition WiFi ON is already in setUp()
+        mInternetDialogDelegateLegacy.mConnectedWifiEntry = null;
+        mInternetDialogDelegateLegacy.mWifiEntriesCount = 0;
+        mInternetDialogDelegateLegacy.updateDialog(false);
+        mBgExecutor.runAllReady();
+
+        mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+                mInternetDialogDelegateLegacy.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_wifiOnAndOneWifiEntry_showWifiListAndSeeAllArea() {
+        // The precondition WiFi ON is already in setUp()
+        mInternetDialogDelegateLegacy.mConnectedWifiEntry = null;
+        mInternetDialogDelegateLegacy.mWifiEntriesCount = 1;
+        mInternetDialogDelegateLegacy.updateDialog(false);
+        mBgExecutor.runAllReady();
+
+        mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+                mInternetDialogDelegateLegacy.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()
+        mInternetDialogDelegateLegacy.mWifiEntriesCount = 0;
+        mInternetDialogDelegateLegacy.updateDialog(false);
+        mBgExecutor.runAllReady();
+
+        mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+                mInternetDialogDelegateLegacy.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
+    public void updateDialog_wifiOnAndHasMaxWifiList_showWifiListAndSeeAll() {
+        // The preconditions WiFi ON and WiFi entries are already in setUp()
+        mInternetDialogDelegateLegacy.mConnectedWifiEntry = null;
+        mInternetDialogDelegateLegacy.mWifiEntriesCount = MAX_WIFI_ENTRY_COUNT;
+        mInternetDialogDelegateLegacy.mHasMoreWifiEntries = true;
+        mInternetDialogDelegateLegacy.updateDialog(false);
+        mBgExecutor.runAllReady();
+
+        mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+                mInternetDialogDelegateLegacy.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
+    public void updateDialog_wifiOnAndHasBothWifiEntry_showBothWifiEntryAndSeeAll() {
+        // The preconditions WiFi ON and WiFi entries are already in setUp()
+        mInternetDialogDelegateLegacy.mWifiEntriesCount = MAX_WIFI_ENTRY_COUNT - 1;
+        mInternetDialogDelegateLegacy.mHasMoreWifiEntries = true;
+        mInternetDialogDelegateLegacy.updateDialog(false);
+        mBgExecutor.runAllReady();
+
+        mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+                mInternetDialogDelegateLegacy.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
+    public void updateDialog_deviceLockedAndNoConnectedWifi_showWifiToggle() {
+        // The preconditions WiFi entries are already in setUp()
+        when(mInternetDetailsContentController.isDeviceLocked()).thenReturn(true);
+        mInternetDialogDelegateLegacy.mConnectedWifiEntry = null;
+        mInternetDialogDelegateLegacy.updateDialog(false);
+        mBgExecutor.runAllReady();
+
+        mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+                mInternetDialogDelegateLegacy.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(mInternetDetailsContentController.isDeviceLocked()).thenReturn(true);
+        mInternetDialogDelegateLegacy.updateDialog(false);
+        mBgExecutor.runAllReady();
+
+        mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+                mInternetDialogDelegateLegacy.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
+    public void updateDialog_disallowChangeWifiState_disableWifiSwitch() {
+        mInternetDialogDelegateLegacy.dismissDialog();
+        when(WifiEnterpriseRestrictionUtils.isChangeWifiStateAllowed(mContext)).thenReturn(false);
+        createInternetDialog();
+        mInternetDialogDelegateLegacy.updateDialog(false);
+        mBgExecutor.runAllReady();
+
+        mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+                mInternetDialogDelegateLegacy.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
+    public void updateDialog_allowChangeWifiState_enableWifiSwitch() {
+        mInternetDialogDelegateLegacy.dismissDialog();
+        when(WifiEnterpriseRestrictionUtils.isChangeWifiStateAllowed(mContext)).thenReturn(true);
+        createInternetDialog();
+        mInternetDialogDelegateLegacy.updateDialog(false);
+        mBgExecutor.runAllReady();
+
+        mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+                mInternetDialogDelegateLegacy.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() {
+        when(mInternetDetailsContentController.getActiveAutoSwitchNonDdsSubId()).thenReturn(1);
+        doReturn(1).when(mInternetDetailsContentController).getActiveAutoSwitchNonDdsSubId();
+        doReturn(true).when(mInternetDetailsContentController).hasActiveSubIdOnDds();
+        doReturn(false).when(mInternetDetailsContentController).isAirplaneModeEnabled();
+        clearInvocations(mInternetDetailsContentController);
+        mInternetDialogDelegateLegacy.updateDialog(true);
+        mBgExecutor.runAllReady();
+
+        mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+                mInternetDialogDelegateLegacy.mLifecycleOwner, i -> {
+                    LinearLayout primaryLayout = mDialogView.requireViewById(
+                            R.id.mobile_network_layout);
+                    LinearLayout secondaryLayout = mDialogView.requireViewById(
+                            R.id.secondary_mobile_network_layout);
+
+                    verify(mInternetDetailsContentController).getMobileNetworkSummary(1);
+                    assertThat(primaryLayout.getBackground()).isNotEqualTo(
+                            secondaryLayout.getBackground());
+                });
+    }
+
+    @Test
+    public void updateDialog_wifiOn_hideWifiScanNotify() {
+        // The preconditions WiFi ON and WiFi entries are already in setUp()
+
+        mInternetDialogDelegateLegacy.updateDialog(false);
+        mBgExecutor.runAllReady();
+
+        mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+                mInternetDialogDelegateLegacy.mLifecycleOwner, i -> {
+                    assertThat(mWifiScanNotify.getVisibility()).isEqualTo(View.GONE);
+                });
+
+        assertThat(mWifiScanNotify.getVisibility()).isEqualTo(View.GONE);
+    }
+
+    @Test
+    public void updateDialog_wifiOffAndWifiScanOff_hideWifiScanNotify() {
+        when(mInternetDetailsContentController.isWifiEnabled()).thenReturn(false);
+        when(mInternetDetailsContentController.isWifiScanEnabled()).thenReturn(false);
+        mInternetDialogDelegateLegacy.updateDialog(false);
+        mBgExecutor.runAllReady();
+
+        mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+                mInternetDialogDelegateLegacy.mLifecycleOwner, i -> {
+                    assertThat(mWifiScanNotify.getVisibility()).isEqualTo(View.GONE);
+                });
+
+        assertThat(mWifiScanNotify.getVisibility()).isEqualTo(View.GONE);
+    }
+
+    @Test
+    public void updateDialog_wifiOffAndWifiScanOnAndDeviceLocked_hideWifiScanNotify() {
+        when(mInternetDetailsContentController.isWifiEnabled()).thenReturn(false);
+        when(mInternetDetailsContentController.isWifiScanEnabled()).thenReturn(true);
+        when(mInternetDetailsContentController.isDeviceLocked()).thenReturn(true);
+        mInternetDialogDelegateLegacy.updateDialog(false);
+        mBgExecutor.runAllReady();
+
+        mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+                mInternetDialogDelegateLegacy.mLifecycleOwner, i -> {
+                    assertThat(mWifiScanNotify.getVisibility()).isEqualTo(View.GONE);
+                });
+
+        assertThat(mWifiScanNotify.getVisibility()).isEqualTo(View.GONE);
+    }
+
+    @Test
+    public void updateDialog_wifiOffAndWifiScanOnAndDeviceUnlocked_showWifiScanNotify() {
+        when(mInternetDetailsContentController.isWifiEnabled()).thenReturn(false);
+        when(mInternetDetailsContentController.isWifiScanEnabled()).thenReturn(true);
+        when(mInternetDetailsContentController.isDeviceLocked()).thenReturn(false);
+        mInternetDialogDelegateLegacy.updateDialog(false);
+        mBgExecutor.runAllReady();
+
+        mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+                mInternetDialogDelegateLegacy.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(mInternetDetailsContentController.isWifiEnabled()).thenReturn(false);
+        mWifiToggleSwitch.setChecked(true);
+        mInternetDialogDelegateLegacy.updateDialog(false);
+        mBgExecutor.runAllReady();
+
+        mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+                mInternetDialogDelegateLegacy.mLifecycleOwner, i -> {
+                    assertThat(mWifiToggleSwitch.isChecked()).isFalse();
+                });
+    }
+
+    @Test
+    public void updateDialog_wifiIsEnabled_checkWifiSwitch() throws Exception {
+        when(mInternetDetailsContentController.isWifiEnabled()).thenReturn(true);
+        mWifiToggleSwitch.setChecked(false);
+        mInternetDialogDelegateLegacy.updateDialog(false);
+        mBgExecutor.runAllReady();
+
+        mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+                mInternetDialogDelegateLegacy.mLifecycleOwner, i -> {
+                    assertThat(mWifiToggleSwitch.isChecked()).isTrue();
+                });
+    }
+
+    @Test
+    public void onClickSeeMoreButton_clickSeeAll_verifyLaunchNetworkSetting() {
+        mSeeAll.performClick();
+
+        verify(mInternetDetailsContentController).launchNetworkSetting(
+                mDialogView.requireViewById(R.id.see_all_layout));
+    }
+
+    @Test
+    public void onWifiScan_isScanTrue_setProgressBarVisibleTrue() {
+        mInternetDialogDelegateLegacy.mIsProgressBarVisible = false;
+
+        mInternetDialogDelegateLegacy.onWifiScan(true);
+
+        assertThat(mInternetDialogDelegateLegacy.mIsProgressBarVisible).isTrue();
+    }
+
+    @Test
+    public void onWifiScan_isScanFalse_setProgressBarVisibleFalse() {
+        mInternetDialogDelegateLegacy.mIsProgressBarVisible = true;
+
+        mInternetDialogDelegateLegacy.onWifiScan(false);
+
+        assertThat(mInternetDialogDelegateLegacy.mIsProgressBarVisible).isFalse();
+    }
+
+    @Test
+    public void getWifiListMaxCount_returnCountCorrectly() {
+        // Both of the Ethernet, MobileData is hidden.
+        // Then the maximum count is equal to MAX_WIFI_ENTRY_COUNT.
+        setNetworkVisible(false, false, false);
+
+        assertThat(mInternetDialogDelegateLegacy.getWifiListMaxCount()).isEqualTo(
+                MAX_WIFI_ENTRY_COUNT);
+
+        // If the Connected Wi-Fi is displayed then reduce one of the Wi-Fi list max count.
+        setNetworkVisible(false, false, true);
+
+        assertThat(mInternetDialogDelegateLegacy.getWifiListMaxCount())
+                .isEqualTo(MAX_WIFI_ENTRY_COUNT - 1);
+
+        // Only one of Ethernet, MobileData is displayed.
+        // Then the maximum count is equal to MAX_WIFI_ENTRY_COUNT.
+        setNetworkVisible(true, false, false);
+
+        assertThat(mInternetDialogDelegateLegacy.getWifiListMaxCount()).isEqualTo(
+                MAX_WIFI_ENTRY_COUNT);
+
+        setNetworkVisible(false, true, false);
+
+        assertThat(mInternetDialogDelegateLegacy.getWifiListMaxCount()).isEqualTo(
+                MAX_WIFI_ENTRY_COUNT);
+
+        // If the Connected Wi-Fi is displayed then reduce one of the Wi-Fi list max count.
+        setNetworkVisible(true, false, true);
+
+        assertThat(mInternetDialogDelegateLegacy.getWifiListMaxCount())
+                .isEqualTo(MAX_WIFI_ENTRY_COUNT - 1);
+
+        setNetworkVisible(false, true, true);
+
+        assertThat(mInternetDialogDelegateLegacy.getWifiListMaxCount())
+                .isEqualTo(MAX_WIFI_ENTRY_COUNT - 1);
+
+        // Both of Ethernet, MobileData, ConnectedWiFi is displayed.
+        // Then the maximum count is equal to MAX_WIFI_ENTRY_COUNT - 1.
+        setNetworkVisible(true, true, false);
+
+        assertThat(mInternetDialogDelegateLegacy.getWifiListMaxCount())
+                .isEqualTo(MAX_WIFI_ENTRY_COUNT - 1);
+
+        // If the Connected Wi-Fi is displayed then reduce one of the Wi-Fi list max count.
+        setNetworkVisible(true, true, true);
+
+        assertThat(mInternetDialogDelegateLegacy.getWifiListMaxCount())
+                .isEqualTo(MAX_WIFI_ENTRY_COUNT - 2);
+    }
+
+    @Test
+    public void updateDialog_shareWifiIntentNull_hideButton() {
+        when(mInternetDetailsContentController.getConfiguratorQrCodeGeneratorIntentOrNull(any()))
+                .thenReturn(null);
+        mInternetDialogDelegateLegacy.updateDialog(false);
+        mBgExecutor.runAllReady();
+
+        mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+                mInternetDialogDelegateLegacy.mLifecycleOwner, i -> {
+                    assertThat(
+                            mInternetDialogDelegateLegacy.mShareWifiButton.getVisibility())
+                            .isEqualTo(View.GONE);
+                });
+    }
+
+    @Test
+    public void updateDialog_shareWifiShareable_showButton() {
+        when(mInternetDetailsContentController.getConfiguratorQrCodeGeneratorIntentOrNull(any()))
+                .thenReturn(new Intent());
+        mInternetDialogDelegateLegacy.updateDialog(false);
+        mBgExecutor.runAllReady();
+
+        mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+                mInternetDialogDelegateLegacy.mLifecycleOwner, i -> {
+                    assertThat(mInternetDialogDelegateLegacy.mShareWifiButton.getVisibility())
+                            .isEqualTo(View.VISIBLE);
+                });
+    }
+
+    @Test
+    public void updateDialog_shouldUpdateMobileNetworkTrue_updateMobileDataLayout() {
+        when(mInternetDetailsContentController.isCarrierNetworkActive()).thenReturn(false);
+        when(mInternetDetailsContentController.isAirplaneModeEnabled()).thenReturn(false);
+        when(mInternetDetailsContentController.hasActiveSubIdOnDds()).thenReturn(true);
+        when(mInternetDetailsContentController.activeNetworkIsCellular()).thenReturn(false);
+        mMobileDataLayout.setVisibility(View.GONE);
+
+        mInternetDialogDelegateLegacy.updateDialog(true);
+        mBgExecutor.runAllReady();
+
+        mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+                mInternetDialogDelegateLegacy.mLifecycleOwner, i -> {
+                    assertThat(mMobileDataLayout.getVisibility()).isEqualTo(View.VISIBLE);
+                });
+    }
+
+    @Test
+    public void updateDialog_shouldUpdateMobileNetworkFalse_doNotUpdateMobileDataLayout() {
+        when(mInternetDetailsContentController.isCarrierNetworkActive()).thenReturn(false);
+        when(mInternetDetailsContentController.isAirplaneModeEnabled()).thenReturn(false);
+        when(mInternetDetailsContentController.hasActiveSubIdOnDds()).thenReturn(true);
+        when(mInternetDetailsContentController.activeNetworkIsCellular()).thenReturn(false);
+        mMobileDataLayout.setVisibility(View.GONE);
+
+        mInternetDialogDelegateLegacy.updateDialog(false);
+        mBgExecutor.runAllReady();
+
+        mInternetDialogDelegateLegacy.mDataInternetContent.observe(
+                mInternetDialogDelegateLegacy.mLifecycleOwner, i -> {
+                    assertThat(mMobileDataLayout.getVisibility()).isEqualTo(View.GONE);
+                });
+    }
+
+    private void setNetworkVisible(boolean ethernetVisible, boolean mobileDataVisible,
+            boolean connectedWifiVisible) {
+        mEthernet.setVisibility(ethernetVisible ? View.VISIBLE : View.GONE);
+        mMobileDataLayout.setVisibility(mobileDataVisible ? View.VISIBLE : View.GONE);
+        mConnectedWifi.setVisibility(connectedWifiVisible ? View.VISIBLE : View.GONE);
+    }
+}
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
deleted file mode 100644
index 8560b67..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateTest.java
+++ /dev/null
@@ -1,837 +0,0 @@
-package com.android.systemui.qs.tiles.dialog;
-
-import static com.android.systemui.qs.tiles.dialog.InternetDialogController.MAX_WIFI_ENTRY_COUNT;
-
-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;
-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.content.Intent;
-import android.os.Handler;
-import android.telephony.SubscriptionManager;
-import android.telephony.TelephonyManager;
-import android.testing.TestableLooper;
-import android.view.View;
-import android.view.Window;
-import android.widget.LinearLayout;
-import android.widget.Switch;
-import android.widget.TextView;
-
-import androidx.recyclerview.widget.RecyclerView;
-import androidx.test.annotation.UiThreadTest;
-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.UiEventLogger;
-import com.android.settingslib.wifi.WifiEnterpriseRestrictionUtils;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.animation.DialogTransitionAnimator;
-import com.android.systemui.res.R;
-import com.android.systemui.shade.domain.interactor.FakeShadeDialogContextInteractor;
-import com.android.systemui.statusbar.phone.SystemUIDialog;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.util.concurrency.FakeExecutor;
-import com.android.systemui.util.time.FakeSystemClock;
-import com.android.wifitrackerlib.WifiEntry;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.mockito.MockitoSession;
-
-import java.util.List;
-
-import kotlinx.coroutines.CoroutineScope;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-@TestableLooper.RunWithLooper(setAsMainLooper = true)
-@UiThreadTest
-public class InternetDialogDelegateTest extends SysuiTestCase {
-
-    private static final String MOBILE_NETWORK_TITLE = "Mobile Title";
-    private static final String MOBILE_NETWORK_SUMMARY = "Mobile Summary";
-    private static final String WIFI_TITLE = "Connected Wi-Fi Title";
-    private static final String WIFI_SUMMARY = "Connected Wi-Fi Summary";
-
-    @Mock
-    private Handler mHandler;
-    @Mock
-    CoroutineScope mScope;
-    @Mock
-    private TelephonyManager mTelephonyManager;
-    @Mock
-    private WifiEntry mInternetWifiEntry;
-    @Mock
-    private List<WifiEntry> mWifiEntries;
-    @Mock
-    private InternetAdapter mInternetAdapter;
-    @Mock
-    private InternetDialogController mInternetDialogController;
-    @Mock
-    private KeyguardStateController mKeyguard;
-    @Mock
-    private DialogTransitionAnimator mDialogTransitionAnimator;
-    @Mock
-    private SystemUIDialog.Factory mSystemUIDialogFactory;
-    @Mock
-    private SystemUIDialog mSystemUIDialog;
-    @Mock
-    private Window mWindow;
-
-    private FakeExecutor mBgExecutor = new FakeExecutor(new FakeSystemClock());
-    private InternetDialogDelegate mInternetDialogDelegate;
-    private View mDialogView;
-    private View mSubTitle;
-    private LinearLayout mEthernet;
-    private LinearLayout mMobileDataLayout;
-    private Switch mMobileToggleSwitch;
-    private LinearLayout mWifiToggle;
-    private Switch mWifiToggleSwitch;
-    private TextView mWifiToggleSummary;
-    private LinearLayout mConnectedWifi;
-    private RecyclerView mWifiList;
-    private LinearLayout mSeeAll;
-    private LinearLayout mWifiScanNotify;
-    private TextView mAirplaneModeSummaryText;
-
-    private MockitoSession mMockitoSession;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        doReturn(mTelephonyManager).when(mTelephonyManager).createForSubscriptionId(anyInt());
-        when(mInternetWifiEntry.getTitle()).thenReturn(WIFI_TITLE);
-        when(mInternetWifiEntry.getSummary(false)).thenReturn(WIFI_SUMMARY);
-        when(mInternetWifiEntry.isDefaultNetwork()).thenReturn(true);
-        when(mInternetWifiEntry.hasInternetAccess()).thenReturn(true);
-        when(mWifiEntries.size()).thenReturn(1);
-
-        when(mInternetDialogController.getMobileNetworkTitle(anyInt()))
-                .thenReturn(MOBILE_NETWORK_TITLE);
-        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), eq(mContext)))
-                .thenReturn(mSystemUIDialog);
-        when(mSystemUIDialog.getContext()).thenReturn(mContext);
-        when(mSystemUIDialog.getWindow()).thenReturn(mWindow);
-        createInternetDialog();
-    }
-
-    private void createInternetDialog() {
-        mInternetDialogDelegate = new InternetDialogDelegate(
-                mContext,
-                mock(InternetDialogManager.class),
-                mInternetDialogController,
-                true,
-                true,
-                true,
-                mScope,
-                mock(UiEventLogger.class),
-                mDialogTransitionAnimator,
-                mHandler,
-                mBgExecutor,
-                mKeyguard,
-                mSystemUIDialogFactory,
-                new FakeShadeDialogContextInteractor(mContext));
-        mInternetDialogDelegate.createDialog();
-        mInternetDialogDelegate.onCreate(mSystemUIDialog, null);
-        mInternetDialogDelegate.mAdapter = mInternetAdapter;
-        mInternetDialogDelegate.mConnectedWifiEntry = mInternetWifiEntry;
-        mInternetDialogDelegate.mWifiEntriesCount = mWifiEntries.size();
-
-        mDialogView = mInternetDialogDelegate.mDialogView;
-        mSubTitle = mDialogView.requireViewById(R.id.internet_dialog_subtitle);
-        mEthernet = mDialogView.requireViewById(R.id.ethernet_layout);
-        mMobileDataLayout = mDialogView.requireViewById(R.id.mobile_network_layout);
-        mMobileToggleSwitch = mDialogView.requireViewById(R.id.mobile_toggle);
-        mWifiToggle = mDialogView.requireViewById(R.id.turn_on_wifi_layout);
-        mWifiToggleSwitch = mDialogView.requireViewById(R.id.wifi_toggle);
-        mWifiToggleSummary = mDialogView.requireViewById(R.id.wifi_toggle_summary);
-        mConnectedWifi = mDialogView.requireViewById(R.id.wifi_connected_layout);
-        mWifiList = mDialogView.requireViewById(R.id.wifi_list_layout);
-        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();
-    }
-
-    @Test
-    public void createInternetDialog_setAccessibilityPaneTitleToQuickSettings() {
-        assertThat(mDialogView.getAccessibilityPaneTitle())
-                .isEqualTo(mContext.getText(R.string.accessibility_desc_quick_settings));
-    }
-
-    @Test
-    public void hideWifiViews_WifiViewsGone() {
-        mInternetDialogDelegate.hideWifiViews();
-
-        assertThat(mInternetDialogDelegate.mIsProgressBarVisible).isFalse();
-        assertThat(mWifiToggle.getVisibility()).isEqualTo(View.GONE);
-        assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
-        assertThat(mWifiList.getVisibility()).isEqualTo(View.GONE);
-        assertThat(mSeeAll.getVisibility()).isEqualTo(View.GONE);
-    }
-
-    @Test
-    public void updateDialog_withApmOn_internetDialogSubTitleGone() {
-        when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
-        mInternetDialogDelegate.updateDialog(true);
-        mBgExecutor.runAllReady();
-
-        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();
-
-        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();
-
-        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();
-
-        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();
-
-        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();
-
-        mInternetDialogDelegate.mDataInternetContent.observe(
-                mInternetDialogDelegate.mLifecycleOwner, i -> {
-                    assertThat(mEthernet.getVisibility()).isEqualTo(View.GONE);
-                });
-    }
-
-    @Test
-    public void updateDialog_apmOffAndNotCarrierNetwork_mobileDataLayoutGone() {
-        // Mobile network should be gone if the list of active subscriptionId is null.
-        when(mInternetDialogController.isCarrierNetworkActive()).thenReturn(false);
-        when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(false);
-        when(mInternetDialogController.hasActiveSubIdOnDds()).thenReturn(false);
-        mInternetDialogDelegate.updateDialog(true);
-        mBgExecutor.runAllReady();
-
-        mInternetDialogDelegate.mDataInternetContent.observe(
-                mInternetDialogDelegate.mLifecycleOwner, i -> {
-                    assertThat(mMobileDataLayout.getVisibility()).isEqualTo(View.GONE);
-                });
-    }
-
-    @Test
-    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();
-
-        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();
-
-        mInternetDialogDelegate.mDataInternetContent.observe(
-                mInternetDialogDelegate.mLifecycleOwner, i -> {
-                    assertThat(mMobileDataLayout.getVisibility()).isEqualTo(View.GONE);
-                });
-    }
-
-    @Test
-    public void updateDialog_apmOnAndWifiOnHasCarrierNetwork_showAirplaneSummary() {
-        when(mInternetDialogController.isCarrierNetworkActive()).thenReturn(true);
-        when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
-        mInternetDialogDelegate.mConnectedWifiEntry = null;
-        doReturn(false).when(mInternetDialogController).activeNetworkIsCellular();
-        mInternetDialogDelegate.updateDialog(true);
-        mBgExecutor.runAllReady();
-
-        mInternetDialogDelegate.mDataInternetContent.observe(
-                mInternetDialogDelegate.mLifecycleOwner, i -> {
-                    assertThat(mMobileDataLayout.getVisibility()).isEqualTo(View.VISIBLE);
-                    assertThat(mAirplaneModeSummaryText.getVisibility()).isEqualTo(View.VISIBLE);
-                });
-    }
-
-    @Test
-    public void updateDialog_apmOffAndWifiOnHasCarrierNetwork_notShowApmSummary() {
-        when(mInternetDialogController.isCarrierNetworkActive()).thenReturn(true);
-        when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(false);
-        mInternetDialogDelegate.mConnectedWifiEntry = null;
-        doReturn(false).when(mInternetDialogController).activeNetworkIsCellular();
-        mInternetDialogDelegate.updateDialog(true);
-        mBgExecutor.runAllReady();
-
-        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();
-
-        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();
-
-        mInternetDialogDelegate.mDataInternetContent.observe(
-                mInternetDialogDelegate.mLifecycleOwner, i -> {
-                    assertThat(mAirplaneModeSummaryText.getVisibility()).isEqualTo(View.GONE);
-                });
-    }
-
-    @Test
-    public void updateDialog_mobileDataIsEnabled_checkMobileDataSwitch() {
-        doReturn(true).when(mInternetDialogController).hasActiveSubIdOnDds();
-        when(mInternetDialogController.isCarrierNetworkActive()).thenReturn(true);
-        when(mInternetDialogController.isMobileDataEnabled()).thenReturn(true);
-        mMobileToggleSwitch.setChecked(false);
-        mInternetDialogDelegate.updateDialog(true);
-        mBgExecutor.runAllReady();
-
-        mInternetDialogDelegate.mDataInternetContent.observe(
-                mInternetDialogDelegate.mLifecycleOwner, i -> {
-                    assertThat(mMobileToggleSwitch.isChecked()).isTrue();
-                });
-    }
-
-    @Test
-    public void updateDialog_mobileDataIsNotChanged_checkMobileDataSwitch() {
-        doReturn(true).when(mInternetDialogController).hasActiveSubIdOnDds();
-        when(mInternetDialogController.isCarrierNetworkActive()).thenReturn(true);
-        when(mInternetDialogController.isMobileDataEnabled()).thenReturn(false);
-        mMobileToggleSwitch.setChecked(false);
-        mInternetDialogDelegate.updateDialog(true);
-        mBgExecutor.runAllReady();
-
-        mInternetDialogDelegate.mDataInternetContent.observe(
-                mInternetDialogDelegate.mLifecycleOwner, i -> {
-                    assertThat(mMobileToggleSwitch.isChecked()).isFalse();
-                });
-    }
-
-    @Test
-    public void updateDialog_wifiOnAndHasInternetWifi_showConnectedWifi() {
-        when(mInternetDialogController.getActiveAutoSwitchNonDdsSubId()).thenReturn(1);
-        doReturn(true).when(mInternetDialogController).hasActiveSubIdOnDds();
-        // The preconditions WiFi ON and Internet WiFi are already in setUp()
-        doReturn(false).when(mInternetDialogController).activeNetworkIsCellular();
-
-        mInternetDialogDelegate.updateDialog(true);
-        mBgExecutor.runAllReady();
-
-        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
-    public void updateDialog_wifiOnAndNoConnectedWifi_hideConnectedWifi() {
-        // The precondition WiFi ON is already in setUp()
-        mInternetDialogDelegate.mConnectedWifiEntry = null;
-        doReturn(false).when(mInternetDialogController).activeNetworkIsCellular();
-        mInternetDialogDelegate.updateDialog(false);
-        mBgExecutor.runAllReady();
-
-        mInternetDialogDelegate.mDataInternetContent.observe(
-                mInternetDialogDelegate.mLifecycleOwner, i -> {
-                    assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
-                });
-    }
-
-    @Test
-    public void updateDialog_wifiOnAndNoWifiEntry_showWifiListAndSeeAllArea() {
-        // The precondition WiFi ON is already in setUp()
-        mInternetDialogDelegate.mConnectedWifiEntry = null;
-        mInternetDialogDelegate.mWifiEntriesCount = 0;
-        mInternetDialogDelegate.updateDialog(false);
-        mBgExecutor.runAllReady();
-
-        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_wifiOnAndOneWifiEntry_showWifiListAndSeeAllArea() {
-        // The precondition WiFi ON is already in setUp()
-        mInternetDialogDelegate.mConnectedWifiEntry = null;
-        mInternetDialogDelegate.mWifiEntriesCount = 1;
-        mInternetDialogDelegate.updateDialog(false);
-        mBgExecutor.runAllReady();
-
-        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();
-
-        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
-    public void updateDialog_wifiOnAndHasMaxWifiList_showWifiListAndSeeAll() {
-        // The preconditions WiFi ON and WiFi entries are already in setUp()
-        mInternetDialogDelegate.mConnectedWifiEntry = null;
-        mInternetDialogDelegate.mWifiEntriesCount = MAX_WIFI_ENTRY_COUNT;
-        mInternetDialogDelegate.mHasMoreWifiEntries = true;
-        mInternetDialogDelegate.updateDialog(false);
-        mBgExecutor.runAllReady();
-
-        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
-    public void updateDialog_wifiOnAndHasBothWifiEntry_showBothWifiEntryAndSeeAll() {
-        // 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();
-
-        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
-    public void updateDialog_deviceLockedAndNoConnectedWifi_showWifiToggle() {
-        // The preconditions WiFi entries are already in setUp()
-        when(mInternetDialogController.isDeviceLocked()).thenReturn(true);
-        mInternetDialogDelegate.mConnectedWifiEntry = null;
-        mInternetDialogDelegate.updateDialog(false);
-        mBgExecutor.runAllReady();
-
-        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();
-
-        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
-    public void updateDialog_disallowChangeWifiState_disableWifiSwitch() {
-        mInternetDialogDelegate.dismissDialog();
-        when(WifiEnterpriseRestrictionUtils.isChangeWifiStateAllowed(mContext)).thenReturn(false);
-        createInternetDialog();
-        mInternetDialogDelegate.updateDialog(false);
-        mBgExecutor.runAllReady();
-
-        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
-    public void updateDialog_allowChangeWifiState_enableWifiSwitch() {
-        mInternetDialogDelegate.dismissDialog();
-        when(WifiEnterpriseRestrictionUtils.isChangeWifiStateAllowed(mContext)).thenReturn(true);
-        createInternetDialog();
-        mInternetDialogDelegate.updateDialog(false);
-        mBgExecutor.runAllReady();
-
-        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() {
-        when(mInternetDialogController.getActiveAutoSwitchNonDdsSubId()).thenReturn(1);
-        doReturn(1).when(mInternetDialogController).getActiveAutoSwitchNonDdsSubId();
-        doReturn(true).when(mInternetDialogController).hasActiveSubIdOnDds();
-        doReturn(false).when(mInternetDialogController).isAirplaneModeEnabled();
-        clearInvocations(mInternetDialogController);
-        mInternetDialogDelegate.updateDialog(true);
-        mBgExecutor.runAllReady();
-
-        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());
-                });
-    }
-
-    @Test
-    public void updateDialog_wifiOn_hideWifiScanNotify() {
-        // 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);
-    }
-
-    @Test
-    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);
-    }
-
-    @Test
-    public void updateDialog_wifiOffAndWifiScanOnAndDeviceLocked_hideWifiScanNotify() {
-        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);
-    }
-
-    @Test
-    public void updateDialog_wifiOffAndWifiScanOnAndDeviceUnlocked_showWifiScanNotify() {
-        when(mInternetDialogController.isWifiEnabled()).thenReturn(false);
-        when(mInternetDialogController.isWifiScanEnabled()).thenReturn(true);
-        when(mInternetDialogController.isDeviceLocked()).thenReturn(false);
-        mInternetDialogDelegate.updateDialog(false);
-        mBgExecutor.runAllReady();
-
-        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();
-
-        mInternetDialogDelegate.mDataInternetContent.observe(
-                mInternetDialogDelegate.mLifecycleOwner, i -> {
-                    assertThat(mWifiToggleSwitch.isChecked()).isFalse();
-                });
-    }
-
-    @Test
-    public void updateDialog_wifiIsEnabled_checkWifiSwitch() throws Exception {
-        when(mInternetDialogController.isWifiEnabled()).thenReturn(true);
-        mWifiToggleSwitch.setChecked(false);
-        mInternetDialogDelegate.updateDialog(false);
-        mBgExecutor.runAllReady();
-
-        mInternetDialogDelegate.mDataInternetContent.observe(
-                mInternetDialogDelegate.mLifecycleOwner, i -> {
-                    assertThat(mWifiToggleSwitch.isChecked()).isTrue();
-                });
-    }
-
-    @Test
-    public void onClickSeeMoreButton_clickSeeAll_verifyLaunchNetworkSetting() {
-        mSeeAll.performClick();
-
-        verify(mInternetDialogController).launchNetworkSetting(
-                mDialogView.requireViewById(R.id.see_all_layout));
-    }
-
-    @Test
-    public void onWifiScan_isScanTrue_setProgressBarVisibleTrue() {
-        mInternetDialogDelegate.mIsProgressBarVisible = false;
-
-        mInternetDialogDelegate.onWifiScan(true);
-
-        assertThat(mInternetDialogDelegate.mIsProgressBarVisible).isTrue();
-    }
-
-    @Test
-    public void onWifiScan_isScanFalse_setProgressBarVisibleFalse() {
-        mInternetDialogDelegate.mIsProgressBarVisible = true;
-
-        mInternetDialogDelegate.onWifiScan(false);
-
-        assertThat(mInternetDialogDelegate.mIsProgressBarVisible).isFalse();
-    }
-
-    @Test
-    public void getWifiListMaxCount_returnCountCorrectly() {
-        // Both of the Ethernet, MobileData is hidden.
-        // Then the maximum count is equal to MAX_WIFI_ENTRY_COUNT.
-        setNetworkVisible(false, false, false);
-
-        assertThat(mInternetDialogDelegate.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT);
-
-        // If the Connected Wi-Fi is displayed then reduce one of the Wi-Fi list max count.
-        setNetworkVisible(false, false, true);
-
-        assertThat(mInternetDialogDelegate.getWifiListMaxCount())
-                .isEqualTo(MAX_WIFI_ENTRY_COUNT - 1);
-
-        // Only one of Ethernet, MobileData is displayed.
-        // Then the maximum count is equal to MAX_WIFI_ENTRY_COUNT.
-        setNetworkVisible(true, false, false);
-
-        assertThat(mInternetDialogDelegate.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT);
-
-        setNetworkVisible(false, true, false);
-
-        assertThat(mInternetDialogDelegate.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT);
-
-        // If the Connected Wi-Fi is displayed then reduce one of the Wi-Fi list max count.
-        setNetworkVisible(true, false, true);
-
-        assertThat(mInternetDialogDelegate.getWifiListMaxCount())
-                .isEqualTo(MAX_WIFI_ENTRY_COUNT - 1);
-
-        setNetworkVisible(false, true, true);
-
-        assertThat(mInternetDialogDelegate.getWifiListMaxCount())
-                .isEqualTo(MAX_WIFI_ENTRY_COUNT - 1);
-
-        // Both of Ethernet, MobileData, ConnectedWiFi is displayed.
-        // Then the maximum count is equal to MAX_WIFI_ENTRY_COUNT - 1.
-        setNetworkVisible(true, true, false);
-
-        assertThat(mInternetDialogDelegate.getWifiListMaxCount())
-                .isEqualTo(MAX_WIFI_ENTRY_COUNT - 1);
-
-        // If the Connected Wi-Fi is displayed then reduce one of the Wi-Fi list max count.
-        setNetworkVisible(true, true, true);
-
-        assertThat(mInternetDialogDelegate.getWifiListMaxCount())
-                .isEqualTo(MAX_WIFI_ENTRY_COUNT - 2);
-    }
-
-    @Test
-    public void updateDialog_shareWifiIntentNull_hideButton() {
-        when(mInternetDialogController.getConfiguratorQrCodeGeneratorIntentOrNull(any()))
-                .thenReturn(null);
-        mInternetDialogDelegate.updateDialog(false);
-        mBgExecutor.runAllReady();
-
-        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();
-
-        mInternetDialogDelegate.mDataInternetContent.observe(
-                mInternetDialogDelegate.mLifecycleOwner, i -> {
-                    assertThat(mInternetDialogDelegate.mShareWifiButton.getVisibility())
-                            .isEqualTo(View.VISIBLE);
-                });
-    }
-
-    private void setNetworkVisible(boolean ethernetVisible, boolean mobileDataVisible,
-            boolean connectedWifiVisible) {
-        mEthernet.setVisibility(ethernetVisible ? View.VISIBLE : View.GONE);
-        mMobileDataLayout.setVisibility(mobileDataVisible ? View.VISIBLE : View.GONE);
-        mConnectedWifi.setVisibility(connectedWifiVisible ? View.VISIBLE : View.GONE);
-    }
-}
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 a0ecb80..f695c13 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/UserTrackerImplTest.kt
@@ -76,6 +76,8 @@
 
     @Mock private lateinit var iActivityManager: IActivityManager
 
+    @Mock private lateinit var beforeUserSwitchingReply: IRemoteCallback
+
     @Mock private lateinit var userSwitchingReply: IRemoteCallback
 
     @Mock(stubOnly = true) private lateinit var dumpManager: DumpManager
@@ -199,9 +201,10 @@
 
             val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java)
             verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
-            captor.value.onBeforeUserSwitching(newID)
+            captor.value.onBeforeUserSwitching(newID, beforeUserSwitchingReply)
             captor.value.onUserSwitching(newID, userSwitchingReply)
             runCurrent()
+            verify(beforeUserSwitchingReply).sendResult(any())
             verify(userSwitchingReply).sendResult(any())
 
             verify(userManager).getProfiles(newID)
@@ -341,10 +344,11 @@
 
             val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java)
             verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
-            captor.value.onBeforeUserSwitching(newID)
+            captor.value.onBeforeUserSwitching(newID, beforeUserSwitchingReply)
             captor.value.onUserSwitching(newID, userSwitchingReply)
             runCurrent()
 
+            verify(beforeUserSwitchingReply).sendResult(any())
             verify(userSwitchingReply).sendResult(any())
             assertThat(callback.calledOnUserChanging).isEqualTo(1)
             assertThat(callback.lastUser).isEqualTo(newID)
@@ -395,7 +399,7 @@
 
             val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java)
             verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
-            captor.value.onBeforeUserSwitching(newID)
+            captor.value.onBeforeUserSwitching(newID, any())
             captor.value.onUserSwitchComplete(newID)
             runCurrent()
 
@@ -453,8 +457,10 @@
 
             val captor = ArgumentCaptor.forClass(IUserSwitchObserver::class.java)
             verify(iActivityManager).registerUserSwitchObserver(capture(captor), anyString())
+            captor.value.onBeforeUserSwitching(newID, beforeUserSwitchingReply)
             captor.value.onUserSwitching(newID, userSwitchingReply)
             runCurrent()
+            verify(beforeUserSwitchingReply).sendResult(any())
             verify(userSwitchingReply).sendResult(any())
             captor.value.onUserSwitchComplete(newID)
 
@@ -488,6 +494,7 @@
         }
 
     private class TestCallback : UserTracker.Callback {
+        var calledOnBeforeUserChanging = 0
         var calledOnUserChanging = 0
         var calledOnUserChanged = 0
         var calledOnProfilesChanged = 0
@@ -495,6 +502,11 @@
         var lastUserContext: Context? = null
         var lastUserProfiles = emptyList<UserInfo>()
 
+        override fun onBeforeUserSwitching(newUser: Int) {
+            calledOnBeforeUserChanging++
+            lastUser = newUser
+        }
+
         override fun onUserChanging(newUser: Int, userContext: Context) {
             calledOnUserChanging++
             lastUser = newUser
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt
index 4cad5f7..77ca51c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt
@@ -36,7 +36,7 @@
 import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState.Idle
 import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState.RunningChipAnim
 import com.android.systemui.statusbar.events.shared.model.SystemEventAnimationState.ShowingPersistentDot
-import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
+import com.android.systemui.statusbar.layout.StatusBarContentInsetsProvider
 import com.android.systemui.statusbar.window.StatusBarWindowController
 import com.android.systemui.statusbar.window.StatusBarWindowControllerStore
 import com.android.systemui.util.time.FakeSystemClock
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DataStoreCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DataStoreCoordinatorTest.kt
index a3c5181..f31d490 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DataStoreCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/DataStoreCoordinatorTest.kt
@@ -26,7 +26,6 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
 import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection
 import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderListListener
-import com.android.systemui.statusbar.notification.collection.render.NotifStackController
 import com.android.systemui.util.mockito.withArgCaptor
 import com.google.common.truth.Truth.assertThat
 import org.junit.Before
@@ -48,7 +47,6 @@
 
     private val pipeline: NotifPipeline = mock()
     private val notifLiveDataStoreImpl: NotifLiveDataStoreImpl = mock()
-    private val stackController: NotifStackController = mock()
     private val section: NotifSection = mock()
 
     @Before
@@ -63,7 +61,7 @@
 
     @Test
     fun testUpdateDataStore_withOneEntry() {
-        afterRenderListListener.onAfterRenderList(listOf(entry), stackController)
+        afterRenderListListener.onAfterRenderList(listOf(entry))
         verify(notifLiveDataStoreImpl).setActiveNotifList(eq(listOf(entry)))
         verifyNoMoreInteractions(notifLiveDataStoreImpl)
     }
@@ -86,8 +84,7 @@
                     .setSection(section)
                     .build(),
                 notificationEntry("baz", 1),
-            ),
-            stackController,
+            )
         )
         val list: List<NotificationEntry> = withArgCaptor {
             verify(notifLiveDataStoreImpl).setActiveNotifList(capture())
@@ -111,7 +108,7 @@
 
     @Test
     fun testUpdateDataStore_withZeroEntries_whenNewPipelineEnabled() {
-        afterRenderListListener.onAfterRenderList(listOf(), stackController)
+        afterRenderListListener.onAfterRenderList(listOf())
         verify(notifLiveDataStoreImpl).setActiveNotifList(eq(listOf()))
         verifyNoMoreInteractions(notifLiveDataStoreImpl)
     }
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 2c37f51..97e99b9 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
@@ -15,7 +15,6 @@
  */
 package com.android.systemui.statusbar.notification.collection.coordinator
 
-import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
 import android.testing.TestableLooper.RunWithLooper
 import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -29,23 +28,20 @@
 import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection
 import com.android.systemui.statusbar.notification.collection.listbuilder.OnAfterRenderListListener
 import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManagerImpl
-import com.android.systemui.statusbar.notification.collection.render.NotifStackController
-import com.android.systemui.statusbar.notification.collection.render.NotifStats
+import com.android.systemui.statusbar.notification.data.model.NotifStats
 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
+import com.android.systemui.util.mockito.withArgCaptor
 import org.junit.Before
 import org.junit.Test
 import org.junit.runner.RunWith
-import org.mockito.kotlin.argumentCaptor
 import org.mockito.kotlin.eq
 import org.mockito.kotlin.mock
 import org.mockito.kotlin.verify
-import org.mockito.kotlin.verifyNoMoreInteractions
 import org.mockito.kotlin.whenever
 
 @SmallTest
@@ -63,7 +59,6 @@
     private val sensitiveNotificationProtectionController:
         SensitiveNotificationProtectionController =
         mock()
-    private val stackController: NotifStackController = mock()
     private val section: NotifSection = mock()
     private val row: ExpandableNotificationRow = mock()
 
@@ -82,198 +77,94 @@
                 sensitiveNotificationProtectionController,
             )
         coordinator.attach(pipeline)
-        val captor = argumentCaptor<OnAfterRenderListListener>()
-        verify(pipeline).addOnAfterRenderListListener(captor.capture())
-        afterRenderListListener = captor.lastValue
+        afterRenderListListener = withArgCaptor {
+            verify(pipeline).addOnAfterRenderListListener(capture())
+        }
     }
 
     @Test
     fun testSetRenderedListOnInteractor() {
-        afterRenderListListener.onAfterRenderList(listOf(entry), stackController)
+        afterRenderListListener.onAfterRenderList(listOf(entry))
         verify(renderListInteractor).setRenderedList(eq(listOf(entry)))
     }
 
     @Test
-    @EnableFlags(FooterViewRefactor.FLAG_NAME)
-    fun testSetRenderedListOnInteractor_footerFlagOn() {
-        afterRenderListListener.onAfterRenderList(listOf(entry), stackController)
-        verify(renderListInteractor).setRenderedList(eq(listOf(entry)))
-    }
-
-    @Test
-    @DisableFlags(FooterViewRefactor.FLAG_NAME)
     fun testSetNotificationStats_clearableAlerting() {
         whenever(section.bucket).thenReturn(BUCKET_ALERTING)
-        afterRenderListListener.onAfterRenderList(listOf(entry), stackController)
-        verify(stackController)
+        afterRenderListListener.onAfterRenderList(listOf(entry))
+        verify(activeNotificationsInteractor)
             .setNotifStats(
                 NotifStats(
-                    1,
                     hasNonClearableAlertingNotifs = false,
                     hasClearableAlertingNotifs = true,
                     hasNonClearableSilentNotifs = false,
                     hasClearableSilentNotifs = false,
                 )
             )
-        verifyNoMoreInteractions(activeNotificationsInteractor)
     }
 
     @Test
-    @DisableFlags(FooterViewRefactor.FLAG_NAME)
     @EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING, FLAG_SCREENSHARE_NOTIFICATION_HIDING_BUG_FIX)
     fun testSetNotificationStats_isSensitiveStateActive_nonClearableAlerting() {
         whenever(sensitiveNotificationProtectionController.isSensitiveStateActive).thenReturn(true)
         whenever(section.bucket).thenReturn(BUCKET_ALERTING)
-        afterRenderListListener.onAfterRenderList(listOf(entry), stackController)
-        verify(stackController)
+        afterRenderListListener.onAfterRenderList(listOf(entry))
+        verify(activeNotificationsInteractor)
             .setNotifStats(
                 NotifStats(
-                    1,
                     hasNonClearableAlertingNotifs = true,
                     hasClearableAlertingNotifs = false,
                     hasNonClearableSilentNotifs = false,
                     hasClearableSilentNotifs = false,
                 )
             )
-        verifyNoMoreInteractions(activeNotificationsInteractor)
     }
 
     @Test
-    @DisableFlags(FooterViewRefactor.FLAG_NAME)
     fun testSetNotificationStats_clearableSilent() {
         whenever(section.bucket).thenReturn(BUCKET_SILENT)
-        afterRenderListListener.onAfterRenderList(listOf(entry), stackController)
-        verify(stackController)
+        afterRenderListListener.onAfterRenderList(listOf(entry))
+        verify(activeNotificationsInteractor)
             .setNotifStats(
                 NotifStats(
-                    1,
                     hasNonClearableAlertingNotifs = false,
                     hasClearableAlertingNotifs = false,
                     hasNonClearableSilentNotifs = false,
                     hasClearableSilentNotifs = true,
                 )
             )
-        verifyNoMoreInteractions(activeNotificationsInteractor)
     }
 
     @Test
-    @DisableFlags(FooterViewRefactor.FLAG_NAME)
     @EnableFlags(FLAG_SCREENSHARE_NOTIFICATION_HIDING, FLAG_SCREENSHARE_NOTIFICATION_HIDING_BUG_FIX)
     fun testSetNotificationStats_isSensitiveStateActive_nonClearableSilent() {
         whenever(sensitiveNotificationProtectionController.isSensitiveStateActive).thenReturn(true)
         whenever(section.bucket).thenReturn(BUCKET_SILENT)
-        afterRenderListListener.onAfterRenderList(listOf(entry), stackController)
-        verify(stackController)
+        afterRenderListListener.onAfterRenderList(listOf(entry))
+        verify(activeNotificationsInteractor)
             .setNotifStats(
                 NotifStats(
-                    1,
                     hasNonClearableAlertingNotifs = false,
                     hasClearableAlertingNotifs = false,
                     hasNonClearableSilentNotifs = true,
                     hasClearableSilentNotifs = false,
                 )
             )
-        verifyNoMoreInteractions(activeNotificationsInteractor)
     }
 
     @Test
-    @EnableFlags(FooterViewRefactor.FLAG_NAME)
-    fun testSetNotificationStats_footerFlagOn_clearableAlerting() {
-        whenever(section.bucket).thenReturn(BUCKET_ALERTING)
-        afterRenderListListener.onAfterRenderList(listOf(entry), stackController)
-        verify(activeNotificationsInteractor)
-            .setNotifStats(
-                NotifStats(
-                    1,
-                    hasNonClearableAlertingNotifs = false,
-                    hasClearableAlertingNotifs = true,
-                    hasNonClearableSilentNotifs = false,
-                    hasClearableSilentNotifs = false,
-                )
-            )
-        verifyNoMoreInteractions(stackController)
-    }
-
-    @Test
-    @EnableFlags(
-        FooterViewRefactor.FLAG_NAME,
-        FLAG_SCREENSHARE_NOTIFICATION_HIDING,
-        FLAG_SCREENSHARE_NOTIFICATION_HIDING_BUG_FIX,
-    )
-    fun testSetNotificationStats_footerFlagOn_isSensitiveStateActive_nonClearableAlerting() {
-        whenever(sensitiveNotificationProtectionController.isSensitiveStateActive).thenReturn(true)
-        whenever(section.bucket).thenReturn(BUCKET_ALERTING)
-        afterRenderListListener.onAfterRenderList(listOf(entry), stackController)
-        verify(activeNotificationsInteractor)
-            .setNotifStats(
-                NotifStats(
-                    1,
-                    hasNonClearableAlertingNotifs = true,
-                    hasClearableAlertingNotifs = false,
-                    hasNonClearableSilentNotifs = false,
-                    hasClearableSilentNotifs = false,
-                )
-            )
-        verifyNoMoreInteractions(stackController)
-    }
-
-    @Test
-    @EnableFlags(FooterViewRefactor.FLAG_NAME)
-    fun testSetNotificationStats_footerFlagOn_clearableSilent() {
-        whenever(section.bucket).thenReturn(BUCKET_SILENT)
-        afterRenderListListener.onAfterRenderList(listOf(entry), stackController)
-        verify(activeNotificationsInteractor)
-            .setNotifStats(
-                NotifStats(
-                    1,
-                    hasNonClearableAlertingNotifs = false,
-                    hasClearableAlertingNotifs = false,
-                    hasNonClearableSilentNotifs = false,
-                    hasClearableSilentNotifs = true,
-                )
-            )
-        verifyNoMoreInteractions(stackController)
-    }
-
-    @Test
-    @EnableFlags(
-        FooterViewRefactor.FLAG_NAME,
-        FLAG_SCREENSHARE_NOTIFICATION_HIDING,
-        FLAG_SCREENSHARE_NOTIFICATION_HIDING_BUG_FIX,
-    )
-    fun testSetNotificationStats_footerFlagOn_isSensitiveStateActive_nonClearableSilent() {
-        whenever(sensitiveNotificationProtectionController.isSensitiveStateActive).thenReturn(true)
-        whenever(section.bucket).thenReturn(BUCKET_SILENT)
-        afterRenderListListener.onAfterRenderList(listOf(entry), stackController)
-        verify(activeNotificationsInteractor)
-            .setNotifStats(
-                NotifStats(
-                    1,
-                    hasNonClearableAlertingNotifs = false,
-                    hasClearableAlertingNotifs = false,
-                    hasNonClearableSilentNotifs = true,
-                    hasClearableSilentNotifs = false,
-                )
-            )
-        verifyNoMoreInteractions(stackController)
-    }
-
-    @Test
-    @EnableFlags(FooterViewRefactor.FLAG_NAME)
-    fun testSetNotificationStats_footerFlagOn_nonClearableRedacted() {
+    fun testSetNotificationStats_nonClearableRedacted() {
         entry.setSensitive(true, true)
         whenever(section.bucket).thenReturn(BUCKET_ALERTING)
-        afterRenderListListener.onAfterRenderList(listOf(entry), stackController)
+        afterRenderListListener.onAfterRenderList(listOf(entry))
         verify(activeNotificationsInteractor)
             .setNotifStats(
                 NotifStats(
-                    1,
                     hasNonClearableAlertingNotifs = true,
                     hasClearableAlertingNotifs = false,
                     hasNonClearableSilentNotifs = false,
                     hasClearableSilentNotifs = false,
                 )
             )
-        verifyNoMoreInteractions(stackController)
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/IconManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/IconManagerTest.kt
index 25138fd..57a12df 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/IconManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/icon/IconManagerTest.kt
@@ -38,6 +38,7 @@
 import com.android.systemui.Flags.FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.controls.controller.AuxiliaryPersistenceWrapperTest.Companion.any
+import com.android.systemui.statusbar.core.StatusBarConnectedDisplays
 import com.android.systemui.statusbar.notification.collection.NotificationEntry
 import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection
@@ -123,7 +124,8 @@
 
     @Test
     @EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON)
-    fun testCreateIcons_chipNotifIconFlagEnabled_statusBarChipIconIsNull() {
+    @DisableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+    fun testCreateIcons_chipNotifIconFlagEnabled_cdFlagDisabled_statusBarChipIconIsNotNull() {
         val entry =
             notificationEntry(hasShortcut = true, hasMessageSenderIcon = true, hasLargeIcon = true)
         entry?.let { iconManager.createIcons(it) }
@@ -133,6 +135,17 @@
     }
 
     @Test
+    @EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON, StatusBarConnectedDisplays.FLAG_NAME)
+    fun testCreateIcons_chipNotifIconFlagEnabled_cdFlagEnabled_statusBarChipIconIsNull() {
+        val entry =
+            notificationEntry(hasShortcut = true, hasMessageSenderIcon = true, hasLargeIcon = true)
+        entry?.let { iconManager.createIcons(it) }
+        testScope.runCurrent()
+
+        assertThat(entry?.icons?.statusBarChipIcon).isNull()
+    }
+
+    @Test
     fun testCreateIcons_importantConversation_shortcutIcon() {
         val entry =
             notificationEntry(hasShortcut = true, hasMessageSenderIcon = true, hasLargeIcon = true)
@@ -158,7 +171,7 @@
             notificationEntry(
                 hasShortcut = false,
                 hasMessageSenderIcon = false,
-                hasLargeIcon = true
+                hasLargeIcon = true,
             )
         entry?.channel?.isImportantConversation = true
         entry?.let { iconManager.createIcons(it) }
@@ -172,7 +185,7 @@
             notificationEntry(
                 hasShortcut = false,
                 hasMessageSenderIcon = false,
-                hasLargeIcon = false
+                hasLargeIcon = false,
             )
         entry?.channel?.isImportantConversation = true
         entry?.let { iconManager.createIcons(it) }
@@ -187,7 +200,7 @@
                 hasShortcut = true,
                 hasMessageSenderIcon = true,
                 useMessagingStyle = false,
-                hasLargeIcon = true
+                hasLargeIcon = true,
             )
         entry?.channel?.isImportantConversation = true
         entry?.let { iconManager.createIcons(it) }
@@ -205,7 +218,8 @@
 
     @Test
     @EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON)
-    fun testCreateIcons_sensitiveImportantConversation() {
+    @DisableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+    fun testCreateIcons_cdFlagDisabled_sensitiveImportantConversation() {
         val entry =
             notificationEntry(hasShortcut = true, hasMessageSenderIcon = true, hasLargeIcon = false)
         entry?.setSensitive(true, true)
@@ -219,8 +233,24 @@
     }
 
     @Test
+    @EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON, StatusBarConnectedDisplays.FLAG_NAME)
+    fun testCreateIcons_cdFlagEnabled_sensitiveImportantConversation() {
+        val entry =
+            notificationEntry(hasShortcut = true, hasMessageSenderIcon = true, hasLargeIcon = false)
+        entry?.setSensitive(true, true)
+        entry?.channel?.isImportantConversation = true
+        entry?.let { iconManager.createIcons(it) }
+        testScope.runCurrent()
+        assertThat(entry?.icons?.statusBarIcon?.sourceIcon).isEqualTo(shortcutIc)
+        assertThat(entry?.icons?.statusBarChipIcon).isNull()
+        assertThat(entry?.icons?.shelfIcon?.sourceIcon).isEqualTo(smallIc)
+        assertThat(entry?.icons?.aodIcon?.sourceIcon).isEqualTo(smallIc)
+    }
+
+    @Test
     @EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON)
-    fun testUpdateIcons_sensitiveImportantConversation() {
+    @DisableFlags(StatusBarConnectedDisplays.FLAG_NAME)
+    fun testUpdateIcons_cdFlagDisabled_sensitiveImportantConversation() {
         val entry =
             notificationEntry(hasShortcut = true, hasMessageSenderIcon = true, hasLargeIcon = false)
         entry?.setSensitive(true, true)
@@ -236,6 +266,23 @@
     }
 
     @Test
+    @EnableFlags(FLAG_STATUS_BAR_CALL_CHIP_NOTIFICATION_ICON, StatusBarConnectedDisplays.FLAG_NAME)
+    fun testUpdateIcons_cdFlagEnabled_sensitiveImportantConversation() {
+        val entry =
+            notificationEntry(hasShortcut = true, hasMessageSenderIcon = true, hasLargeIcon = false)
+        entry?.setSensitive(true, true)
+        entry?.channel?.isImportantConversation = true
+        entry?.let { iconManager.createIcons(it) }
+        // Updating the icons after creation shouldn't break anything
+        entry?.let { iconManager.updateIcons(it) }
+        testScope.runCurrent()
+        assertThat(entry?.icons?.statusBarIcon?.sourceIcon).isEqualTo(shortcutIc)
+        assertThat(entry?.icons?.statusBarChipIcon).isNull()
+        assertThat(entry?.icons?.shelfIcon?.sourceIcon).isEqualTo(smallIc)
+        assertThat(entry?.icons?.aodIcon?.sourceIcon).isEqualTo(smallIc)
+    }
+
+    @Test
     fun testUpdateIcons_sensitivityChange() {
         val entry =
             notificationEntry(hasShortcut = true, hasMessageSenderIcon = true, hasLargeIcon = false)
@@ -254,7 +301,7 @@
         hasShortcut: Boolean,
         hasMessageSenderIcon: Boolean,
         useMessagingStyle: Boolean = true,
-        hasLargeIcon: Boolean
+        hasLargeIcon: Boolean,
     ): NotificationEntry? {
         val n =
             Notification.Builder(mContext, "id")
@@ -270,7 +317,7 @@
                         SystemClock.currentThreadTimeMillis(),
                         Person.Builder()
                             .setIcon(if (hasMessageSenderIcon) messageIc else null)
-                            .build()
+                            .build(),
                     )
                 )
         if (useMessagingStyle) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt
index 2d35ea5..61943f2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerWithScenesTest.kt
@@ -65,7 +65,7 @@
 import com.android.systemui.statusbar.notification.NotificationActivityStarter
 import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider
 import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
-import com.android.systemui.statusbar.notification.headsup.headsUpManager
+import com.android.systemui.statusbar.notification.headsup.mockHeadsUpManager
 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
 import com.android.systemui.statusbar.notification.stack.NotificationListContainer
 import com.android.systemui.statusbar.notificationLockscreenUserManager
@@ -128,7 +128,7 @@
     private val shadeController = kosmos.shadeControllerSceneImpl
     private val notificationLockscreenUserManager = kosmos.notificationLockscreenUserManager
     private val statusBarStateController = kosmos.statusBarStateController
-    private val headsUpManager = kosmos.headsUpManager
+    private val headsUpManager = kosmos.mockHeadsUpManager
     private val activityStarter = kosmos.activityStarter
     private val userManager = kosmos.userManager
     private val activeNotificationsInteractor = kosmos.activeNotificationsInteractor
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 e1a8916..3763282 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
@@ -16,7 +16,6 @@
 
 package com.android.systemui.statusbar.notification.stack;
 
-import static android.view.View.GONE;
 import static android.view.WindowInsets.Type.ime;
 
 import static com.android.systemui.flags.SceneContainerFlagParameterizationKt.parameterizeSceneContainerFlag;
@@ -28,17 +27,14 @@
 import static com.google.common.truth.Truth.assertWithMessage;
 
 import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertNotNull;
 import static junit.framework.Assert.assertTrue;
 
 import static org.junit.Assert.assertFalse;
-import static org.mockito.AdditionalMatchers.not;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyFloat;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doNothing;
@@ -64,7 +60,6 @@
 import android.view.ViewGroup;
 import android.view.WindowInsets;
 import android.view.WindowInsetsAnimation;
-import android.widget.TextView;
 
 import androidx.test.filters.SmallTest;
 
@@ -92,8 +87,6 @@
 import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
 import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyShadeFix;
 import com.android.systemui.statusbar.notification.emptyshade.ui.view.EmptyShadeView;
-import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor;
-import com.android.systemui.statusbar.notification.footer.shared.NotifRedesignFooter;
 import com.android.systemui.statusbar.notification.footer.ui.view.FooterView;
 import com.android.systemui.statusbar.notification.headsup.AvalancheController;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -603,158 +596,6 @@
     }
 
     @Test
-    @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
-    public void manageNotifications_visible() {
-        FooterView view = mock(FooterView.class);
-        mStackScroller.setFooterView(view);
-        when(view.willBeGone()).thenReturn(true);
-
-        mStackScroller.updateFooterView(true, false, true);
-
-        verify(view).setVisible(eq(true), anyBoolean());
-        verify(view).setClearAllButtonVisible(eq(false), anyBoolean());
-    }
-
-    @Test
-    @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
-    public void clearAll_visible() {
-        FooterView view = mock(FooterView.class);
-        mStackScroller.setFooterView(view);
-        when(view.willBeGone()).thenReturn(true);
-
-        mStackScroller.updateFooterView(true, true, true);
-
-        verify(view).setVisible(eq(true), anyBoolean());
-        verify(view).setClearAllButtonVisible(eq(true), anyBoolean());
-    }
-
-    @Test
-    @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
-    public void testInflateFooterView() {
-        mStackScroller.inflateFooterView();
-        ArgumentCaptor<FooterView> captor = ArgumentCaptor.forClass(FooterView.class);
-        verify(mStackScroller).setFooterView(captor.capture());
-
-        assertNotNull(captor.getValue().findViewById(R.id.manage_text));
-        assertNotNull(captor.getValue().findViewById(R.id.dismiss_text));
-    }
-
-    @Test
-    @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
-    public void testUpdateFooter_noNotifications() {
-        setBarStateForTest(StatusBarState.SHADE);
-        mStackScroller.setCurrentUserSetup(true);
-
-        FooterView view = mock(FooterView.class);
-        mStackScroller.setFooterView(view);
-        mStackScroller.updateFooter();
-        verify(mStackScroller, atLeastOnce()).updateFooterView(false, false, true);
-    }
-
-    @Test
-    @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
-    @DisableSceneContainer
-    public void testUpdateFooter_remoteInput() {
-        setBarStateForTest(StatusBarState.SHADE);
-        mStackScroller.setCurrentUserSetup(true);
-
-        mStackScroller.setIsRemoteInputActive(true);
-        when(mStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1);
-        when(mStackScrollLayoutController.hasActiveClearableNotifications(eq(ROWS_ALL)))
-                .thenReturn(true);
-
-        FooterView view = mock(FooterView.class);
-        mStackScroller.setFooterView(view);
-        mStackScroller.updateFooter();
-        verify(mStackScroller, atLeastOnce()).updateFooterView(false, true, true);
-    }
-
-    @Test
-    @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
-    public void testUpdateFooter_withoutNotifications() {
-        setBarStateForTest(StatusBarState.SHADE);
-        mStackScroller.setCurrentUserSetup(true);
-
-        when(mStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
-        when(mStackScrollLayoutController.hasActiveClearableNotifications(eq(ROWS_ALL)))
-                .thenReturn(false);
-
-        FooterView view = mock(FooterView.class);
-        mStackScroller.setFooterView(view);
-        mStackScroller.updateFooter();
-        verify(mStackScroller, atLeastOnce()).updateFooterView(false, false, true);
-    }
-
-    @Test
-    @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
-    @DisableSceneContainer
-    public void testUpdateFooter_oneClearableNotification() {
-        setBarStateForTest(StatusBarState.SHADE);
-        mStackScroller.setCurrentUserSetup(true);
-
-        when(mStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1);
-        when(mStackScrollLayoutController.hasActiveClearableNotifications(eq(ROWS_ALL)))
-                .thenReturn(true);
-
-        FooterView view = mock(FooterView.class);
-        mStackScroller.setFooterView(view);
-        mStackScroller.updateFooter();
-        verify(mStackScroller, atLeastOnce()).updateFooterView(true, true, true);
-    }
-
-    @Test
-    @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
-    @DisableSceneContainer
-    public void testUpdateFooter_withoutHistory() {
-        setBarStateForTest(StatusBarState.SHADE);
-        mStackScroller.setCurrentUserSetup(true);
-
-        when(mStackScrollLayoutController.isHistoryEnabled()).thenReturn(false);
-        when(mStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1);
-        when(mStackScrollLayoutController.hasActiveClearableNotifications(eq(ROWS_ALL)))
-                .thenReturn(true);
-
-        FooterView view = mock(FooterView.class);
-        mStackScroller.setFooterView(view);
-        mStackScroller.updateFooter();
-        verify(mStackScroller, atLeastOnce()).updateFooterView(true, true, false);
-    }
-
-    @Test
-    @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
-    public void testUpdateFooter_oneClearableNotification_beforeUserSetup() {
-        setBarStateForTest(StatusBarState.SHADE);
-        mStackScroller.setCurrentUserSetup(false);
-
-        when(mStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1);
-        when(mStackScrollLayoutController.hasActiveClearableNotifications(eq(ROWS_ALL)))
-                .thenReturn(true);
-
-        FooterView view = mock(FooterView.class);
-        mStackScroller.setFooterView(view);
-        mStackScroller.updateFooter();
-        verify(mStackScroller, atLeastOnce()).updateFooterView(false, true, true);
-    }
-
-    @Test
-    @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
-    @DisableSceneContainer
-    public void testUpdateFooter_oneNonClearableNotification() {
-        setBarStateForTest(StatusBarState.SHADE);
-        mStackScroller.setCurrentUserSetup(true);
-
-        when(mStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1);
-        when(mStackScrollLayoutController.hasActiveClearableNotifications(eq(ROWS_ALL)))
-                .thenReturn(false);
-        when(mEmptyShadeView.getVisibility()).thenReturn(GONE);
-
-        FooterView view = mock(FooterView.class);
-        mStackScroller.setFooterView(view);
-        mStackScroller.updateFooter();
-        verify(mStackScroller, atLeastOnce()).updateFooterView(true, false, true);
-    }
-
-    @Test
     public void testFooterPosition_atEnd() {
         // add footer
         FooterView view = mock(FooterView.class);
@@ -772,19 +613,6 @@
     }
 
     @Test
-    @DisableFlags({FooterViewRefactor.FLAG_NAME,
-        ModesEmptyShadeFix.FLAG_NAME,
-        NotifRedesignFooter.FLAG_NAME})
-    public void testReInflatesFooterViews() {
-        when(mEmptyShadeView.getTextResource()).thenReturn(R.string.empty_shade_text);
-        clearInvocations(mStackScroller);
-        mStackScroller.reinflateViews();
-        verify(mStackScroller).setFooterView(any());
-        verify(mStackScroller).setEmptyShadeView(any());
-    }
-
-    @Test
-    @EnableFlags(FooterViewRefactor.FLAG_NAME)
     @DisableFlags(ModesEmptyShadeFix.FLAG_NAME)
     public void testReInflatesEmptyShadeView() {
         when(mEmptyShadeView.getTextResource()).thenReturn(R.string.empty_shade_text);
@@ -1231,31 +1059,6 @@
     }
 
     @Test
-    @DisableFlags({FooterViewRefactor.FLAG_NAME, NotifRedesignFooter.FLAG_NAME})
-    public void hasFilteredOutSeenNotifs_updateFooter() {
-        mStackScroller.setCurrentUserSetup(true);
-
-        // add footer
-        mStackScroller.inflateFooterView();
-        TextView footerLabel =
-                mStackScroller.mFooterView.requireViewById(R.id.unlock_prompt_footer);
-
-        mStackScroller.setHasFilteredOutSeenNotifications(true);
-        mStackScroller.updateFooter();
-
-        assertThat(footerLabel.getVisibility()).isEqualTo(View.VISIBLE);
-    }
-
-    @Test
-    @DisableFlags({FooterViewRefactor.FLAG_NAME, ModesEmptyShadeFix.FLAG_NAME})
-    public void hasFilteredOutSeenNotifs_updateEmptyShadeView() {
-        mStackScroller.setHasFilteredOutSeenNotifications(true);
-        mStackScroller.updateEmptyShadeView(true, false);
-
-        verify(mEmptyShadeView).setFooterText(not(eq(0)));
-    }
-
-    @Test
     @DisableSceneContainer
     public void testWindowInsetAnimationProgress_updatesBottomInset() {
         int imeInset = 100;
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 411c81d13..7d3aa47 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
@@ -36,6 +36,8 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
+import static kotlinx.coroutines.flow.FlowKt.flowOf;
+
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -54,8 +56,6 @@
 
 import static java.util.Collections.emptySet;
 
-import static kotlinx.coroutines.flow.FlowKt.flowOf;
-
 import android.app.ActivityManager;
 import android.app.IWallpaperManager;
 import android.app.NotificationManager;
@@ -132,6 +132,7 @@
 import com.android.systemui.notetask.NoteTaskController;
 import com.android.systemui.plugins.ActivityStarter;
 import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
+import com.android.systemui.plugins.DarkIconDispatcher;
 import com.android.systemui.plugins.PluginDependencyProvider;
 import com.android.systemui.plugins.PluginManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -175,6 +176,7 @@
 import com.android.systemui.statusbar.core.StatusBarConnectedDisplays;
 import com.android.systemui.statusbar.core.StatusBarInitializerImpl;
 import com.android.systemui.statusbar.data.repository.FakeStatusBarModeRepository;
+import com.android.systemui.statusbar.data.repository.StatusBarConfigurationController;
 import com.android.systemui.statusbar.data.repository.StatusBarModePerDisplayRepository;
 import com.android.systemui.statusbar.notification.NotifPipelineFlags;
 import com.android.systemui.statusbar.notification.NotificationActivityStarter;
@@ -213,9 +215,14 @@
 import com.android.systemui.util.settings.SystemSettings;
 import com.android.systemui.util.time.FakeSystemClock;
 import com.android.systemui.volume.VolumeComponent;
+import com.android.systemui.wallet.controller.QuickAccessWalletController;
 import com.android.wm.shell.bubbles.Bubbles;
 import com.android.wm.shell.startingsurface.StartingSurface;
 
+import dagger.Lazy;
+
+import kotlinx.coroutines.test.TestScope;
+
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -232,9 +239,6 @@
 
 import javax.inject.Provider;
 
-import dagger.Lazy;
-import kotlinx.coroutines.test.TestScope;
-
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 @RunWithLooper(setAsMainLooper = true)
@@ -370,6 +374,7 @@
     @Mock private NotificationSettingsInteractor mNotificationSettingsInteractor;
     @Mock private ViewCaptureAwareWindowManager mViewCaptureAwareWindowManager;
     @Mock private StatusBarLongPressGestureDetector mStatusBarLongPressGestureDetector;
+    @Mock private QuickAccessWalletController mQuickAccessWalletController;
     private ShadeController mShadeController;
     private final FakeSystemClock mFakeSystemClock = new FakeSystemClock();
     private final FakeGlobalSettings mFakeGlobalSettings = new FakeGlobalSettings();
@@ -536,6 +541,8 @@
                 new StatusBarInitializerImpl(
                         mStatusBarWindowController,
                         mStatusBarModePerDisplayRepository,
+                        mock(StatusBarConfigurationController.class),
+                        mock(DarkIconDispatcher.class),
                         mCollapsedStatusBarFragmentProvider,
                         mock(StatusBarRootFactory.class),
                         mock(HomeStatusBarComponent.Factory.class),
@@ -635,7 +642,8 @@
                 mBrightnessMirrorShowingInteractor,
                 mGlanceableHubContainerController,
                 mEmergencyGestureIntentFactory,
-                mViewCaptureAwareWindowManager
+                mViewCaptureAwareWindowManager,
+                mQuickAccessWalletController
         );
         mScreenLifecycle.addObserver(mCentralSurfaces.mScreenObserver);
         mCentralSurfaces.initShadeVisibilityListener();
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 0ba0aeb..243be3d 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
@@ -21,8 +21,12 @@
 import android.app.StatusBarManager.WINDOW_STATE_SHOWING
 import android.app.StatusBarManager.WINDOW_STATUS_BAR
 import android.graphics.Insets
+import android.hardware.display.DisplayManagerGlobal
 import android.platform.test.annotations.DisableFlags
 import android.platform.test.annotations.EnableFlags
+import android.view.Display
+import android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS
+import android.view.DisplayInfo
 import android.view.InputDevice
 import android.view.LayoutInflater
 import android.view.MotionEvent
@@ -35,6 +39,7 @@
 import androidx.test.platform.app.InstrumentationRegistry
 import com.android.systemui.Flags as AconfigFlags
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.SysuiTestableContext
 import com.android.systemui.battery.BatteryMeterView
 import com.android.systemui.flags.FeatureFlags
 import com.android.systemui.flags.Flags
@@ -45,7 +50,6 @@
 import com.android.systemui.shade.ShadeLogger
 import com.android.systemui.shade.ShadeViewController
 import com.android.systemui.shade.StatusBarLongPressGestureDetector
-import com.android.systemui.shade.data.repository.fakeShadeDisplaysRepository
 import com.android.systemui.shade.display.StatusBarTouchShadeDisplayPolicy
 import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor
 import com.android.systemui.statusbar.CommandQueue
@@ -78,7 +82,6 @@
 import org.mockito.Mockito.verify
 import org.mockito.Mockito.`when`
 import org.mockito.MockitoAnnotations
-import org.mockito.kotlin.verifyNoMoreInteractions
 
 @SmallTest
 @RunWith(AndroidJUnit4::class)
@@ -88,7 +91,6 @@
     private val statusBarContentInsetsProvider = statusBarContentInsetsProviderStore.defaultDisplay
 
     private val fakeDarkIconDispatcher = kosmos.fakeDarkIconDispatcher
-    private val fakeShadeDisplaysRepository = kosmos.fakeShadeDisplaysRepository
     @Mock private lateinit var shadeViewController: ShadeViewController
     @Mock private lateinit var panelExpansionInteractor: PanelExpansionInteractor
     @Mock private lateinit var featureFlags: FeatureFlags
@@ -111,6 +113,8 @@
     private lateinit var view: PhoneStatusBarView
     private lateinit var controller: PhoneStatusBarViewController
 
+    private lateinit var viewForSecondaryDisplay: PhoneStatusBarView
+
     private val clockView: Clock
         get() = view.requireViewById(R.id.clock)
 
@@ -138,6 +142,26 @@
                     as PhoneStatusBarView
             controller = createAndInitController(view)
         }
+
+        val contextForSecondaryDisplay =
+            SysuiTestableContext(
+                mContext.createDisplayContext(
+                    Display(
+                        DisplayManagerGlobal.getInstance(),
+                        SECONDARY_DISPLAY_ID,
+                        DisplayInfo(),
+                        DEFAULT_DISPLAY_ADJUSTMENTS,
+                    )
+                )
+            )
+
+        InstrumentationRegistry.getInstrumentation().runOnMainSync {
+            val parent = FrameLayout(contextForSecondaryDisplay) // add parent to keep layout params
+            viewForSecondaryDisplay =
+                LayoutInflater.from(contextForSecondaryDisplay)
+                    .inflate(R.layout.status_bar, parent, false) as PhoneStatusBarView
+            createAndInitController(viewForSecondaryDisplay)
+        }
     }
 
     @Test
@@ -264,10 +288,9 @@
 
     @Test
     @DisableFlags(AconfigFlags.FLAG_STATUS_BAR_CONNECTED_DISPLAYS)
-    fun handleTouchEventFromStatusBar_statusBarConnectedDisplaysDisabled_viewReceivesEvent() {
+    fun handleTouchEventFromStatusBar_touchOnPrimaryDisplay_statusBarConnectedDisplaysDisabled_shadeReceivesEvent() {
         `when`(centralSurfacesImpl.commandQueuePanelsEnabled).thenReturn(true)
         `when`(shadeViewController.isViewEnabled).thenReturn(true)
-        fakeShadeDisplaysRepository.setDisplayId(SECONDARY_DISPLAY_ID)
         val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 2f, 0)
 
         view.onTouchEvent(event)
@@ -280,10 +303,9 @@
         AconfigFlags.FLAG_STATUS_BAR_CONNECTED_DISPLAYS,
         AconfigFlags.FLAG_SHADE_WINDOW_GOES_AROUND,
     )
-    fun handleTouchEventFromStatusBar_statusBarConnectedDisplaysEnabled_shadeWindowGoesAroundEnabled_viewReceivesEvent() {
+    fun handleTouchEventFromStatusBar_touchOnPrimaryDisplay_statusBarConnectedDisplaysEnabled_shadeWindowGoesAroundEnabled_shadeReceivesEvent() {
         `when`(centralSurfacesImpl.commandQueuePanelsEnabled).thenReturn(true)
         `when`(shadeViewController.isViewEnabled).thenReturn(true)
-        fakeShadeDisplaysRepository.setDisplayId(SECONDARY_DISPLAY_ID)
         val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 2f, 0)
 
         view.onTouchEvent(event)
@@ -294,10 +316,9 @@
     @Test
     @EnableFlags(AconfigFlags.FLAG_STATUS_BAR_CONNECTED_DISPLAYS)
     @DisableFlags(AconfigFlags.FLAG_SHADE_WINDOW_GOES_AROUND)
-    fun handleTouchEventFromStatusBar_touchOnShadeDisplay_statusBarConnectedDisplaysEnabled_shadeWindowGoesAroundDisabled_viewReceivesEvent() {
+    fun handleTouchEventFromStatusBar_touchOnPrimaryDisplay_statusBarConnectedDisplaysEnabled_shadeWindowGoesAroundDisabled_shadeReceivesEvent() {
         `when`(centralSurfacesImpl.commandQueuePanelsEnabled).thenReturn(true)
         `when`(shadeViewController.isViewEnabled).thenReturn(true)
-        fakeShadeDisplaysRepository.setDisplayId(DISPLAY_ID)
         val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 2f, 0)
 
         view.onTouchEvent(event)
@@ -306,18 +327,43 @@
     }
 
     @Test
-    @EnableFlags(AconfigFlags.FLAG_STATUS_BAR_CONNECTED_DISPLAYS)
-    @DisableFlags(AconfigFlags.FLAG_SHADE_WINDOW_GOES_AROUND)
-    fun handleTouchEventFromStatusBar_touchNotOnShadeDisplay_statusBarConnectedDisplaysEnabled_shadeWindowGoesAroundDisabled_viewDoesNotReceiveEvent() {
+    @DisableFlags(AconfigFlags.FLAG_STATUS_BAR_CONNECTED_DISPLAYS)
+    fun handleTouchEventFromStatusBar_touchOnSecondaryDisplay_statusBarConnectedDisplaysDisabled_shadeReceivesEvent() {
         `when`(centralSurfacesImpl.commandQueuePanelsEnabled).thenReturn(true)
         `when`(shadeViewController.isViewEnabled).thenReturn(true)
-        fakeShadeDisplaysRepository.setDisplayId(SECONDARY_DISPLAY_ID)
         val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 2f, 0)
 
-        view.onTouchEvent(event)
+        viewForSecondaryDisplay.onTouchEvent(event)
 
-        verify(shadeViewController).isViewEnabled
-        verifyNoMoreInteractions(shadeViewController)
+        verify(shadeViewController).handleExternalTouch(event)
+    }
+
+    @Test
+    @EnableFlags(
+        AconfigFlags.FLAG_STATUS_BAR_CONNECTED_DISPLAYS,
+        AconfigFlags.FLAG_SHADE_WINDOW_GOES_AROUND,
+    )
+    fun handleTouchEventFromStatusBar_touchOnSecondaryDisplay_statusBarConnectedDisplaysEnabled_shadeWindowGoesAroundEnabled_shadeReceivesEvent() {
+        `when`(centralSurfacesImpl.commandQueuePanelsEnabled).thenReturn(true)
+        `when`(shadeViewController.isViewEnabled).thenReturn(true)
+        val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 2f, 0)
+
+        viewForSecondaryDisplay.onTouchEvent(event)
+
+        verify(shadeViewController).handleExternalTouch(event)
+    }
+
+    @Test
+    @EnableFlags(AconfigFlags.FLAG_STATUS_BAR_CONNECTED_DISPLAYS)
+    @DisableFlags(AconfigFlags.FLAG_SHADE_WINDOW_GOES_AROUND)
+    fun handleTouchEventFromStatusBar_touchOnSecondaryDisplay_statusBarConnectedDisplaysEnabled_shadeWindowGoesAroundDisabled_shadeDoesNotReceiveEvent() {
+        `when`(centralSurfacesImpl.commandQueuePanelsEnabled).thenReturn(true)
+        `when`(shadeViewController.isViewEnabled).thenReturn(true)
+        val event = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 2f, 0)
+
+        viewForSecondaryDisplay.onTouchEvent(event)
+
+        verify(shadeViewController, never()).handleExternalTouch(event)
     }
 
     @Test
@@ -493,7 +539,6 @@
                 fakeDarkIconDispatcher,
                 statusBarContentInsetsProviderStore,
                 Lazy { statusBarTouchShadeDisplayPolicy },
-                fakeShadeDisplaysRepository,
             )
             .create(view)
             .also { it.init() }
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 0b443675..30ab416 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
@@ -25,6 +25,7 @@
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertTrue;
 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;
@@ -41,6 +42,7 @@
 import android.testing.TestableLooper.RunWithLooper;
 import android.view.View;
 
+import androidx.annotation.NonNull;
 import androidx.test.filters.SmallTest;
 
 import com.android.keyguard.KeyguardUpdateMonitor;
@@ -61,6 +63,9 @@
 import com.android.systemui.statusbar.OperatorNameViewController;
 import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips;
 import com.android.systemui.statusbar.core.StatusBarRootModernization;
+import com.android.systemui.statusbar.data.repository.DarkIconDispatcherStore;
+import com.android.systemui.statusbar.data.repository.StatusBarConfigurationController;
+import com.android.systemui.statusbar.data.repository.StatusBarConfigurationControllerStore;
 import com.android.systemui.statusbar.disableflags.DisableFlagsLogger;
 import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
 import com.android.systemui.statusbar.notification.icon.ui.viewbinder.NotificationIconContainerStatusBarViewBinder;
@@ -73,8 +78,12 @@
 import com.android.systemui.statusbar.phone.ui.StatusBarIconController;
 import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.FakeHomeStatusBarViewBinder;
 import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.FakeHomeStatusBarViewModel;
+import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.HomeStatusBarViewModel;
+import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.HomeStatusBarViewModel.HomeStatusBarViewModelFactory;
 import com.android.systemui.statusbar.pipeline.shared.ui.viewmodel.StatusBarOperatorNameViewModel;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.statusbar.window.StatusBarWindowController;
+import com.android.systemui.statusbar.window.StatusBarWindowControllerStore;
 import com.android.systemui.statusbar.window.StatusBarWindowStateController;
 import com.android.systemui.statusbar.window.StatusBarWindowStateListener;
 import com.android.systemui.util.CarrierConfigTracker;
@@ -134,6 +143,12 @@
     private StatusBarWindowStateController mStatusBarWindowStateController;
     @Mock
     private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+    @Mock private StatusBarWindowControllerStore mStatusBarWindowControllerStore;
+    @Mock private StatusBarWindowController mStatusBarWindowController;
+    @Mock private StatusBarConfigurationControllerStore mStatusBarConfigurationControllerStore;
+    @Mock private StatusBarConfigurationController mStatusBarConfigurationController;
+    @Mock private DarkIconDispatcherStore mDarkIconDispatcherStore;
+    @Mock private DarkIconDispatcher mDarkIconDispatcher;
     @Rule
     public final AnimatorTestRule mAnimatorTestRule = new AnimatorTestRule(this);
 
@@ -145,6 +160,12 @@
 
     @Before
     public void setup() {
+        when(mStatusBarWindowControllerStore.forDisplay(anyInt()))
+                .thenReturn(mStatusBarWindowController);
+        when(mStatusBarConfigurationControllerStore.forDisplay(anyInt()))
+                .thenReturn(mStatusBarConfigurationController);
+        when(mDarkIconDispatcherStore.forDisplay(anyInt())).thenReturn(mDarkIconDispatcher);
+
         injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
         mDependency.injectMockDependency(DarkIconDispatcher.class);
 
@@ -1250,6 +1271,15 @@
                 mock(StatusBarOperatorNameViewModel.class));
         mCollapsedStatusBarViewBinder = new FakeHomeStatusBarViewBinder();
 
+        HomeStatusBarViewModelFactory homeStatusBarViewModelFactory =
+                new HomeStatusBarViewModelFactory() {
+            @NonNull
+            @Override
+            public HomeStatusBarViewModel create(int displayId) {
+                return mCollapsedStatusBarViewModel;
+            }
+        };
+
         return new CollapsedStatusBarFragment(
                 mStatusBarFragmentComponentFactory,
                 mOngoingCallController,
@@ -1257,7 +1287,7 @@
                 mShadeExpansionStateManager,
                 mStatusBarIconController,
                 mIconManagerFactory,
-                mCollapsedStatusBarViewModel,
+                homeStatusBarViewModelFactory,
                 mCollapsedStatusBarViewBinder,
                 mStatusBarHideIconsForBouncerManager,
                 mKeyguardStateController,
@@ -1276,11 +1306,14 @@
                 mDumpManager,
                 mStatusBarWindowStateController,
                 mKeyguardUpdateMonitor,
-                mock(DemoModeController.class));
+                mock(DemoModeController.class),
+                mStatusBarWindowControllerStore,
+                mStatusBarConfigurationControllerStore,
+                mDarkIconDispatcherStore);
     }
 
     private void setUpDaggerComponent() {
-        when(mStatusBarFragmentComponentFactory.create(any()))
+        when(mStatusBarFragmentComponentFactory.create(any(), any(), any(), any()))
                 .thenReturn(mHomeStatusBarComponent);
         when(mHomeStatusBarComponent.getHeadsUpAppearanceController())
                 .thenReturn(mHeadsUpAppearanceController);
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 728f418..3a25ecb 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
@@ -37,7 +37,7 @@
 import android.telephony.SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX
 import android.telephony.SubscriptionManager.PROFILE_CLASS_UNSET
 import android.telephony.TelephonyCallback
-import android.telephony.TelephonyCallback.CarrierRoamingNtnModeListener
+import android.telephony.TelephonyCallback.CarrierRoamingNtnListener
 import android.telephony.TelephonyCallback.DataActivityListener
 import android.telephony.TelephonyCallback.DisplayInfoListener
 import android.telephony.TelephonyCallback.ServiceStateListener
@@ -1311,7 +1311,7 @@
             // Starts out false
             assertThat(latest).isFalse()
 
-            val callback = getTelephonyCallbackForType<CarrierRoamingNtnModeListener>()
+            val callback = getTelephonyCallbackForType<CarrierRoamingNtnListener>()
 
             callback.onCarrierRoamingNtnModeChanged(true)
             assertThat(latest).isTrue()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt
index 89f2d3d..599729d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/data/prod/DeviceBasedSatelliteRepositoryImplTest.kt
@@ -22,7 +22,7 @@
 import android.telephony.TelephonyManager
 import android.telephony.satellite.NtnSignalStrength
 import android.telephony.satellite.NtnSignalStrengthCallback
-import android.telephony.satellite.SatelliteCommunicationAllowedStateCallback
+import android.telephony.satellite.SatelliteCommunicationAccessStateCallback
 import android.telephony.satellite.SatelliteManager
 import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_CONNECTED
 import android.telephony.satellite.SatelliteManager.SATELLITE_MODEM_STATE_DATAGRAM_RETRYING
@@ -193,19 +193,19 @@
             runCurrent()
 
             val callback =
-                withArgCaptor<SatelliteCommunicationAllowedStateCallback> {
+                withArgCaptor<SatelliteCommunicationAccessStateCallback> {
                     verify(satelliteManager)
-                        .registerForCommunicationAllowedStateChanged(any(), capture())
+                        .registerForCommunicationAccessStateChanged(any(), capture())
                 }
 
             // WHEN satellite manager says it's not available
-            callback.onSatelliteCommunicationAllowedStateChanged(false)
+            callback.onAccessAllowedStateChanged(false)
 
             // THEN it's not!
             assertThat(latest).isFalse()
 
             // WHEN satellite manager says it's changed to available
-            callback.onSatelliteCommunicationAllowedStateChanged(true)
+            callback.onAccessAllowedStateChanged(true)
 
             // THEN it is!
             assertThat(latest).isTrue()
@@ -219,7 +219,7 @@
             // GIVEN SatelliteManager gon' throw exceptions when we ask to register the callback
             doThrow(RuntimeException("Test exception"))
                 .`when`(satelliteManager)
-                .registerForCommunicationAllowedStateChanged(any(), any())
+                .registerForCommunicationAccessStateChanged(any(), any())
 
             // WHEN the latest value is requested (and thus causes an exception to be thrown)
             val latest by collectLastValue(underTest.isSatelliteAllowedForCurrentLocation)
@@ -236,9 +236,9 @@
             runCurrent()
 
             val callback =
-                withArgCaptor<SatelliteCommunicationAllowedStateCallback> {
+                withArgCaptor<SatelliteCommunicationAccessStateCallback> {
                     verify(satelliteManager)
-                        .registerForCommunicationAllowedStateChanged(any(), capture())
+                        .registerForCommunicationAccessStateChanged(any(), capture())
                 }
 
             val telephonyCallback =
@@ -249,7 +249,7 @@
                 )
 
             // GIVEN satellite is currently provisioned
-            callback.onSatelliteCommunicationAllowedStateChanged(true)
+            callback.onAccessAllowedStateChanged(true)
 
             assertThat(latest).isTrue()
 
@@ -261,7 +261,7 @@
 
             // THEN listener is re-registered
             verify(satelliteManager, times(2))
-                .registerForCommunicationAllowedStateChanged(any(), any())
+                .registerForCommunicationAccessStateChanged(any(), any())
         }
 
     @Test
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/user/domain/interactor/RefreshUsersSchedulerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/RefreshUsersSchedulerTest.kt
similarity index 100%
rename from packages/SystemUI/multivalentTests/src/com/android/systemui/user/domain/interactor/RefreshUsersSchedulerTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/user/domain/interactor/RefreshUsersSchedulerTest.kt
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java
index 40094e5..733e2ed 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/QuickAccessWalletControllerTest.java
@@ -16,21 +16,26 @@
 
 package com.android.systemui.wallet.controller;
 
+import static android.service.quickaccesswallet.Flags.FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotSame;
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.app.PendingIntent;
 import android.app.role.RoleManager;
 import android.content.Intent;
+import android.platform.test.annotations.EnableFlags;
 import android.service.quickaccesswallet.GetWalletCardsRequest;
 import android.service.quickaccesswallet.QuickAccessWalletClient;
 import android.testing.TestableLooper;
@@ -54,6 +59,7 @@
 import org.mockito.Captor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
 
 import java.util.List;
 
@@ -101,15 +107,22 @@
             callback.onWalletPendingIntentRetrieved(null);
             return null;
         }).when(mQuickAccessWalletClient).getWalletPendingIntent(any(), any());
+        doAnswer(invocation -> {
+            QuickAccessWalletClient.GesturePendingIntentCallback callback =
+                    (QuickAccessWalletClient.GesturePendingIntentCallback) invocation
+                            .getArguments()[1];
+            callback.onGesturePendingIntentRetrieved(null);
+            return null;
+        }).when(mQuickAccessWalletClient).getGestureTargetActivityPendingIntent(any(), any());
 
-        mController = new QuickAccessWalletController(
+        mController = spy(new QuickAccessWalletController(
                 mContext,
                 MoreExecutors.directExecutor(),
                 MoreExecutors.directExecutor(),
                 mSecureSettings,
                 mQuickAccessWalletClient,
                 mClock,
-                mRoleManager);
+                mRoleManager));
     }
 
     @Test
@@ -287,4 +300,41 @@
                 intent.getComponent().getClassName(),
                 "com.google.android.apps.testapp.TestActivity");
     }
+
+    @Test
+    @EnableFlags(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
+    public void getGestureUiIntent_targetActivityViaPendingIntent_intentComponentIsCorrect() {
+        doAnswer(invocation -> {
+            QuickAccessWalletClient.GesturePendingIntentCallback callback =
+                    (QuickAccessWalletClient.GesturePendingIntentCallback) invocation
+                            .getArguments()[1];
+            Intent intent = new Intent(Intent.ACTION_VIEW).setClassName(
+                    "com.google.android.apps.testapp",
+                    "com.google.android.apps.testapp.GestureTestActivity");
+            callback.onGesturePendingIntentRetrieved(
+                    PendingIntent.getActivity(mContext, 0, intent,
+                            PendingIntent.FLAG_IMMUTABLE));
+            return null;
+        }).when(mQuickAccessWalletClient).getGestureTargetActivityPendingIntent(any(), any());
+        mController.startGestureUiIntent(mActivityStarter, mAnimationController);
+        verify(mActivityStarter).startPendingIntentMaybeDismissingKeyguard(
+                mPendingIntentCaptor.capture(),
+                nullable(Runnable.class),
+                nullable(ActivityTransitionAnimator.Controller.class));
+        PendingIntent pendingIntent = mPendingIntentCaptor.getValue();
+        Intent intent = pendingIntent.getIntent();
+        assertEquals(intent.getAction(), Intent.ACTION_VIEW);
+        assertEquals(
+                intent.getComponent().getClassName(),
+                "com.google.android.apps.testapp.GestureTestActivity");
+    }
+
+    @Test
+    @EnableFlags(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
+    public void getGestureUiIntent_noPendingIntent_startsQuickAccessUiIntent() {
+        mController.startGestureUiIntent(mActivityStarter, mAnimationController);
+
+        verify(mController).startQuickAccessUiIntent(eq(mActivityStarter), eq(mAnimationController),
+                anyBoolean());
+    }
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestableContext.java b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestableContext.java
index b8be6aa..64d89c5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestableContext.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestableContext.java
@@ -81,6 +81,14 @@
         return super.getDisplay();
     }
 
+    @Override
+    public int getDisplayId() {
+        if (mCustomDisplay != null) {
+            return mCustomDisplay.getDisplayId();
+        }
+        return super.getDisplayId();
+    }
+
     public SysuiTestableContext createDefaultDisplayContext() {
         Display display = getBaseContext().getSystemService(DisplayManager.class).getDisplays()[0];
         return (SysuiTestableContext) createDisplayContext(display);
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeDialogTransitionAnimator.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeDialogTransitionAnimator.kt
index 1709329..2a1877a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeDialogTransitionAnimator.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/FakeDialogTransitionAnimator.kt
@@ -24,7 +24,6 @@
     @Main mainExecutor: Executor,
     isUnlocked: Boolean = true,
     isShowingAlternateAuthOnUnlock: Boolean = false,
-    isPredictiveBackQsDialogAnim: Boolean = false,
     interactionJankMonitor: InteractionJankMonitor,
 ): DialogTransitionAnimator {
     return DialogTransitionAnimator(
@@ -35,10 +34,6 @@
                 isShowingAlternateAuthOnUnlock = isShowingAlternateAuthOnUnlock,
             ),
         interactionJankMonitor = interactionJankMonitor,
-        featureFlags =
-            object : AnimationFeatureFlags {
-                override val isPredictiveBackQsDialogAnim = isPredictiveBackQsDialogAnim
-            },
         transitionAnimator = fakeTransitionAnimator(mainExecutor),
         isForTesting = true,
     )
@@ -50,6 +45,8 @@
     private val isShowingAlternateAuthOnUnlock: Boolean = false,
 ) : DialogTransitionAnimator.Callback {
     override fun isDreaming(): Boolean = isDreaming
+
     override fun isUnlocked(): Boolean = isUnlocked
+
     override fun isShowingAlternateAuthOnUnlock() = isShowingAlternateAuthOnUnlock
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/compose/Snapshot.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/compose/Snapshot.kt
new file mode 100644
index 0000000..fb6699c
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/compose/Snapshot.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.compose
+
+import androidx.compose.runtime.snapshots.Snapshot
+import com.android.systemui.kosmos.runCurrent
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+
+/**
+ * Runs the given test [block] in a [TestScope] that's set up such that the Compose snapshot state
+ * is settled eagerly. This is the Compose equivalent to using an [UnconfinedTestDispatcher] or
+ * using [runCurrent] a lot.
+ *
+ * Note that this shouldn't be needed or used in a Compose test environment.
+ */
+fun TestScope.runTestWithSnapshots(block: suspend TestScope.() -> Unit) {
+    val handle = Snapshot.registerGlobalWriteObserver { Snapshot.sendApplyNotifications() }
+
+    try {
+        runTest { block() }
+    } finally {
+        handle.dispose()
+    }
+}
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 dad8569..9815da9 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
@@ -19,7 +19,6 @@
 import android.platform.test.annotations.EnableFlags
 import com.android.systemui.Flags.FLAG_KEYGUARD_WM_STATE_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
 
 /**
@@ -29,7 +28,6 @@
 @EnableFlags(
     FLAG_KEYGUARD_WM_STATE_REFACTOR,
     FLAG_NOTIFICATION_AVALANCHE_THROTTLE_HUN,
-    FLAG_PREDICTIVE_BACK_SYSUI,
     FLAG_SCENE_CONTAINER,
 )
 @Retention(AnnotationRetention.RUNTIME)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
index 2641070..3df3ee9 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/KeyboardShortcutHelperKosmos.kt
@@ -53,6 +53,7 @@
 import com.android.systemui.kosmos.testDispatcher
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.model.sysUiState
+import com.android.systemui.plugins.activityStarter
 import com.android.systemui.settings.displayTracker
 import com.android.systemui.settings.userTracker
 import com.android.systemui.statusbar.phone.systemUIDialogFactory
@@ -67,15 +68,7 @@
     Kosmos.Fixture { MultitaskingShortcutsSource(mainResources, applicationContext) }
 
 val Kosmos.shortcutHelperStateRepository by
-    Kosmos.Fixture {
-        ShortcutHelperStateRepository(
-            fakeCommandQueue,
-            broadcastDispatcher,
-            fakeInputManager.inputManager,
-            testScope,
-            testDispatcher,
-        )
-    }
+    Kosmos.Fixture { ShortcutHelperStateRepository(fakeInputManager.inputManager, testDispatcher) }
 
 var Kosmos.shortcutHelperInputShortcutsSource: KeyboardShortcutGroupsSource by
     Kosmos.Fixture {
@@ -153,14 +146,27 @@
         )
     }
 
+val Kosmos.shortcutHelperCoreStartable by
+    Kosmos.Fixture {
+        ShortcutHelperCoreStartable(
+            fakeCommandQueue,
+            broadcastDispatcher,
+            shortcutHelperStateRepository,
+            activityStarter,
+            testScope,
+            customInputGesturesRepository
+        )
+    }
+
 val Kosmos.shortcutHelperTestHelper by
     Kosmos.Fixture {
         ShortcutHelperTestHelper(
-            shortcutHelperStateRepository,
+            shortcutHelperCoreStartable,
             applicationContext,
             broadcastDispatcher,
             fakeCommandQueue,
             fakeInputManager,
+            activityStarter,
             windowManager,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperTestHelper.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperTestHelper.kt
index 8b45662..e5f2449a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperTestHelper.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyboard/shortcut/data/repository/ShortcutHelperTestHelper.kt
@@ -23,23 +23,22 @@
 import android.view.WindowManager
 import android.view.WindowManager.KeyboardShortcutsReceiver
 import com.android.systemui.broadcast.FakeBroadcastDispatcher
+import com.android.systemui.keyboard.shortcut.ShortcutHelperCoreStartable
 import com.android.systemui.keyguard.data.repository.FakeCommandQueue
+import com.android.systemui.plugins.ActivityStarter
 import com.android.systemui.util.mockito.any
 import com.android.systemui.util.mockito.whenever
+import org.mockito.kotlin.eq
 
 class ShortcutHelperTestHelper(
-    repo: ShortcutHelperStateRepository,
+    coreStartable: ShortcutHelperCoreStartable,
     private val context: Context,
     private val fakeBroadcastDispatcher: FakeBroadcastDispatcher,
     private val fakeCommandQueue: FakeCommandQueue,
     private val fakeInputManager: FakeInputManager,
-    windowManager: WindowManager
+    private val activityStarter: ActivityStarter,
+    windowManager: WindowManager,
 ) {
-
-    companion object {
-        const val DEFAULT_DEVICE_ID = 123
-    }
-
     private var imeShortcuts: List<KeyboardShortcutGroup> = emptyList()
     private var currentAppsShortcuts: List<KeyboardShortcutGroup> = emptyList()
 
@@ -47,14 +46,15 @@
         whenever(windowManager.requestImeKeyboardShortcuts(any(), any())).thenAnswer {
             val keyboardShortcutReceiver = it.getArgument<KeyboardShortcutsReceiver>(0)
             keyboardShortcutReceiver.onKeyboardShortcutsReceived(imeShortcuts)
-            return@thenAnswer Unit
         }
         whenever(windowManager.requestAppKeyboardShortcuts(any(), any())).thenAnswer {
             val keyboardShortcutReceiver = it.getArgument<KeyboardShortcutsReceiver>(0)
             keyboardShortcutReceiver.onKeyboardShortcutsReceived(currentAppsShortcuts)
-            return@thenAnswer Unit
         }
-        repo.start()
+        whenever(activityStarter.dismissKeyguardThenExecute(any(), any(), eq(true))).then {
+            (it.arguments[0] as ActivityStarter.OnDismissAction).onDismiss()
+        }
+        coreStartable.start()
     }
 
     /**
@@ -76,21 +76,21 @@
     fun hideThroughCloseSystemDialogs() {
         fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
             context,
-            Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)
+            Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS),
         )
     }
 
     fun hideFromActivity() {
         fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
             context,
-            Intent(Intent.ACTION_DISMISS_KEYBOARD_SHORTCUTS)
+            Intent(Intent.ACTION_DISMISS_KEYBOARD_SHORTCUTS),
         )
     }
 
     fun showFromActivity() {
         fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly(
             context,
-            Intent(Intent.ACTION_SHOW_KEYBOARD_SHORTCUTS)
+            Intent(Intent.ACTION_SHOW_KEYBOARD_SHORTCUTS),
         )
     }
 
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 1288d31..8489d83 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
@@ -46,9 +46,6 @@
         MutableSharedFlow(extraBufferCapacity = 1)
     override val keyguardDoneAnimationsFinished: Flow<Unit> = _keyguardDoneAnimationsFinished
 
-    private val _clockShouldBeCentered = MutableStateFlow<Boolean>(true)
-    override val clockShouldBeCentered: Flow<Boolean> = _clockShouldBeCentered
-
     private val _dismissAction = MutableStateFlow<DismissAction>(DismissAction.None)
     override val dismissAction: StateFlow<DismissAction> = _dismissAction
 
@@ -192,10 +189,6 @@
         _keyguardDoneAnimationsFinished.tryEmit(Unit)
     }
 
-    override fun setClockShouldBeCentered(shouldBeCentered: Boolean) {
-        _clockShouldBeCentered.value = shouldBeCentered
-    }
-
     override fun setKeyguardEnabled(enabled: Boolean) {
         _isKeyguardEnabled.value = enabled
     }
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 8209ee1..f479100 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
@@ -402,6 +402,12 @@
             )
         )
     }
+
+    suspend fun transitionTo(from: KeyguardState, to: KeyguardState) {
+        sendTransitionStep(TransitionStep(from, to, 0f, TransitionState.STARTED))
+        sendTransitionStep(TransitionStep(from, to, 0.5f, TransitionState.RUNNING))
+        sendTransitionStep(TransitionStep(from, to, 1f, TransitionState.FINISHED))
+    }
 }
 
 @Module
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 3de8093..ee21bdc 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
@@ -24,8 +24,6 @@
 import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
 import com.android.systemui.keyguard.shared.model.KeyguardState
 import com.android.systemui.keyguard.shared.model.TransitionStep
-import com.android.systemui.power.domain.interactor.PowerInteractor
-import com.android.systemui.power.domain.interactor.PowerInteractorFactory
 import com.android.systemui.scene.domain.interactor.SceneInteractor
 import com.android.systemui.shade.data.repository.FakeShadeRepository
 import com.android.systemui.util.mockito.mock
@@ -55,7 +53,6 @@
         fromLockscreenTransitionInteractor: FromLockscreenTransitionInteractor = mock(),
         fromOccludedTransitionInteractor: FromOccludedTransitionInteractor = mock(),
         fromAlternateBouncerTransitionInteractor: FromAlternateBouncerTransitionInteractor = mock(),
-        powerInteractor: PowerInteractor = PowerInteractorFactory.create().powerInteractor,
         testScope: CoroutineScope = TestScope(),
     ): WithDependencies {
         // Mock these until they are replaced by kosmos
@@ -73,10 +70,8 @@
             bouncerRepository = bouncerRepository,
             configurationRepository = configurationRepository,
             shadeRepository = shadeRepository,
-            powerInteractor = powerInteractor,
             KeyguardInteractor(
                 repository = repository,
-                powerInteractor = powerInteractor,
                 bouncerRepository = bouncerRepository,
                 configurationInteractor = ConfigurationInteractorImpl(configurationRepository),
                 shadeRepository = shadeRepository,
@@ -99,7 +94,6 @@
         val bouncerRepository: FakeKeyguardBouncerRepository,
         val configurationRepository: FakeConfigurationRepository,
         val shadeRepository: FakeShadeRepository,
-        val powerInteractor: PowerInteractor,
         val keyguardInteractor: KeyguardInteractor,
     )
 }
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 f5f8ef7..869bae2 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
@@ -21,7 +21,6 @@
 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.shade.data.repository.shadeRepository
 
@@ -29,7 +28,6 @@
     Kosmos.Fixture {
         KeyguardInteractor(
             repository = keyguardRepository,
-            powerInteractor = powerInteractor,
             bouncerRepository = keyguardBouncerRepository,
             configurationInteractor = configurationInteractor,
             shadeRepository = shadeRepository,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/transitions/FakeBouncerTransition.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/transitions/FakeBouncerTransition.kt
index 15d00d9..edc1cce 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/transitions/FakeBouncerTransition.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/transitions/FakeBouncerTransition.kt
@@ -20,4 +20,5 @@
 
 class FakeBouncerTransition : PrimaryBouncerTransition {
     override val windowBlurRadius: MutableStateFlow<Float> = MutableStateFlow(0.0f)
+    override val notificationBlurRadius: MutableStateFlow<Float> = MutableStateFlow(0.0f)
 }
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 4383560..afe4821 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/GeneralKosmos.kt
@@ -1,6 +1,7 @@
 package com.android.systemui.kosmos
 
 import com.android.systemui.SysuiTestCase
+import com.android.systemui.compose.runTestWithSnapshots
 import com.android.systemui.coroutines.FlowValue
 import com.android.systemui.coroutines.collectLastValue
 import com.android.systemui.coroutines.collectValues
@@ -16,7 +17,6 @@
 import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.UnconfinedTestDispatcher
 import kotlinx.coroutines.test.runCurrent
-import kotlinx.coroutines.test.runTest
 import org.mockito.kotlin.verify
 
 var Kosmos.testDispatcher by Fixture { StandardTestDispatcher() }
@@ -52,10 +52,11 @@
 
 /**
  * Run this test body with a [Kosmos] as receiver, and using the [testScope] currently installed in
- * that kosmos instance
+ * that Kosmos instance
  */
-fun Kosmos.runTest(testBody: suspend Kosmos.() -> Unit) =
-    testScope.runTest testBody@{ this@runTest.testBody() }
+fun Kosmos.runTest(testBody: suspend Kosmos.() -> Unit) {
+    testScope.runTestWithSnapshots testBody@{ this@runTest.testBody() }
+}
 
 fun Kosmos.runCurrent() = testScope.runCurrent()
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/ui/view/SceneJankMonitorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/ui/view/SceneJankMonitorKosmos.kt
new file mode 100644
index 0000000..bcba5ee
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/ui/view/SceneJankMonitorKosmos.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF 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.view
+
+import com.android.systemui.authentication.domain.interactor.authenticationInteractor
+import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
+import com.android.systemui.jank.interactionJankMonitor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+
+val Kosmos.sceneJankMonitorFactory: SceneJankMonitor.Factory by Fixture {
+    object : SceneJankMonitor.Factory {
+        override fun create(): SceneJankMonitor {
+            return SceneJankMonitor(
+                authenticationInteractor = authenticationInteractor,
+                deviceUnlockedInteractor = deviceUnlockedInteractor,
+                interactionJankMonitor = interactionJankMonitor,
+            )
+        }
+    }
+}
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 4a86fd5..74deaab 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
@@ -21,6 +21,8 @@
 import dagger.Binds
 import dagger.Module
 import javax.inject.Inject
+import kotlinx.coroutines.channels.BufferOverflow
+import kotlinx.coroutines.flow.MutableSharedFlow
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.asStateFlow
@@ -37,8 +39,8 @@
     override val udfpsTransitionToFullShadeProgress =
         _udfpsTransitionToFullShadeProgress.asStateFlow()
 
-    private val _currentFling: MutableStateFlow<FlingInfo?> = MutableStateFlow(null)
-    override val currentFling: StateFlow<FlingInfo?> = _currentFling.asStateFlow()
+    override val currentFling: MutableSharedFlow<FlingInfo?> =
+        MutableSharedFlow(replay = 2, onBufferOverflow = BufferOverflow.DROP_OLDEST)
 
     private val _lockscreenShadeExpansion = MutableStateFlow(0f)
     override val lockscreenShadeExpansion = _lockscreenShadeExpansion.asStateFlow()
@@ -139,7 +141,7 @@
     }
 
     override fun setCurrentFling(info: FlingInfo?) {
-        _currentFling.value = info
+        currentFling.tryEmit(info)
     }
 
     override fun setLockscreenShadeExpansion(lockscreenShadeExpansion: Float) {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModelKosmos.kt
deleted file mode 100644
index 2316a2f..0000000
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/demo/ui/viewmodel/DemoNotifChipViewModelKosmos.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.chips.notification.demo.ui.viewmodel
-
-import android.content.packageManager
-import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.statusbar.commandline.commandRegistry
-import com.android.systemui.util.time.fakeSystemClock
-
-val Kosmos.demoNotifChipViewModel: DemoNotifChipViewModel by
-    Kosmos.Fixture {
-        DemoNotifChipViewModel(
-            commandRegistry = commandRegistry,
-            packageManager = packageManager,
-            systemClock = fakeSystemClock,
-        )
-    }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractorFactoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractorFactoryKosmos.kt
index 1c095e1..b36b463 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractorFactoryKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractorFactoryKosmos.kt
@@ -23,9 +23,10 @@
 
 val Kosmos.singleNotificationChipInteractorFactory: SingleNotificationChipInteractor.Factory by
     Kosmos.Fixture {
-        SingleNotificationChipInteractor.Factory { startingModel ->
+        SingleNotificationChipInteractor.Factory { startingModel, creationTime ->
             SingleNotificationChipInteractor(
                 startingModel,
+                creationTime,
                 activityManagerRepository.fake,
                 logBuffer = statusBarChipsLogger,
             )
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorKosmos.kt
index 03e9f3d..234efcb 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorKosmos.kt
@@ -20,11 +20,13 @@
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.statusbar.chips.statusBarChipsLogger
 import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
+import com.android.systemui.util.time.fakeSystemClock
 
 val Kosmos.statusBarNotificationChipsInteractor: StatusBarNotificationChipsInteractor by
     Kosmos.Fixture {
         StatusBarNotificationChipsInteractor(
             testScope.backgroundScope,
+            fakeSystemClock,
             activeNotificationsInteractor,
             singleNotificationChipInteractorFactory,
             logBuffer = statusBarChipsLogger,
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 0300bf4..ee34aa6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt
@@ -20,7 +20,6 @@
 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.notification.demo.ui.viewmodel.demoNotifChipViewModel
 import com.android.systemui.statusbar.chips.notification.ui.viewmodel.notifChipsViewModel
 import com.android.systemui.statusbar.chips.screenrecord.ui.viewmodel.screenRecordChipViewModel
 import com.android.systemui.statusbar.chips.sharetoapp.ui.viewmodel.shareToAppChipViewModel
@@ -35,7 +34,6 @@
             castToOtherDeviceChipViewModel = castToOtherDeviceChipViewModel,
             callChipViewModel = callChipViewModel,
             notifChipsViewModel = notifChipsViewModel,
-            demoNotifChipViewModel = demoNotifChipViewModel,
             logger = statusBarChipsLogger,
         )
     }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerFactory.kt
index 50a19a9..fb2e2a3 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/FakeStatusBarInitializerFactory.kt
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar.core
 
+import com.android.systemui.plugins.DarkIconDispatcher
+import com.android.systemui.statusbar.data.repository.StatusBarConfigurationController
 import com.android.systemui.statusbar.data.repository.StatusBarModePerDisplayRepository
 import com.android.systemui.statusbar.window.StatusBarWindowController
 
@@ -24,5 +26,7 @@
     override fun create(
         statusBarWindowController: StatusBarWindowController,
         statusBarModePerDisplayRepository: StatusBarModePerDisplayRepository,
+        statusBarConfigurationController: StatusBarConfigurationController,
+        darkIconDispatcher: DarkIconDispatcher,
     ): StatusBarInitializer = FakeStatusBarInitializer()
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarInitializerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarInitializerKosmos.kt
index 6e99027..b8dafb2 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarInitializerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/core/StatusBarInitializerKosmos.kt
@@ -19,7 +19,9 @@
 import com.android.systemui.display.data.repository.displayRepository
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.statusbar.data.repository.darkIconDispatcherStore
 import com.android.systemui.statusbar.data.repository.fakeStatusBarModeRepository
+import com.android.systemui.statusbar.data.repository.statusBarConfigurationControllerStore
 import com.android.systemui.statusbar.window.fakeStatusBarWindowControllerStore
 
 val Kosmos.fakeStatusBarInitializer by Kosmos.Fixture { FakeStatusBarInitializer() }
@@ -39,6 +41,8 @@
             fakeStatusBarInitializerFactory,
             fakeStatusBarWindowControllerStore,
             fakeStatusBarModeRepository,
+            statusBarConfigurationControllerStore,
+            darkIconDispatcherStore,
         )
     }
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarContentInsetsProviderStore.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarContentInsetsProviderStore.kt
index 642c2ff..67f8572 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarContentInsetsProviderStore.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/FakeStatusBarContentInsetsProviderStore.kt
@@ -17,7 +17,7 @@
 package com.android.systemui.statusbar.data.repository
 
 import android.view.Display
-import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
+import com.android.systemui.statusbar.layout.StatusBarContentInsetsProvider
 import org.mockito.kotlin.mock
 
 class FakeStatusBarContentInsetsProviderStore() : StatusBarContentInsetsProviderStore {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/StatusBarContentInsetsProviderStoreKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/StatusBarContentInsetsProviderStoreKosmos.kt
index a34fb09..af7a463 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/StatusBarContentInsetsProviderStoreKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/data/repository/StatusBarContentInsetsProviderStoreKosmos.kt
@@ -21,7 +21,7 @@
 import com.android.systemui.display.data.repository.displayWindowPropertiesRepository
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
-import com.android.systemui.statusbar.phone.statusBarContentInsetsProviderFactory
+import com.android.systemui.statusbar.layout.statusBarContentInsetsProviderFactory
 import com.android.systemui.sysUICutoutProviderFactory
 
 val Kosmos.fakeStatusBarContentInsetsProviderStore by
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/FakeStatusBarContentInsetsProviderFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/layout/FakeStatusBarContentInsetsProviderFactory.kt
similarity index 95%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/FakeStatusBarContentInsetsProviderFactory.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/layout/FakeStatusBarContentInsetsProviderFactory.kt
index 4fb8cf4..ad742c8 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/FakeStatusBarContentInsetsProviderFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/layout/FakeStatusBarContentInsetsProviderFactory.kt
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.statusbar.phone
+package com.android.systemui.statusbar.layout
 
 import android.content.Context
 import com.android.systemui.SysUICutoutProvider
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/layout/StatusBarContentInsetsProviderKosmos.kt
similarity index 91%
rename from packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderKosmos.kt
rename to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/layout/StatusBarContentInsetsProviderKosmos.kt
index 705df3c..69e215d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/layout/StatusBarContentInsetsProviderKosmos.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2024 The Android Open Source Project
+ * Copyright (C) 2025 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.systemui.statusbar.phone
+package com.android.systemui.statusbar.layout
 
 import com.android.systemui.kosmos.Kosmos
 import org.mockito.kotlin.mock
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/headsup/AvalancheControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/headsup/AvalancheControllerKosmos.kt
new file mode 100644
index 0000000..2a2f5f9
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/headsup/AvalancheControllerKosmos.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.notification.headsup
+
+import com.android.internal.logging.uiEventLoggerFake
+import com.android.systemui.dump.dumpManager
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.util.mockito.mock
+
+var Kosmos.mockAvalancheController by Fixture { mock<AvalancheController>() }
+
+val Kosmos.avalancheController by Fixture {
+    AvalancheController(dumpManager, uiEventLoggerFake, headsUpManagerLogger, bgHandler = mock())
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerKosmos.kt
index de9485d..dfc8a68 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerKosmos.kt
@@ -16,8 +16,44 @@
 
 package com.android.systemui.statusbar.notification.headsup
 
+import android.content.applicationContext
+import android.view.accessibility.accessibilityManagerWrapper
+import com.android.internal.logging.uiEventLoggerFake
+import com.android.systemui.concurrency.fakeExecutor
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.plugins.statusbar.statusBarStateController
+import com.android.systemui.shade.domain.interactor.shadeInteractor
+import com.android.systemui.statusbar.notification.collection.provider.visualStabilityProvider
+import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager
+import com.android.systemui.statusbar.phone.keyguardBypassController
+import com.android.systemui.statusbar.policy.configurationController
+import com.android.systemui.util.concurrency.mockExecutorHandler
+import com.android.systemui.util.kotlin.JavaAdapter
 import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.settings.fakeGlobalSettings
+import com.android.systemui.util.time.fakeSystemClock
 
-var Kosmos.headsUpManager by Fixture { mock<HeadsUpManager>() }
+var Kosmos.mockHeadsUpManager by Fixture { mock<HeadsUpManager>() }
+
+var Kosmos.headsUpManager: HeadsUpManager by Fixture {
+    HeadsUpManagerImpl(
+        applicationContext,
+        headsUpManagerLogger,
+        statusBarStateController,
+        keyguardBypassController,
+        mock<GroupMembershipManager>(),
+        visualStabilityProvider,
+        configurationController,
+        mockExecutorHandler(fakeExecutor),
+        fakeGlobalSettings,
+        fakeSystemClock,
+        fakeExecutor,
+        accessibilityManagerWrapper,
+        uiEventLoggerFake,
+        JavaAdapter(testScope.backgroundScope),
+        shadeInteractor,
+        avalancheController,
+    )
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerLoggerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerLoggerKosmos.kt
new file mode 100644
index 0000000..d595fa6
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/headsup/HeadsUpManagerLoggerKosmos.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.headsup
+
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.log.logcatLogBuffer
+import org.mockito.kotlin.mock
+
+val Kosmos.mockHeadsUpManagerLogger by Fixture { mock<HeadsUpManagerLogger>() }
+
+val Kosmos.headsUpManagerLogger by Fixture {
+    HeadsUpManagerLogger(logcatLogBuffer("HeadsUpManagerLogger"))
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/OnUserInteractionCallbackKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/OnUserInteractionCallbackKosmos.kt
index ec54c33..cc3f21b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/OnUserInteractionCallbackKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/OnUserInteractionCallbackKosmos.kt
@@ -22,14 +22,14 @@
 import com.android.systemui.statusbar.notification.collection.inflation.OnUserInteractionCallbackImpl
 import com.android.systemui.statusbar.notification.collection.notifCollection
 import com.android.systemui.statusbar.notification.collection.render.notificationVisibilityProvider
-import com.android.systemui.statusbar.notification.headsup.headsUpManager
+import com.android.systemui.statusbar.notification.headsup.mockHeadsUpManager
 
 var Kosmos.onUserInteractionCallback: OnUserInteractionCallback by
     Kosmos.Fixture {
         OnUserInteractionCallbackImpl(
             notificationVisibilityProvider,
             notifCollection,
-            headsUpManager,
+            mockHeadsUpManager,
             statusBarStateController,
             visualStabilityCoordinator,
         )
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/AmbientStateKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/AmbientStateKosmos.kt
index 383e31d..65f4ec1 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/AmbientStateKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/AmbientStateKosmos.kt
@@ -21,6 +21,7 @@
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
 import com.android.systemui.shade.transition.largeScreenShadeInterpolator
+import com.android.systemui.statusbar.notification.headsup.mockAvalancheController
 import com.android.systemui.statusbar.phone.statusBarKeyguardViewManager
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 
@@ -33,6 +34,6 @@
         /*bypassController=*/ stackScrollAlgorithmBypassController,
         /*statusBarKeyguardViewManager=*/ statusBarKeyguardViewManager,
         /*largeScreenShadeInterpolator=*/ largeScreenShadeInterpolator,
-        /*avalancheController=*/ avalancheController,
+        /*avalancheController=*/ mockAvalancheController,
     )
 }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmKosmos.kt
index a5c4bfd..67343c95 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmKosmos.kt
@@ -18,7 +18,6 @@
 
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.Kosmos.Fixture
-import com.android.systemui.statusbar.notification.headsup.AvalancheController
 import com.android.systemui.util.mockito.mock
 
 var Kosmos.stackScrollAlgorithmSectionProvider by Fixture {
@@ -28,5 +27,3 @@
 var Kosmos.stackScrollAlgorithmBypassController by Fixture {
     mock<StackScrollAlgorithm.BypassController>()
 }
-
-var Kosmos.avalancheController by Fixture { mock<AvalancheController>() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt
index d1619b7..60e092c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt
@@ -57,6 +57,7 @@
 import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationStackAppearanceInteractor
 import com.android.systemui.statusbar.notification.stack.domain.interactor.sharedNotificationContainerInteractor
 import com.android.systemui.unfold.domain.interactor.unfoldTransitionInteractor
+import com.android.systemui.window.ui.viewmodel.fakeBouncerTransitions
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 
 @OptIn(ExperimentalCoroutinesApi::class)
@@ -99,6 +100,7 @@
         primaryBouncerToGoneTransitionViewModel = primaryBouncerToGoneTransitionViewModel,
         primaryBouncerToLockscreenTransitionViewModel =
             primaryBouncerToLockscreenTransitionViewModel,
+        primaryBouncerTransitions = fakeBouncerTransitions,
         aodBurnInViewModel = aodBurnInViewModel,
         communalSceneInteractor = communalSceneInteractor,
         headsUpNotificationInteractor = { headsUpNotificationInteractor },
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/WindowRootViewVisibilityInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/WindowRootViewVisibilityInteractorKosmos.kt
index e972c2c..64f16da 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/WindowRootViewVisibilityInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/WindowRootViewVisibilityInteractorKosmos.kt
@@ -25,14 +25,14 @@
 import com.android.systemui.scene.domain.interactor.WindowRootViewVisibilityInteractor
 import com.android.systemui.scene.domain.interactor.sceneInteractor
 import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor
-import com.android.systemui.statusbar.notification.headsup.headsUpManager
+import com.android.systemui.statusbar.notification.headsup.mockHeadsUpManager
 
 val Kosmos.windowRootViewVisibilityInteractor by Fixture {
     WindowRootViewVisibilityInteractor(
         scope = applicationCoroutineScope,
         windowRootViewVisibilityRepository = windowRootViewVisibilityRepository,
         keyguardRepository = keyguardRepository,
-        headsUpManager = headsUpManager,
+        headsUpManager = mockHeadsUpManager,
         powerInteractor = powerInteractor,
         activeNotificationsInteractor = activeNotificationsInteractor,
     ) {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/DozeServiceHostKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/DozeServiceHostKosmos.kt
index f86824a..d0bf584 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/DozeServiceHostKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/DozeServiceHostKosmos.kt
@@ -26,7 +26,7 @@
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.plugins.statusbar.statusBarStateController
 import com.android.systemui.shade.domain.interactor.shadeLockscreenInteractor
-import com.android.systemui.statusbar.notification.headsup.headsUpManager
+import com.android.systemui.statusbar.notification.headsup.mockHeadsUpManager
 import com.android.systemui.statusbar.notificationShadeWindowController
 import com.android.systemui.statusbar.policy.batteryController
 import com.android.systemui.statusbar.policy.deviceProvisionedController
@@ -42,7 +42,7 @@
             wakefulnessLifecycle,
             statusBarStateController,
             deviceProvisionedController,
-            headsUpManager,
+            mockHeadsUpManager,
             batteryController,
             scrimController,
             { biometricUnlockController },
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ShadeTouchableRegionManagerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ShadeTouchableRegionManagerKosmos.kt
index 5b7f23b..9adaeff9 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ShadeTouchableRegionManagerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ShadeTouchableRegionManagerKosmos.kt
@@ -24,7 +24,7 @@
 import com.android.systemui.kosmos.testScope
 import com.android.systemui.scene.domain.interactor.sceneInteractor
 import com.android.systemui.shade.domain.interactor.shadeInteractor
-import com.android.systemui.statusbar.notification.headsup.headsUpManager
+import com.android.systemui.statusbar.notification.headsup.mockHeadsUpManager
 import com.android.systemui.statusbar.notificationShadeWindowController
 import com.android.systemui.statusbar.policy.configurationController
 import com.android.systemui.util.kotlin.JavaAdapter
@@ -36,7 +36,7 @@
             applicationContext,
             notificationShadeWindowController,
             configurationController,
-            headsUpManager,
+            mockHeadsUpManager,
             shadeInteractor,
             { sceneInteractor },
             JavaAdapter(testScope.backgroundScope),
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterKosmos.kt
index 6083414..7743a1c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterKosmos.kt
@@ -36,7 +36,7 @@
 import com.android.systemui.statusbar.commandQueue
 import com.android.systemui.statusbar.notification.collection.provider.launchFullScreenIntentProvider
 import com.android.systemui.statusbar.notification.collection.render.notificationVisibilityProvider
-import com.android.systemui.statusbar.notification.headsup.headsUpManager
+import com.android.systemui.statusbar.notification.headsup.mockHeadsUpManager
 import com.android.systemui.statusbar.notification.notificationTransitionAnimatorControllerProvider
 import com.android.systemui.statusbar.notification.row.onUserInteractionCallback
 import com.android.systemui.statusbar.notificationClickNotifier
@@ -58,7 +58,7 @@
             fakeExecutorHandler,
             fakeExecutor,
             notificationVisibilityProvider,
-            headsUpManager,
+            mockHeadsUpManager,
             activityStarter,
             commandQueue,
             notificationClickNotifier,
diff --git a/media/java/android/media/quality/ParamCapability.aidl b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/ethernet/domain/EthernetInteractorKosmos.kt
similarity index 64%
copy from media/java/android/media/quality/ParamCapability.aidl
copy to packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/ethernet/domain/EthernetInteractorKosmos.kt
index b43409d..3be6818 100644
--- a/media/java/android/media/quality/ParamCapability.aidl
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/ethernet/domain/EthernetInteractorKosmos.kt
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
-package android.media.quality;
+package com.android.systemui.statusbar.pipeline.ethernet.domain
 
-parcelable ParamCapability;
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.kosmos.Kosmos.Fixture
+import com.android.systemui.statusbar.pipeline.shared.data.repository.connectivityRepository
+
+val Kosmos.ethernetInteractor by Fixture { EthernetInteractor(connectivityRepository) }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelKosmos.kt
index b38a723..5db0d5a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/HomeStatusBarViewModelKosmos.kt
@@ -16,6 +16,7 @@
 
 package com.android.systemui.statusbar.pipeline.shared.ui.viewmodel
 
+import android.content.testableContext
 import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
 import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
 import com.android.systemui.kosmos.Kosmos
@@ -36,6 +37,7 @@
 var Kosmos.homeStatusBarViewModel: HomeStatusBarViewModel by
     Kosmos.Fixture {
         HomeStatusBarViewModelImpl(
+            testableContext.displayId,
             homeStatusBarInteractor,
             homeStatusBarIconBlockListInteractor,
             lightsOutInteractor,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowControllerFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowControllerFactory.kt
index 7eaecb1..3a19547 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowControllerFactory.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/FakeStatusBarWindowControllerFactory.kt
@@ -19,7 +19,7 @@
 import android.content.Context
 import com.android.app.viewcapture.ViewCaptureAwareWindowManager
 import com.android.systemui.statusbar.data.repository.StatusBarConfigurationController
-import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
+import com.android.systemui.statusbar.layout.StatusBarContentInsetsProvider
 
 class FakeStatusBarWindowControllerFactory : StatusBarWindowController.Factory {
     override fun create(
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/StatusBarWindowControllerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/StatusBarWindowControllerKosmos.kt
index 23f2b42..f595aef 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/StatusBarWindowControllerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/StatusBarWindowControllerKosmos.kt
@@ -22,7 +22,7 @@
 import com.android.systemui.concurrency.fakeExecutor
 import com.android.systemui.fragments.fragmentService
 import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.statusbar.phone.statusBarContentInsetsProvider
+import com.android.systemui.statusbar.layout.statusBarContentInsetsProvider
 import com.android.systemui.statusbar.policy.statusBarConfigurationController
 import java.util.Optional
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/data/repository/FakeStatusBarWindowStatePerDisplayRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/data/repository/FakeStatusBarWindowStatePerDisplayRepository.kt
index 6532a7e..b240132 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/data/repository/FakeStatusBarWindowStatePerDisplayRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/window/data/repository/FakeStatusBarWindowStatePerDisplayRepository.kt
@@ -17,7 +17,7 @@
 package com.android.systemui.statusbar.window.data.repository
 
 import android.view.Display
-import com.android.systemui.statusbar.window.data.model.StatusBarWindowState
+import com.android.systemui.statusbar.window.shared.model.StatusBarWindowState
 import kotlinx.coroutines.flow.MutableStateFlow
 import kotlinx.coroutines.flow.asStateFlow
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/touchpad/ui/gesture/TouchpadGestureResourcesKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/touchpad/ui/gesture/TouchpadGestureResourcesKosmos.kt
new file mode 100644
index 0000000..d795941
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/touchpad/ui/gesture/TouchpadGestureResourcesKosmos.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.touchpad.ui.gesture
+
+import android.content.res.mockResources
+import com.android.systemui.common.ui.domain.interactor.configurationInteractor
+import com.android.systemui.kosmos.Kosmos
+import com.android.systemui.touchpad.tutorial.ui.viewmodel.TouchpadGestureResources
+
+var Kosmos.touchpadGestureResources: TouchpadGestureResources by
+    Kosmos.Fixture { TouchpadGestureResources(configurationInteractor, mockResources) }
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 0089199..8f48b1f 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
@@ -20,7 +20,6 @@
 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;
 import com.android.systemui.statusbar.phone.ui.StatusBarIconController;
 
@@ -77,14 +76,6 @@
     }
 
     @Override
-    public void setCallStrengthIcons(String slot, List<CallIndicatorIconState> states) {
-    }
-
-    @Override
-    public void setNoCallingIcons(String slot, List<CallIndicatorIconState> states) {
-    }
-
-    @Override
     public void setIconVisibility(String slotTty, boolean b) {
     }
 
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractorKosmos.kt
index 44917dd..198d72a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/volume/dialog/sliders/domain/interactor/VolumeDialogSliderInteractorKosmos.kt
@@ -19,6 +19,7 @@
 import com.android.systemui.kosmos.Kosmos
 import com.android.systemui.kosmos.applicationCoroutineScope
 import com.android.systemui.plugins.volumeDialogController
+import com.android.systemui.statusbar.policy.domain.interactor.zenModeInteractor
 import com.android.systemui.volume.dialog.domain.interactor.volumeDialogStateInteractor
 import com.android.systemui.volume.dialog.sliders.domain.model.volumeDialogSliderType
 
@@ -29,5 +30,6 @@
             applicationCoroutineScope,
             volumeDialogStateInteractor,
             volumeDialogController,
+            zenModeInteractor,
         )
     }
diff --git a/packages/SystemUI/utils/kairos/Android.bp b/packages/SystemUI/utils/kairos/Android.bp
index 1442591..e10de99 100644
--- a/packages/SystemUI/utils/kairos/Android.bp
+++ b/packages/SystemUI/utils/kairos/Android.bp
@@ -22,7 +22,7 @@
 java_library {
     name: "kairos",
     host_supported: true,
-    kotlincflags: ["-opt-in=com.android.systemui.kairos.ExperimentalFrpApi"],
+    kotlincflags: ["-opt-in=com.android.systemui.kairos.ExperimentalKairosApi"],
     srcs: ["src/**/*.kt"],
     static_libs: [
         "kotlin-stdlib",
@@ -32,6 +32,7 @@
 
 java_test {
     name: "kairos-test",
+    kotlincflags: ["-opt-in=com.android.systemui.kairos.ExperimentalKairosApi"],
     optimize: {
         enabled: false,
     },
diff --git a/packages/SystemUI/utils/kairos/README.md b/packages/SystemUI/utils/kairos/README.md
index 85f622c..5174c45 100644
--- a/packages/SystemUI/utils/kairos/README.md
+++ b/packages/SystemUI/utils/kairos/README.md
@@ -22,22 +22,21 @@
 
 ## Usage
 
-First, stand up a new `FrpNetwork`. All reactive events and state is kept
+First, stand up a new `KairosNetwork`. All reactive events and state is kept
 consistent within a single network.
 
 ``` kotlin
 val coroutineScope: CoroutineScope = ...
-val frpNetwork = coroutineScope.newFrpNetwork()
+val network = coroutineScope.launchKairosNetwork()
 ```
 
-You can use the `FrpNetwork` to stand-up a network of reactive events and state.
-Events are modeled with `TFlow` (short for "transactional flow"), and state
-`TState` (short for "transactional state").
+You can use the `KairosNetwork` to stand-up a network of reactive events and
+state. Events are modeled with `Events`, and states with `State`.
 
 ``` kotlin
-suspend fun activate(network: FrpNetwork) {
+suspend fun activate(network: KairosNetwork) {
     network.activateSpec {
-        val input = network.mutableTFlow<Unit>()
+        val input = network.mutableEvents<Unit>()
         // Launch a long-running side-effect that emits to the network
         // every second.
         launchEffect {
@@ -47,7 +46,7 @@
             }
         }
         // Accumulate state
-        val count: TState<Int> = input.fold { _, i -> i + 1 }
+        val count: State<Int> = input.foldState { _, i -> i + 1 }
         // Observe events to perform side-effects in reaction to them
         input.observe {
             println("Got event ${count.sample()} at time: ${System.currentTimeMillis()}")
@@ -56,7 +55,7 @@
 }
 ```
 
-`FrpNetwork.activateSpec` will suspend indefinitely; cancelling the invocation
+`KairosNetwork.activateSpec` will suspend indefinitely; cancelling the invocation
 will tear-down all effects and obervers running within the lambda.
 
 ## Resources
diff --git a/packages/SystemUI/utils/kairos/docs/flow-to-kairos-cheatsheet.md b/packages/SystemUI/utils/kairos/docs/flow-to-kairos-cheatsheet.md
index 9f7fd02..afe6437 100644
--- a/packages/SystemUI/utils/kairos/docs/flow-to-kairos-cheatsheet.md
+++ b/packages/SystemUI/utils/kairos/docs/flow-to-kairos-cheatsheet.md
@@ -2,117 +2,116 @@
 
 ## Key differences
 
-* Kairos evaluates all events (`TFlow` emissions + observers) in a transaction.
+* Kairos evaluates all events (`Events` emissions + observers) in a transaction.
 
-* Kairos splits `Flow` APIs into two distinct types: `TFlow` and `TState`
+* Kairos splits `Flow` APIs into two distinct types: `Events` and `State`
 
-    * `TFlow` is roughly equivalent to `SharedFlow` w/ a replay cache that
+    * `Events` is roughly equivalent to `SharedFlow` w/ a replay cache that
       exists for the duration of the current Kairos transaction and shared with
       `SharingStarted.WhileSubscribed()`
 
-    * `TState` is roughly equivalent to `StateFlow` shared with
+    * `State` is roughly equivalent to `StateFlow` shared with
       `SharingStarted.Eagerly`, but the current value can only be queried within
       a Kairos transaction, and the value is only updated at the end of the
       transaction
 
 * Kairos further divides `Flow` APIs based on how they internally use state:
 
-  * **FrpTransactionScope:** APIs that internally query some state need to be
+  * **TransactionScope:** APIs that internally query some state need to be
     performed within an Kairos transaction
 
     * this scope is available from the other scopes, and from most lambdas
       passed to other Kairos APIs
 
-  * **FrpStateScope:** APIs that internally accumulate state in reaction to
-    events need to be performed within an FRP State scope (akin to a
-    `CoroutineScope`)
+  * **StateScope:** APIs that internally accumulate state in reaction to events
+    need to be performed within a State scope (akin to a `CoroutineScope`)
 
-    * this scope is a side-effect-free subset of FrpBuildScope, and so can be
-      used wherever you have an FrpBuildScope
+    * this scope is a side-effect-free subset of BuildScope, and so can be
+      used wherever you have an BuildScope
 
-  * **FrpBuildScope:** APIs that perform external side-effects (`Flow.collect`)
-    need to be performed within an FRP Build scope (akin to a `CoroutineScope`)
+  * **BuildScope:** APIs that perform external side-effects (`Flow.collect`)
+    need to be performed within a Build scope (akin to a `CoroutineScope`)
 
-    * this scope is available from `FrpNetwork.activateSpec { … }`
+    * this scope is available from `Network.activateSpec { … }`
 
   * All other APIs can be used anywhere
 
 ## emptyFlow()
 
-Use `emptyTFlow`
+Use `emptyEvents`
 
 ``` kotlin
-// this TFlow emits nothing
-val noEvents: TFlow<Int> = emptyTFlow
+// this Events emits nothing
+val noEvents: Events<Int> = emptyEvents
 ```
 
 ## map { … }
 
-Use `TFlow.map` / `TState.map`
+Use `Events.map` / `State.map`
 
 ``` kotlin
-val anInt: TState<Int> = …
-val squared: TState<Int> = anInt.map { it * it }
-val messages: TFlow<String> = …
-val messageLengths: TFlow<Int> = messages.map { it.size }
+val anInt: State<Int> = …
+val squared: State<Int> = anInt.map { it * it }
+val messages: Events<String> = …
+val messageLengths: Events<Int> = messages.map { it.size }
 ```
 
 ## filter { … } / mapNotNull { … }
 
-### I have a TFlow
+### I have an Events
 
-Use `TFlow.filter` / `TFlow.mapNotNull`
+Use `Events.filter` / `Events.mapNotNull`
 
 ``` kotlin
-val messages: TFlow<String> = …
-val nonEmpty: TFlow<String> = messages.filter { it.isNotEmpty() }
+val messages: Events<String> = …
+val nonEmpty: Events<String> = messages.filter { it.isNotEmpty() }
 ```
 
-### I have a TState
+### I have a State
 
-Convert the `TState` to `TFlow` using `TState.stateChanges`, then use
-`TFlow.filter` / `TFlow.mapNotNull`
+Convert the `State` to `Events` using `State.stateChanges`, then use
+`Events.filter` / `Events.mapNotNull`
 
-If you need to convert back to `TState`, use `TFlow.hold(initialValue)` on the
-result.
+If you need to convert back to `State`, use `Events.holdState(initialValue)` on
+the result.
 
 ``` kotlin
-tState.stateChanges.filter { … }.hold(initialValue)
+state.stateChanges.filter { … }.holdState(initialValue)
 ```
 
-Note that `TFlow.hold` is only available within an `FrpStateScope` in order to
-track the lifetime of the state accumulation.
+Note that `Events.holdState` is only available within an `StateScope` in order
+to track the lifetime of the state accumulation.
 
 ## combine(...) { … }
 
-### I have TStates
+### I have States
 
-Use `combine(TStates)`
+Use `combine(States)`
 
 ``` kotlin
-val someInt: TState<Int> = …
-val someString: TState<String> = …
-val model: TState<MyModel> = combine(someInt, someString) { i, s -> MyModel(i, s) }
+val someInt: State<Int> = …
+val someString: State<String> = …
+val model: State<MyModel> = combine(someInt, someString) { i, s -> MyModel(i, s) }
 ```
 
-### I have TFlows
+### I have Events
 
-Convert the TFlows to TStates using `TFlow.hold(initialValue)`, then use
-`combine(TStates)`
+Convert the Events to States using `Events.holdState(initialValue)`, then use
+`combine(States)`
 
 If you want the behavior of Flow.combine where nothing is emitted until each
-TFlow has emitted at least once, you can use filter:
+Events has emitted at least once, you can use filter:
 
 ``` kotlin
 // null used as an example, can use a different sentinel if needed
-combine(tFlowA.hold(null), tFlowB.hold(null)) { a, b ->
+combine(eventsA.holdState(null), eventsB.holdState(null)) { a, b ->
         a?.let { b?.let { … } }
     }
     .filterNotNull()
 ```
 
-Note that `TFlow.hold` is only available within an `FrpStateScope` in order to
-track the lifetime of the state accumulation.
+Note that `Events.holdState` is only available within an `StateScope` in order
+to track the lifetime of the state accumulation.
 
 #### Explanation
 
@@ -126,7 +125,7 @@
 developers generally append `.onStart { emit(initialValue) }` to the `Flows`
 that don't immediately emit.
 
-Kairos avoids this gotcha by forcing usage of `TState` for `combine`, thus
+Kairos avoids this gotcha by forcing usage of `State` for `combine`, thus
 ensuring that there is always a current value to be combined for each input.
 
 ## collect { … }
@@ -134,197 +133,197 @@
 Use `observe { … }`
 
 ``` kotlin
-val job: Job = tFlow.observe { println("observed: $it") }
+val job: Job = events.observe { println("observed: $it") }
 ```
 
-Note that `observe` is only available within an `FrpBuildScope` in order to
-track the lifetime of the observer. `FrpBuildScope` can only come from a
-top-level `FrpNetwork.transaction { … }`, or a sub-scope created by using a
-`-Latest` operator.
+Note that `observe` is only available within a `BuildScope` in order to track
+the lifetime of the observer. `BuildScope` can only come from a top-level
+`Network.transaction { … }`, or a sub-scope created by using a `-Latest`
+operator.
 
 ## sample(flow) { … }
 
-### I want to sample a TState
+### I want to sample a State
 
-Use `TState.sample()` to get the current value of a `TState`. This can be
-invoked anywhere you have access to an `FrpTransactionScope`.
+Use `State.sample()` to get the current value of a `State`. This can be
+invoked anywhere you have access to an `TransactionScope`.
 
 ``` kotlin
-// the lambda passed to map receives an FrpTransactionScope, so it can invoke
+// the lambda passed to map receives an TransactionScope, so it can invoke
 // sample
-tFlow.map { tState.sample() }
+events.map { state.sample() }
 ```
 
 #### Explanation
 
-To keep all state-reads consistent, the current value of a TState can only be
-queried within a Kairos transaction, modeled with `FrpTransactionScope`. Note
-that both `FrpStateScope` and `FrpBuildScope` extend `FrpTransactionScope`.
+To keep all state-reads consistent, the current value of a State can only be
+queried within a Kairos transaction, modeled with `TransactionScope`. Note that
+both `StateScope` and `BuildScope` extend `TransactionScope`.
 
-### I want to sample a TFlow
+### I want to sample an Events
 
-Convert to a `TState` by using `TFlow.hold(initialValue)`, then use `sample`.
+Convert to a `State` by using `Events.holdState(initialValue)`, then use `sample`.
 
-Note that `hold` is only available within an `FrpStateScope` in order to track
+Note that `holdState` is only available within an `StateScope` in order to track
 the lifetime of the state accumulation.
 
 ## stateIn(scope, sharingStarted, initialValue)
 
-Use `TFlow.hold(initialValue)`. There is no need to supply a sharingStarted
-argument; all states are accumulated eagerly.
+Use `Events.holdState(initialValue)`. There is no need to supply a
+sharingStarted argument; all states are accumulated eagerly.
 
 ``` kotlin
-val ints: TFlow<Int> = …
-val lastSeenInt: TState<Int> = ints.hold(initialValue = 0)
+val ints: Events<Int> = …
+val lastSeenInt: State<Int> = ints.holdState(initialValue = 0)
 ```
 
-Note that `hold` is only available within an `FrpStateScope` in order to track
+Note that `holdState` is only available within an `StateScope` in order to track
 the lifetime of the state accumulation (akin to the scope parameter of
-`Flow.stateIn`). `FrpStateScope` can only come from a top-level
-`FrpNetwork.transaction { … }`, or a sub-scope created by using a `-Latest`
-operator. Also note that `FrpBuildScope` extends `FrpStateScope`.
+`Flow.stateIn`). `StateScope` can only come from a top-level
+`Network.transaction { … }`, or a sub-scope created by using a `-Latest`
+operator. Also note that `BuildScope` extends `StateScope`.
 
 ## distinctUntilChanged()
 
-Use `distinctUntilChanged` like normal. This is only available for `TFlow`;
-`TStates` are already `distinctUntilChanged`.
+Use `distinctUntilChanged` like normal. This is only available for `Events`;
+`States` are already `distinctUntilChanged`.
 
 ## merge(...)
 
-### I have TFlows
+### I have Eventss
 
-Use `merge(TFlows) { … }`. The lambda argument is used to disambiguate multiple
+Use `merge(Events) { … }`. The lambda argument is used to disambiguate multiple
 simultaneous emissions within the same transaction.
 
 #### Explanation
 
-Under Kairos's rules, a `TFlow` may only emit up to once per transaction. This
-means that if we are merging two or more `TFlows` that are emitting at the same
-time (within the same transaction), the resulting merged `TFlow` must emit a
+Under Kairos's rules, an `Events` may only emit up to once per transaction. This
+means that if we are merging two or more `Events` that are emitting at the same
+time (within the same transaction), the resulting merged `Events` must emit a
 single value. The lambda argument allows the developer to decide what to do in
 this case.
 
-### I have TStates
+### I have States
 
-If `combine` doesn't satisfy your needs, you can use `TState.stateChanges` to
-convert to a `TFlow`, and then `merge`.
+If `combine` doesn't satisfy your needs, you can use `State.changes` to
+convert to a `Events`, and then `merge`.
 
 ## conflatedCallbackFlow { … }
 
-Use `tFlow { … }`.
+Use `events { … }`.
 
 As a shortcut, if you already have a `conflatedCallbackFlow { … }`, you can
-convert it to a TFlow via `Flow.toTFlow()`.
+convert it to an Events via `Flow.toEvents()`.
 
-Note that `tFlow` is only available within an `FrpBuildScope` in order to track
-the lifetime of the input registration.
+Note that `events` is only available within a `BuildScope` in order to track the
+lifetime of the input registration.
 
 ## first()
 
-### I have a TState
+### I have a State
 
-Use `TState.sample`.
+Use `State.sample`.
 
-### I have a TFlow
+### I have an Events
 
-Use `TFlow.nextOnly`, which works exactly like `Flow.first` but instead of
-suspending it returns a `TFlow` that emits once.
+Use `Events.nextOnly`, which works exactly like `Flow.first` but instead of
+suspending it returns a `Events` that emits once.
 
 The naming is intentionally different because `first` implies that it is the
 first-ever value emitted from the `Flow` (which makes sense for cold `Flows`),
 whereas `nextOnly` indicates that only the next value relative to the current
 transaction (the one `nextOnly` is being invoked in) will be emitted.
 
-Note that `nextOnly` is only available within an `FrpStateScope` in order to
-track the lifetime of the state accumulation.
+Note that `nextOnly` is only available within an `StateScope` in order to track
+the lifetime of the state accumulation.
 
 ## flatMapLatest { … }
 
 If you want to use -Latest to cancel old side-effects, similar to what the Flow
 -Latest operators offer for coroutines, see `mapLatest`.
 
-### I have a TState…
+### I have a State…
 
-#### …and want to switch TStates
+#### …and want to switch States
 
-Use `TState.flatMap`
+Use `State.flatMap`
 
 ``` kotlin
-val flattened = tState.flatMap { a -> getTState(a) }
+val flattened = state.flatMap { a -> gestate(a) }
 ```
 
-#### …and want to switch TFlows
+#### …and want to switch Events
 
-Use `TState<TFlow<T>>.switch()`
+Use `State<Events<T>>.switchEvents()`
 
 ``` kotlin
-val tFlow = tState.map { a -> getTFlow(a) }.switch()
+val events = state.map { a -> getEvents(a) }.switchEvents()
 ```
 
-### I have a TFlow…
+### I have an Events…
 
-#### …and want to switch TFlows
+#### …and want to switch Events
 
-Use `hold` to convert to a `TState<TFlow<T>>`, then use `switch` to switch to
-the latest `TFlow`.
+Use `holdState` to convert to a `State<Events<T>>`, then use `switchEvents` to
+switch to the latest `Events`.
 
 ``` kotlin
-val tFlow = tFlowOfFlows.hold(emptyTFlow).switch()
+val events = eventsOfFlows.holdState(emptyEvents).switchEvents()
 ```
 
-#### …and want to switch TStates
+#### …and want to switch States
 
-Use `hold` to convert to a `TState<TState<T>>`, then use `flatMap` to switch to
-the latest `TState`.
+Use `holdState` to convert to a `State<State<T>>`, then use `flatMap` to switch
+to the latest `State`.
 
 ``` kotlin
-val tState = tFlowOfStates.hold(tStateOf(initialValue)).flatMap { it }
+val state = eventsOfStates.holdState(stateOf(initialValue)).flatMap { it }
 ```
 
 ## mapLatest { … } / collectLatest { … }
 
-`FrpStateScope` and `FrpBuildScope` both provide `-Latest` operators that
+`StateScope` and `BuildScope` both provide `-Latest` operators that
 automatically cancel old work when new values are emitted.
 
 ``` kotlin
-val currentModel: TState<SomeModel> = …
-val mapped: TState<...> = currentModel.mapLatestBuild { model ->
+val currentModel: State<SomeModel> = …
+val mapped: State<...> = currentModel.mapLatestBuild { model ->
     effect { "new model in the house: $model" }
     model.someState.observe { "someState: $it" }
-    val someData: TState<SomeInfo> =
+    val someData: State<SomeInfo> =
         getBroadcasts(model.uri)
             .map { extractInfo(it) }
-            .hold(initialInfo)
+            .holdState(initialInfo)

 }
 ```
 
 ## flowOf(...)
 
-### I want a TState
+### I want a State
 
-Use `tStateOf(initialValue)`.
+Use `stateOf(initialValue)`.
 
-### I want a TFlow
+### I want an Events
 
 Use `now.map { initialValue }`
 
-Note that `now` is only available within an `FrpTransactionScope`.
+Note that `now` is only available within an `TransactionScope`.
 
 #### Explanation
 
-`TFlows` are not cold, and so there isn't a notion of "emit this value once
+`Events` are not cold, and so there isn't a notion of "emit this value once
 there is a collector" like there is for `Flow`. The closest analog would be
-`TState`, since the initial value is retained indefinitely until there is an
+`State`, since the initial value is retained indefinitely until there is an
 observer. However, it is often useful to immediately emit a value within the
-current transaction, usually when using a `flatMap` or `switch`. In these cases,
-using `now` explicitly models that the emission will occur within the current
-transaction.
+current transaction, usually when using a `flatMap` or `switchEvents`. In these
+cases, using `now` explicitly models that the emission will occur within the
+current transaction.
 
 ``` kotlin
-fun <T> FrpTransactionScope.tFlowOf(value: T): TFlow<T> = now.map { value }
+fun <T> TransactionScope.eventsOf(value: T): Events<T> = now.map { value }
 ```
 
 ## MutableStateFlow / MutableSharedFlow
 
-Use `MutableTState(frpNetwork, initialValue)` and `MutableTFlow(frpNetwork)`.
+Use `MutableState(frpNetwork, initialValue)` and `MutableEvents(frpNetwork)`.
diff --git a/packages/SystemUI/utils/kairos/docs/semantics.md b/packages/SystemUI/utils/kairos/docs/semantics.md
index d43bb44..c8e4680 100644
--- a/packages/SystemUI/utils/kairos/docs/semantics.md
+++ b/packages/SystemUI/utils/kairos/docs/semantics.md
@@ -33,39 +33,39 @@
 
 typealias Transactional<T> = (Time) -> T
 
-typealias TFlow<T> = SortedMap<Time, T>
+typealias Events<T> = SortedMap<Time, T>
 
 private fun <T> SortedMap<Time, T>.pairwise(): List<Pair<Pair<Time, T>, Pair<Time<T>>>> =
   // NOTE: pretend evaluation is lazy, so that error() doesn't immediately throw
   (toList() + Pair(Time.Infinity, error("no value"))).zipWithNext()
 
-class TState<T> internal constructor(
+class State<T> internal constructor(
   internal val current: Transactional<T>,
-  val stateChanges: TFlow<T>,
+  val stateChanges: Events<T>,
 )
 
-val emptyTFlow: TFlow<Nothing> = emptyMap()
+val emptyEvents: Events<Nothing> = emptyMap()
 
-fun <A, B> TFlow<A>.map(f: FrpTransactionScope.(A) -> B): TFlow<B> =
-  mapValues { (t, a) -> FrpTransactionScope(t).f(a) }
+fun <A, B> Events<A>.map(f: TransactionScope.(A) -> B): Events<B> =
+  mapValues { (t, a) -> TransactionScope(t).f(a) }
 
-fun <A> TFlow<A>.filter(f: FrpTransactionScope.(A) -> Boolean): TFlow<A> =
-  filter { (t, a) -> FrpTransactionScope(t).f(a) }
+fun <A> Events<A>.filter(f: TransactionScope.(A) -> Boolean): Events<A> =
+  filter { (t, a) -> TransactionScope(t).f(a) }
 
 fun <A> merge(
-  first: TFlow<A>,
-  second: TFlow<A>,
+  first: Events<A>,
+  second: Events<A>,
   onCoincidence: Time.(A, A) -> A,
-): TFlow<A> =
+): Events<A> =
   first.toMutableMap().also { result ->
     second.forEach { (t, a) ->
       result.merge(t, a) { f, s ->
-        FrpTranscationScope(t).onCoincidence(f, a)
+        TransactionScope(t).onCoincidence(f, a)
       }
     }
   }.toSortedMap()
 
-fun <A> TState<TFlow<A>>.switch(): TFlow<A> {
+fun <A> State<Events<A>>.switchEvents(): Events<A> {
   val truncated = listOf(Pair(Time.BigBang, current.invoke(Time.BigBang))) +
     stateChanges.dropWhile { (time, _) -> time < time0 }
   val events =
@@ -77,7 +77,7 @@
   return events.toSortedMap()
 }
 
-fun <A> TState<TFlow<A>>.switchPromptly(): TFlow<A> {
+fun <A> State<Events<A>>.switchEventsPromptly(): Events<A> {
   val truncated = listOf(Pair(Time.BigBang, current.invoke(Time.BigBang))) +
     stateChanges.dropWhile { (time, _) -> time < time0 }
   val events =
@@ -89,24 +89,24 @@
   return events.toSortedMap()
 }
 
-typealias GroupedTFlow<K, V> = TFlow<Map<K, V>>
+typealias GroupedEvents<K, V> = Events<Map<K, V>>
 
-fun <K, V> TFlow<Map<K, V>>.groupByKey(): GroupedTFlow<K, V> = this
+fun <K, V> Events<Map<K, V>>.groupByKey(): GroupedEvents<K, V> = this
 
-fun <K, V> GroupedTFlow<K, V>.eventsForKey(key: K): TFlow<V> =
+fun <K, V> GroupedEvents<K, V>.eventsForKey(key: K): Events<V> =
   map { m -> m[k] }.filter { it != null }.map { it!! }
 
-fun <A, B> TState<A>.map(f: (A) -> B): TState<B> =
-  TState(
+fun <A, B> State<A>.map(f: (A) -> B): State<B> =
+  State(
     current = { t -> f(current.invoke(t)) },
     stateChanges = stateChanges.map { f(it) },
   )
 
-fun <A, B, C> TState<A>.combineWith(
-  other: TState<B>,
+fun <A, B, C> State<A>.combineWith(
+  other: State<B>,
   f: (A, B) -> C,
-): TState<C> =
-  TState(
+): State<C> =
+  State(
     current = { t -> f(current.invoke(t), other.current.invoke(t)) },
     stateChanges = run {
       val aChanges =
@@ -129,7 +129,7 @@
     },
   )
 
-fun <A> TState<TState<A>>.flatten(): TState<A> {
+fun <A> State<State<A>>.flatten(): State<A> {
   val changes =
     stateChanges
       .pairwise()
@@ -144,55 +144,55 @@
           inWindow
         }
       }
-  return TState(
+  return State(
     current = { t -> current.invoke(t).current.invoke(t) },
     stateChanges = changes.toSortedMap(),
   )
 }
 
-open class FrpTranscationScope internal constructor(
+open class TransactionScope internal constructor(
   internal val currentTime: Time,
 ) {
-  val now: TFlow<Unit> =
+  val now: Events<Unit> =
     sortedMapOf(currentTime to Unit)
 
   fun <A> Transactional<A>.sample(): A =
     invoke(currentTime)
 
-  fun <A> TState<A>.sample(): A =
+  fun <A> State<A>.sample(): A =
     current.sample()
 }
 
-class FrpStateScope internal constructor(
+class StateScope internal constructor(
   time: Time,
   internal val stopTime: Time,
-): FrpTransactionScope(time) {
+): TransactionScope(time) {
 
-  fun <A, B> TFlow<A>.fold(
+  fun <A, B> Events<A>.foldState(
     initialValue: B,
-    f: FrpTransactionScope.(B, A) -> B,
-  ): TState<B> {
+    f: TransactionScope.(B, A) -> B,
+  ): State<B> {
     val truncated =
       dropWhile { (t, _) -> t < currentTime }
         .takeWhile { (t, _) -> t <= stopTime }
-    val folded =
+    val foldStateed =
       truncated
         .scan(Pair(currentTime, initialValue)) { (_, b) (t, a) ->
-          Pair(t, FrpTransactionScope(t).f(a, b))
+          Pair(t, TransactionScope(t).f(a, b))
         }
     val lookup = { t1 ->
-      folded.lastOrNull { (t0, _) -> t0 < t1 }?.value ?: initialValue
+      foldStateed.lastOrNull { (t0, _) -> t0 < t1 }?.value ?: initialValue
     }
-    return TState(lookup, folded.toSortedMap())
+    return State(lookup, foldStateed.toSortedMap())
   }
 
-  fun <A> TFlow<A>.hold(initialValue: A): TState<A> =
-    fold(initialValue) { _, a -> a }
+  fun <A> Events<A>.holdState(initialValue: A): State<A> =
+    foldState(initialValue) { _, a -> a }
 
-  fun <K, V> TFlow<Map<K, Maybe<V>>>.foldMapIncrementally(
+  fun <K, V> Events<Map<K, Maybe<V>>>.foldStateMapIncrementally(
     initialValues: Map<K, V>
-  ): TState<Map<K, V>> =
-    fold(initialValues) { patch, map ->
+  ): State<Map<K, V>> =
+    foldState(initialValues) { patch, map ->
       val eithers = patch.map { (k, v) ->
         if (v is Just) Left(k to v.value) else Right(k)
       }
@@ -203,18 +203,18 @@
       updated
     }
 
-  fun <K : Any, V> TFlow<Map<K, Maybe<TFlow<V>>>>.mergeIncrementally(
-    initialTFlows: Map<K, TFlow<V>>,
-  ): TFlow<Map<K, V>> =
-    foldMapIncrementally(initialTFlows).map { it.merge() }.switch()
+  fun <K : Any, V> Events<Map<K, Maybe<Events<V>>>>.mergeIncrementally(
+    initialEventss: Map<K, Events<V>>,
+  ): Events<Map<K, V>> =
+    foldStateMapIncrementally(initialEventss).map { it.merge() }.switchEvents()
 
-  fun <K, A, B> TFlow<Map<K, Maybe<A>>.mapLatestStatefulForKey(
-    transform: suspend FrpStateScope.(A) -> B,
-  ): TFlow<Map<K, Maybe<B>>> =
+  fun <K, A, B> Events<Map<K, Maybe<A>>.mapLatestStatefulForKey(
+    transform: suspend StateScope.(A) -> B,
+  ): Events<Map<K, Maybe<B>>> =
     pairwise().map { ((t0, patch), (t1, _)) ->
       patch.map { (k, ma) ->
         ma.map { a ->
-          FrpStateScope(t0, t1).transform(a)
+          StateScope(t0, t1).transform(a)
         }
       }
     }
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Booleans.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Booleans.kt
new file mode 100644
index 0000000..ca02576
--- /dev/null
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Booleans.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.kairos
+
+/** Returns a [State] that is `true` only when all of [states] are `true`. */
+@ExperimentalKairosApi
+fun allOf(vararg states: State<Boolean>): State<Boolean> = combine(*states) { it.allTrue() }
+
+/** Returns a [State] that is `true` when any of [states] are `true`. */
+@ExperimentalKairosApi
+fun anyOf(vararg states: State<Boolean>): State<Boolean> = combine(*states) { it.anyTrue() }
+
+/** Returns a [State] containing the inverse of the Boolean held by the original [State]. */
+@ExperimentalKairosApi fun not(state: State<Boolean>): State<Boolean> = state.mapCheapUnsafe { !it }
+
+private fun Iterable<Boolean>.allTrue() = all { it }
+
+private fun Iterable<Boolean>.anyTrue() = any { it }
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/BuildScope.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/BuildScope.kt
new file mode 100644
index 0000000..bd2173c
--- /dev/null
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/BuildScope.kt
@@ -0,0 +1,872 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.kairos
+
+import com.android.systemui.kairos.util.Maybe
+import com.android.systemui.kairos.util.map
+import kotlin.coroutines.CoroutineContext
+import kotlin.coroutines.EmptyCoroutineContext
+import kotlinx.coroutines.CompletableDeferred
+import kotlinx.coroutines.Deferred
+import kotlinx.coroutines.DisposableHandle
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.awaitCancellation
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.FlowCollector
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharedFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.dropWhile
+import kotlinx.coroutines.flow.scan
+
+/** A computation that can modify the Kairos network. */
+typealias BuildSpec<A> = BuildScope.() -> A
+
+/**
+ * Constructs a [BuildSpec]. The passed [block] will be invoked with a [BuildScope] that can be used
+ * to perform network-building operations, including adding new inputs and outputs to the network,
+ * as well as all operations available in [TransactionScope].
+ */
+@ExperimentalKairosApi
+@Suppress("NOTHING_TO_INLINE")
+inline fun <A> buildSpec(noinline block: BuildScope.() -> A): BuildSpec<A> = block
+
+/** Applies the [BuildSpec] within this [BuildScope]. */
+@ExperimentalKairosApi
+inline operator fun <A> BuildScope.invoke(block: BuildScope.() -> A) = run(block)
+
+/** Operations that add inputs and outputs to a Kairos network. */
+@ExperimentalKairosApi
+interface BuildScope : HasNetwork, StateScope {
+
+    /**
+     * Defers invoking [block] until after the current [BuildScope] code-path completes, returning a
+     * [DeferredValue] that can be used to reference the result.
+     *
+     * Useful for recursive definitions.
+     *
+     * @see deferredBuildScopeAction
+     * @see DeferredValue
+     */
+    fun <R> deferredBuildScope(block: BuildScope.() -> R): DeferredValue<R>
+
+    /**
+     * Defers invoking [block] until after the current [BuildScope] code-path completes.
+     *
+     * Useful for recursive definitions.
+     *
+     * @see deferredBuildScope
+     */
+    fun deferredBuildScopeAction(block: BuildScope.() -> Unit)
+
+    /**
+     * Returns an [Events] containing the results of applying [transform] to each value of the
+     * original [Events].
+     *
+     * [transform] can perform modifications to the Kairos network via its [BuildScope] receiver.
+     * Unlike [mapLatestBuild], these modifications are not undone with each subsequent emission of
+     * the original [Events].
+     *
+     * **NOTE:** This API does not [observe] the original [Events], meaning that unless the returned
+     * (or a downstream) [Events] is observed separately, [transform] will not be invoked, and no
+     * internal side-effects will occur.
+     */
+    fun <A, B> Events<A>.mapBuild(transform: BuildScope.(A) -> B): Events<B>
+
+    /**
+     * Invokes [block] whenever this [Events] emits a value, allowing side-effects to be safely
+     * performed in reaction to the emission.
+     *
+     * Specifically, [block] is deferred to the end of the transaction, and is only actually
+     * executed if this [BuildScope] is still active by that time. It can be deactivated due to a
+     * -Latest combinator, for example.
+     *
+     * [Disposing][DisposableHandle.dispose] of the returned [DisposableHandle] will stop the
+     * observation of new emissions. It will however *not* cancel any running effects from previous
+     * emissions. To achieve this behavior, use [launchScope] or [asyncScope] to create a child
+     * build scope:
+     * ``` kotlin
+     *   val job = launchScope {
+     *       events.observe { x ->
+     *           launchEffect { longRunningEffect(x) }
+     *       }
+     *   }
+     *   // cancels observer and any running effects:
+     *   job.cancel()
+     * ```
+     */
+    // TODO: remove disposable handle return? might add more confusion than convenience
+    fun <A> Events<A>.observe(
+        coroutineContext: CoroutineContext = EmptyCoroutineContext,
+        block: EffectScope.(A) -> Unit = {},
+    ): DisposableHandle
+
+    /**
+     * Returns an [Events] containing the results of applying each [BuildSpec] emitted from the
+     * original [Events], and a [DeferredValue] containing the result of applying [initialSpecs]
+     * immediately.
+     *
+     * When each [BuildSpec] is applied, changes from the previously-active [BuildSpec] with the
+     * same key are undone (any registered [observers][observe] are unregistered, and any pending
+     * [side-effects][effect] are cancelled).
+     *
+     * If the [Maybe] value for an associated key is [absent][Maybe.absent], then the
+     * previously-active [BuildSpec] will be undone with no replacement.
+     */
+    fun <K, A, B> Events<Map<K, Maybe<BuildSpec<A>>>>.applyLatestSpecForKey(
+        initialSpecs: DeferredValue<Map<K, BuildSpec<B>>>,
+        numKeys: Int? = null,
+    ): Pair<Events<Map<K, Maybe<A>>>, DeferredValue<Map<K, B>>>
+
+    /**
+     * Creates an instance of an [Events] with elements that are emitted from [builder].
+     *
+     * [builder] is run in its own coroutine, allowing for ongoing work that can emit to the
+     * provided [EventProducerScope].
+     *
+     * By default, [builder] is only running while the returned [Events] is being
+     * [observed][observe]. If you want it to run at all times, simply add a no-op observer:
+     * ``` kotlin
+     *   events { ... }.apply { observe() }
+     * ```
+     */
+    // TODO: eventually this should be defined on KairosNetwork + an extension on HasNetwork
+    //  - will require modifying InputNode so that it can be manually killed, as opposed to using
+    //    takeUntil (which requires a StateScope).
+    fun <T> events(builder: suspend EventProducerScope<T>.() -> Unit): Events<T>
+
+    /**
+     * Creates an instance of an [Events] with elements that are emitted from [builder].
+     *
+     * [builder] is run in its own coroutine, allowing for ongoing work that can emit to the
+     * provided [CoalescingEventProducerScope].
+     *
+     * By default, [builder] is only running while the returned [Events] is being
+     * [observed][observe]. If you want it to run at all times, simply add a no-op observer:
+     * ``` kotlin
+     *   events { ... }.apply { observe() }
+     * ```
+     *
+     * In the event of backpressure, emissions are *coalesced* into batches. When a value is
+     * [emitted][CoalescingEventProducerScope.emit] from [builder], it is merged into the batch via
+     * [coalesce]. Once the batch is consumed by the kairos network in the next transaction, the
+     * batch is reset back to [getInitialValue].
+     */
+    // TODO: see TODO for [events]
+    fun <In, Out> coalescingEvents(
+        getInitialValue: () -> Out,
+        coalesce: (old: Out, new: In) -> Out,
+        builder: suspend CoalescingEventProducerScope<In>.() -> Unit,
+    ): Events<Out>
+
+    /**
+     * Creates a new [BuildScope] that is a child of this one.
+     *
+     * This new scope can be manually cancelled via the returned [Job], or will be cancelled
+     * automatically when its parent is cancelled. Cancellation will unregister all
+     * [observers][observe] and cancel all scheduled [effects][effect].
+     *
+     * The return value from [block] can be accessed via the returned [DeferredValue].
+     */
+    // TODO: return a DisposableHandle instead of Job?
+    fun <A> asyncScope(block: BuildSpec<A>): Pair<DeferredValue<A>, Job>
+
+    // TODO: once we have context params, these can all become extensions:
+
+    /**
+     * Returns an [Events] containing the results of applying the given [transform] function to each
+     * value of the original [Events].
+     *
+     * Unlike [Events.map], [transform] can perform arbitrary asynchronous code. This code is run
+     * outside of the current Kairos transaction; when [transform] returns, the returned value is
+     * emitted from the result [Events] in a new transaction.
+     *
+     * ``` kotlin
+     *     fun <A, B> Events<A>.mapAsyncLatest(transform: suspend (A) -> B): Events<B> =
+     *         mapLatestBuild { a -> asyncEvent { transform(a) } }.flatten()
+     * ```
+     */
+    fun <A, B> Events<A>.mapAsyncLatest(transform: suspend (A) -> B): Events<B> =
+        mapLatestBuild { a -> asyncEvent { transform(a) } }.flatten()
+
+    /**
+     * Invokes [block] whenever this [Events] emits a value. [block] receives an [BuildScope] that
+     * can be used to make further modifications to the Kairos network, and/or perform side-effects
+     * via [effect].
+     *
+     * @see observe
+     */
+    fun <A> Events<A>.observeBuild(block: BuildScope.(A) -> Unit = {}): DisposableHandle =
+        mapBuild(block).observe()
+
+    /**
+     * Returns a [StateFlow] whose [value][StateFlow.value] tracks the current
+     * [value of this State][State.sample], and will emit at the same rate as [State.changes].
+     */
+    fun <A> State<A>.toStateFlow(): StateFlow<A> {
+        val innerStateFlow = MutableStateFlow(sampleDeferred())
+        changes.observe { innerStateFlow.value = deferredOf(it) }
+        return object : StateFlow<A> {
+            override val replayCache: List<A>
+                get() = innerStateFlow.replayCache.map { it.value }
+
+            override val value: A
+                get() = innerStateFlow.value.value
+
+            override suspend fun collect(collector: FlowCollector<A>): Nothing {
+                innerStateFlow.collect { collector.emit(it.value) }
+            }
+        }
+    }
+
+    /**
+     * Returns a [SharedFlow] configured with a replay cache of size [replay] that emits the current
+     * [value][State.sample] of this [State] followed by all [changes].
+     */
+    fun <A> State<A>.toSharedFlow(replay: Int = 0): SharedFlow<A> {
+        val result = MutableSharedFlow<A>(replay, extraBufferCapacity = 1)
+        deferredBuildScope {
+            result.tryEmit(sample())
+            changes.observe { a -> result.tryEmit(a) }
+        }
+        return result
+    }
+
+    /**
+     * Returns a [SharedFlow] configured with a replay cache of size [replay] that emits values
+     * whenever this [Events] emits.
+     */
+    fun <A> Events<A>.toSharedFlow(replay: Int = 0): SharedFlow<A> {
+        val result = MutableSharedFlow<A>(replay, extraBufferCapacity = 1)
+        observe { a -> result.tryEmit(a) }
+        return result
+    }
+
+    /**
+     * Returns a [State] that holds onto the value returned by applying the most recently emitted
+     * [BuildSpec] from the original [Events], or the value returned by applying [initialSpec] if
+     * nothing has been emitted since it was constructed.
+     *
+     * When each [BuildSpec] is applied, changes from the previously-active [BuildSpec] are undone
+     * (any registered [observers][observe] are unregistered, and any pending [side-effects][effect]
+     * are cancelled).
+     */
+    fun <A> Events<BuildSpec<A>>.holdLatestSpec(initialSpec: BuildSpec<A>): State<A> {
+        val (changes: Events<A>, initApplied: DeferredValue<A>) = applyLatestSpec(initialSpec)
+        return changes.holdStateDeferred(initApplied)
+    }
+
+    /**
+     * Returns a [State] containing the value returned by applying the [BuildSpec] held by the
+     * original [State].
+     *
+     * When each [BuildSpec] is applied, changes from the previously-active [BuildSpec] are undone
+     * (any registered [observers][observe] are unregistered, and any pending [side-effects][effect]
+     * are cancelled).
+     */
+    fun <A> State<BuildSpec<A>>.applyLatestSpec(): State<A> {
+        val (appliedChanges: Events<A>, init: DeferredValue<A>) =
+            changes.applyLatestSpec(buildSpec { sample().applySpec() })
+        return appliedChanges.holdStateDeferred(init)
+    }
+
+    /**
+     * Returns an [Events] containing the results of applying each [BuildSpec] emitted from the
+     * original [Events].
+     *
+     * When each [BuildSpec] is applied, changes from the previously-active [BuildSpec] are undone
+     * (any registered [observers][observe] are unregistered, and any pending [side-effects][effect]
+     * are cancelled).
+     */
+    fun <A> Events<BuildSpec<A>>.applyLatestSpec(): Events<A> = applyLatestSpec(buildSpec {}).first
+
+    /**
+     * Returns an [Events] that switches to a new [Events] produced by [transform] every time the
+     * original [Events] emits a value.
+     *
+     * [transform] can perform modifications to the Kairos network via its [BuildScope] receiver.
+     * When the original [Events] emits a new value, those changes are undone (any registered
+     * [observers][observe] are unregistered, and any pending [effects][effect] are cancelled).
+     */
+    fun <A, B> Events<A>.flatMapLatestBuild(transform: BuildScope.(A) -> Events<B>): Events<B> =
+        mapCheap { buildSpec { transform(it) } }.applyLatestSpec().flatten()
+
+    /**
+     * Returns a [State] by applying [transform] to the value held by the original [State].
+     *
+     * [transform] can perform modifications to the Kairos network via its [BuildScope] receiver.
+     * When the value held by the original [State] changes, those changes are undone (any registered
+     * [observers][observe] are unregistered, and any pending [effects][effect] are cancelled).
+     */
+    fun <A, B> State<A>.flatMapLatestBuild(transform: BuildScope.(A) -> State<B>): State<B> =
+        mapLatestBuild { transform(it) }.flatten()
+
+    /**
+     * Returns a [State] that transforms the value held inside this [State] by applying it to the
+     * [transform].
+     *
+     * [transform] can perform modifications to the Kairos network via its [BuildScope] receiver.
+     * When the value held by the original [State] changes, those changes are undone (any registered
+     * [observers][observe] are unregistered, and any pending [effects][effect] are cancelled).
+     */
+    fun <A, B> State<A>.mapLatestBuild(transform: BuildScope.(A) -> B): State<B> =
+        mapCheapUnsafe { buildSpec { transform(it) } }.applyLatestSpec()
+
+    /**
+     * Returns an [Events] containing the results of applying each [BuildSpec] emitted from the
+     * original [Events], and a [DeferredValue] containing the result of applying [initialSpec]
+     * immediately.
+     *
+     * When each [BuildSpec] is applied, changes from the previously-active [BuildSpec] are undone
+     * (any registered [observers][observe] are unregistered, and any pending [side-effects][effect]
+     * are cancelled).
+     */
+    fun <A : Any?, B> Events<BuildSpec<B>>.applyLatestSpec(
+        initialSpec: BuildSpec<A>
+    ): Pair<Events<B>, DeferredValue<A>> {
+        val (events, result) =
+            mapCheap { spec -> mapOf(Unit to Maybe.present(spec)) }
+                .applyLatestSpecForKey(initialSpecs = mapOf(Unit to initialSpec), numKeys = 1)
+        val outEvents: Events<B> =
+            events.mapMaybe {
+                checkNotNull(it[Unit]) { "applyLatest: expected result, but none present in: $it" }
+            }
+        val outInit: DeferredValue<A> = deferredBuildScope {
+            val initResult: Map<Unit, A> = result.value
+            check(Unit in initResult) {
+                "applyLatest: expected initial result, but none present in: $initResult"
+            }
+            @Suppress("UNCHECKED_CAST")
+            initResult.getOrDefault(Unit) { null } as A
+        }
+        return Pair(outEvents, outInit)
+    }
+
+    /**
+     * Returns an [Events] containing the results of applying [transform] to each value of the
+     * original [Events].
+     *
+     * [transform] can perform modifications to the Kairos network via its [BuildScope] receiver.
+     * With each invocation of [transform], changes from the previous invocation are undone (any
+     * registered [observers][observe] are unregistered, and any pending [side-effects][effect] are
+     * cancelled).
+     */
+    fun <A, B> Events<A>.mapLatestBuild(transform: BuildScope.(A) -> B): Events<B> =
+        mapCheap { buildSpec { transform(it) } }.applyLatestSpec()
+
+    /**
+     * Returns an [Events] containing the results of applying [transform] to each value of the
+     * original [Events], and a [DeferredValue] containing the result of applying [transform] to
+     * [initialValue] immediately.
+     *
+     * [transform] can perform modifications to the Kairos network via its [BuildScope] receiver.
+     * With each invocation of [transform], changes from the previous invocation are undone (any
+     * registered [observers][observe] are unregistered, and any pending [side-effects][effect] are
+     * cancelled).
+     */
+    fun <A, B> Events<A>.mapLatestBuild(
+        initialValue: A,
+        transform: BuildScope.(A) -> B,
+    ): Pair<Events<B>, DeferredValue<B>> =
+        mapLatestBuildDeferred(deferredOf(initialValue), transform)
+
+    /**
+     * Returns an [Events] containing the results of applying [transform] to each value of the
+     * original [Events], and a [DeferredValue] containing the result of applying [transform] to
+     * [initialValue] immediately.
+     *
+     * [transform] can perform modifications to the Kairos network via its [BuildScope] receiver.
+     * With each invocation of [transform], changes from the previous invocation are undone (any
+     * registered [observers][observe] are unregistered, and any pending [side-effects][effect] are
+     * cancelled).
+     */
+    fun <A, B> Events<A>.mapLatestBuildDeferred(
+        initialValue: DeferredValue<A>,
+        transform: BuildScope.(A) -> B,
+    ): Pair<Events<B>, DeferredValue<B>> =
+        mapCheap { buildSpec { transform(it) } }
+            .applyLatestSpec(initialSpec = buildSpec { transform(initialValue.value) })
+
+    /**
+     * Returns an [Events] containing the results of applying each [BuildSpec] emitted from the
+     * original [Events], and a [DeferredValue] containing the result of applying [initialSpecs]
+     * immediately.
+     *
+     * When each [BuildSpec] is applied, changes from the previously-active [BuildSpec] with the
+     * same key are undone (any registered [observers][observe] are unregistered, and any pending
+     * [side-effects][effect] are cancelled).
+     *
+     * If the [Maybe] value for an associated key is [absent][Maybe.absent], then the
+     * previously-active [BuildSpec] will be undone with no replacement.
+     */
+    fun <K, A, B> Events<Map<K, Maybe<BuildSpec<A>>>>.applyLatestSpecForKey(
+        initialSpecs: Map<K, BuildSpec<B>>,
+        numKeys: Int? = null,
+    ): Pair<Events<Map<K, Maybe<A>>>, DeferredValue<Map<K, B>>> =
+        applyLatestSpecForKey(deferredOf(initialSpecs), numKeys)
+
+    /**
+     * Returns an [Incremental] containing the results of applying each [BuildSpec] emitted from the
+     * original [Incremental].
+     *
+     * When each [BuildSpec] is applied, changes from the previously-active [BuildSpec] with the
+     * same key are undone (any registered [observers][observe] are unregistered, and any pending
+     * [side-effects][effect] are cancelled).
+     *
+     * If the [Maybe] value for an associated key is [absent][Maybe.absent], then the
+     * previously-active [BuildSpec] will be undone with no replacement.
+     */
+    fun <K, V> Incremental<K, BuildSpec<V>>.applyLatestSpecForKey(
+        numKeys: Int? = null
+    ): Incremental<K, V> {
+        val (events, initial) = updates.applyLatestSpecForKey(sampleDeferred(), numKeys)
+        return events.foldStateMapIncrementally(initial)
+    }
+
+    /**
+     * Returns an [Events] containing the results of applying each [BuildSpec] emitted from the
+     * original [Events].
+     *
+     * When each [BuildSpec] is applied, changes from the previously-active [BuildSpec] with the
+     * same key are undone (any registered [observers][observe] are unregistered, and any pending
+     * [side-effects][effect] are cancelled).
+     *
+     * If the [Maybe] value for an associated key is [absent][Maybe.absent], then the
+     * previously-active [BuildSpec] will be undone with no replacement.
+     */
+    fun <K, V> Events<Map<K, Maybe<BuildSpec<V>>>>.applyLatestSpecForKey(
+        numKeys: Int? = null
+    ): Events<Map<K, Maybe<V>>> =
+        applyLatestSpecForKey<K, V, Nothing>(deferredOf(emptyMap()), numKeys).first
+
+    /**
+     * Returns a [State] containing the latest results of applying each [BuildSpec] emitted from the
+     * original [Events].
+     *
+     * When each [BuildSpec] is applied, changes from the previously-active [BuildSpec] with the
+     * same key are undone (any registered [observers][observe] are unregistered, and any pending
+     * [side-effects][effect] are cancelled).
+     *
+     * If the [Maybe] value for an associated key is [absent][Maybe.absent], then the
+     * previously-active [BuildSpec] will be undone with no replacement.
+     */
+    fun <K, V> Events<Map<K, Maybe<BuildSpec<V>>>>.holdLatestSpecForKey(
+        initialSpecs: DeferredValue<Map<K, BuildSpec<V>>>,
+        numKeys: Int? = null,
+    ): Incremental<K, V> {
+        val (changes, initialValues) = applyLatestSpecForKey(initialSpecs, numKeys)
+        return changes.foldStateMapIncrementally(initialValues)
+    }
+
+    /**
+     * Returns a [State] containing the latest results of applying each [BuildSpec] emitted from the
+     * original [Events].
+     *
+     * When each [BuildSpec] is applied, changes from the previously-active [BuildSpec] with the
+     * same key are undone (any registered [observers][observe] are unregistered, and any pending
+     * [side-effects][effect] are cancelled).
+     *
+     * If the [Maybe] value for an associated key is [absent][Maybe.absent], then the
+     * previously-active [BuildSpec] will be undone with no replacement.
+     */
+    fun <K, V> Events<Map<K, Maybe<BuildSpec<V>>>>.holdLatestSpecForKey(
+        initialSpecs: Map<K, BuildSpec<V>> = emptyMap(),
+        numKeys: Int? = null,
+    ): Incremental<K, V> = holdLatestSpecForKey(deferredOf(initialSpecs), numKeys)
+
+    /**
+     * Returns an [Events] containing the results of applying [transform] to each value of the
+     * original [Events], and a [DeferredValue] containing the result of applying [transform] to
+     * [initialValues] immediately.
+     *
+     * [transform] can perform modifications to the Kairos network via its [BuildScope] receiver.
+     * With each invocation of [transform], changes from the previous invocation are undone (any
+     * registered [observers][observe] are unregistered, and any pending [side-effects][effect] are
+     * cancelled).
+     *
+     * If the [Maybe] value for an associated key is [absent][Maybe.absent], then the
+     * previously-active [BuildScope] will be undone with no replacement.
+     */
+    fun <K, A, B> Events<Map<K, Maybe<A>>>.mapLatestBuildForKey(
+        initialValues: DeferredValue<Map<K, A>>,
+        numKeys: Int? = null,
+        transform: BuildScope.(K, A) -> B,
+    ): Pair<Events<Map<K, Maybe<B>>>, DeferredValue<Map<K, B>>> =
+        map { patch -> patch.mapValues { (k, v) -> v.map { buildSpec { transform(k, it) } } } }
+            .applyLatestSpecForKey(
+                deferredBuildScope {
+                    initialValues.value.mapValues { (k, v) -> buildSpec { transform(k, v) } }
+                },
+                numKeys = numKeys,
+            )
+
+    /**
+     * Returns an [Events] containing the results of applying [transform] to each value of the
+     * original [Events], and a [DeferredValue] containing the result of applying [transform] to
+     * [initialValues] immediately.
+     *
+     * [transform] can perform modifications to the Kairos network via its [BuildScope] receiver.
+     * With each invocation of [transform], changes from the previous invocation are undone (any
+     * registered [observers][observe] are unregistered, and any pending [side-effects][effect] are
+     * cancelled).
+     *
+     * If the [Maybe] value for an associated key is [absent][Maybe.absent], then the
+     * previously-active [BuildScope] will be undone with no replacement.
+     */
+    fun <K, A, B> Events<Map<K, Maybe<A>>>.mapLatestBuildForKey(
+        initialValues: Map<K, A>,
+        numKeys: Int? = null,
+        transform: BuildScope.(K, A) -> B,
+    ): Pair<Events<Map<K, Maybe<B>>>, DeferredValue<Map<K, B>>> =
+        mapLatestBuildForKey(deferredOf(initialValues), numKeys, transform)
+
+    /**
+     * Returns an [Events] containing the results of applying [transform] to each value of the
+     * original [Events].
+     *
+     * [transform] can perform modifications to the Kairos network via its [BuildScope] receiver.
+     * With each invocation of [transform], changes from the previous invocation are undone (any
+     * registered [observers][observe] are unregistered, and any pending [side-effects][effect] are
+     * cancelled).
+     *
+     * If the [Maybe] value for an associated key is [absent][Maybe.absent], then the
+     * previously-active [BuildScope] will be undone with no replacement.
+     */
+    fun <K, A, B> Events<Map<K, Maybe<A>>>.mapLatestBuildForKey(
+        numKeys: Int? = null,
+        transform: BuildScope.(K, A) -> B,
+    ): Events<Map<K, Maybe<B>>> = mapLatestBuildForKey(emptyMap(), numKeys, transform).first
+
+    /** Returns a [Deferred] containing the next value to be emitted from this [Events]. */
+    fun <R> Events<R>.nextDeferred(): Deferred<R> {
+        lateinit var next: CompletableDeferred<R>
+        val job = launchScope { nextOnly().observe { next.complete(it) } }
+        next = CompletableDeferred(parent = job)
+        return next
+    }
+
+    /** Returns a [State] that reflects the [StateFlow.value] of this [StateFlow]. */
+    fun <A> StateFlow<A>.toState(): State<A> {
+        val initial = value
+        return events { dropWhile { it == initial }.collect { emit(it) } }.holdState(initial)
+    }
+
+    /** Returns an [Events] that emits whenever this [Flow] emits. */
+    fun <A> Flow<A>.toEvents(): Events<A> = events { collect { emit(it) } }
+
+    /**
+     * Shorthand for:
+     * ``` kotlin
+     * flow.toEvents().holdState(initialValue)
+     * ```
+     */
+    fun <A> Flow<A>.toState(initialValue: A): State<A> = toEvents().holdState(initialValue)
+
+    /**
+     * Shorthand for:
+     * ``` kotlin
+     * flow.scan(initialValue, operation).toEvents().holdState(initialValue)
+     * ```
+     */
+    fun <A, B> Flow<A>.scanToState(initialValue: B, operation: (B, A) -> B): State<B> =
+        scan(initialValue, operation).toEvents().holdState(initialValue)
+
+    /**
+     * Shorthand for:
+     * ``` kotlin
+     * flow.scan(initialValue) { a, f -> f(a) }.toEvents().holdState(initialValue)
+     * ```
+     */
+    fun <A> Flow<(A) -> A>.scanToState(initialValue: A): State<A> =
+        scanToState(initialValue) { a, f -> f(a) }
+
+    /**
+     * Invokes [block] whenever this [Events] emits a value. [block] receives an [BuildScope] that
+     * can be used to make further modifications to the Kairos network, and/or perform side-effects
+     * via [effect].
+     *
+     * With each invocation of [block], changes from the previous invocation are undone (any
+     * registered [observers][observe] are unregistered, and any pending [side-effects][effect] are
+     * cancelled).
+     */
+    fun <A> Events<A>.observeLatestBuild(block: BuildScope.(A) -> Unit = {}): DisposableHandle =
+        mapLatestBuild { block(it) }.observe()
+
+    /**
+     * Invokes [block] whenever this [Events] emits a value, allowing side-effects to be safely
+     * performed in reaction to the emission.
+     *
+     * With each invocation of [block], running effects from the previous invocation are cancelled.
+     */
+    fun <A> Events<A>.observeLatest(block: EffectScope.(A) -> Unit = {}): DisposableHandle {
+        var innerJob: Job? = null
+        return observeBuild {
+            innerJob?.cancel()
+            innerJob = effect { block(it) }
+        }
+    }
+
+    /**
+     * Invokes [block] with the value held by this [State], allowing side-effects to be safely
+     * performed in reaction to the state changing.
+     *
+     * With each invocation of [block], running effects from the previous invocation are cancelled.
+     */
+    fun <A> State<A>.observeLatest(block: EffectScope.(A) -> Unit = {}): Job = launchScope {
+        var innerJob = effect { block(sample()) }
+        changes.observeBuild {
+            innerJob.cancel()
+            innerJob = effect { block(it) }
+        }
+    }
+
+    /**
+     * Applies [block] to the value held by this [State]. [block] receives an [BuildScope] that can
+     * be used to make further modifications to the Kairos network, and/or perform side-effects via
+     * [effect].
+     *
+     * [block] can perform modifications to the Kairos network via its [BuildScope] receiver. With
+     * each invocation of [block], changes from the previous invocation are undone (any registered
+     * [observers][observe] are unregistered, and any pending [side-effects][effect] are cancelled).
+     */
+    fun <A> State<A>.observeLatestBuild(block: BuildScope.(A) -> Unit = {}): Job = launchScope {
+        var innerJob: Job = launchScope { block(sample()) }
+        changes.observeBuild {
+            innerJob.cancel()
+            innerJob = launchScope { block(it) }
+        }
+    }
+
+    /** Applies the [BuildSpec] within this [BuildScope]. */
+    fun <A> BuildSpec<A>.applySpec(): A = this()
+
+    /**
+     * Applies the [BuildSpec] within this [BuildScope], returning the result as an [DeferredValue].
+     */
+    fun <A> BuildSpec<A>.applySpecDeferred(): DeferredValue<A> = deferredBuildScope { applySpec() }
+
+    /**
+     * Invokes [block] on the value held in this [State]. [block] receives an [BuildScope] that can
+     * be used to make further modifications to the Kairos network, and/or perform side-effects via
+     * [effect].
+     *
+     * ``` kotlin
+     *     fun <A> State<A>.observeBuild(block: BuildScope.(A) -> Unit = {}): Job = launchScope {
+     *         block(sample())
+     *         changes.observeBuild(block)
+     *     }
+     * ```
+     */
+    fun <A> State<A>.observeBuild(block: BuildScope.(A) -> Unit = {}): Job = launchScope {
+        block(sample())
+        changes.observeBuild(block)
+    }
+
+    /**
+     * Invokes [block] with the current value of this [State], re-invoking whenever it changes,
+     * allowing side-effects to be safely performed in reaction value changing.
+     *
+     * Specifically, [block] is deferred to the end of the transaction, and is only actually
+     * executed if this [BuildScope] is still active by that time. It can be deactivated due to a
+     * -Latest combinator, for example.
+     *
+     * If the [State] is changing within the *current* transaction (i.e. [changes] is presently
+     * emitting) then [block] will be invoked for the first time with the new value; otherwise, it
+     * will be invoked with the [current][sample] value.
+     */
+    fun <A> State<A>.observe(block: EffectScope.(A) -> Unit = {}): DisposableHandle =
+        now.map { sample() }.mergeWith(changes) { _, new -> new }.observe { block(it) }
+}
+
+/**
+ * Returns an [Events] that emits the result of [block] once it completes. [block] is evaluated
+ * outside of the current Kairos transaction; when it completes, the returned [Events] emits in a
+ * new transaction.
+ *
+ * ``` kotlin
+ *   fun <A> BuildScope.asyncEvent(block: suspend () -> A): Events<A> =
+ *       events { emit(block()) }.apply { observe() }
+ * ```
+ */
+@ExperimentalKairosApi
+fun <A> BuildScope.asyncEvent(block: suspend () -> A): Events<A> =
+    events {
+            // TODO: if block completes synchronously, it would be nice to emit within this
+            //  transaction
+            emit(block())
+        }
+        .apply { observe() }
+
+/**
+ * Performs a side-effect in a safe manner w/r/t the current Kairos transaction.
+ *
+ * Specifically, [block] is deferred to the end of the current transaction, and is only actually
+ * executed if this [BuildScope] is still active by that time. It can be deactivated due to a
+ * -Latest combinator, for example.
+ *
+ * ``` kotlin
+ *   fun BuildScope.effect(
+ *       context: CoroutineContext = EmptyCoroutineContext,
+ *       block: EffectScope.() -> Unit,
+ *   ): Job =
+ *       launchScope { now.observe(context) { block() } }
+ * ```
+ */
+@ExperimentalKairosApi
+fun BuildScope.effect(
+    context: CoroutineContext = EmptyCoroutineContext,
+    block: EffectScope.() -> Unit,
+): Job = launchScope { now.observe(context) { block() } }
+
+/**
+ * Launches [block] in a new coroutine, returning a [Job] bound to the coroutine.
+ *
+ * This coroutine is not actually started until the *end* of the current Kairos transaction. This is
+ * done because the current [BuildScope] might be deactivated within this transaction, perhaps due
+ * to a -Latest combinator. If this happens, then the coroutine will never actually be started.
+ *
+ * ``` kotlin
+ *   fun BuildScope.launchEffect(block: suspend KairosScope.() -> Unit): Job =
+ *       effect { effectCoroutineScope.launch { block() } }
+ * ```
+ */
+@ExperimentalKairosApi
+fun BuildScope.launchEffect(block: suspend KairosCoroutineScope.() -> Unit): Job =
+    asyncEffect(block)
+
+/**
+ * Launches [block] in a new coroutine, returning the result as a [Deferred].
+ *
+ * This coroutine is not actually started until the *end* of the current Kairos transaction. This is
+ * done because the current [BuildScope] might be deactivated within this transaction, perhaps due
+ * to a -Latest combinator. If this happens, then the coroutine will never actually be started.
+ *
+ * Shorthand for:
+ * ``` kotlin
+ *   fun <R> BuildScope.asyncEffect(block: suspend KairosScope.() -> R): Deferred<R> =
+ *       CompletableDeferred<R>.apply {
+ *               effect { effectCoroutineScope.launch { complete(block()) } }
+ *           }
+ *           .await()
+ * ```
+ */
+@ExperimentalKairosApi
+fun <R> BuildScope.asyncEffect(block: suspend KairosCoroutineScope.() -> R): Deferred<R> {
+    val result = CompletableDeferred<R>()
+    val job = effect { launch { result.complete(block()) } }
+    val handle = job.invokeOnCompletion { result.cancel() }
+    result.invokeOnCompletion {
+        handle.dispose()
+        job.cancel()
+    }
+    return result
+}
+
+/** Like [BuildScope.asyncScope], but ignores the result of [block]. */
+@ExperimentalKairosApi
+fun BuildScope.launchScope(block: BuildSpec<*>): Job = asyncScope(block).second
+
+/**
+ * Creates an instance of an [Events] with elements that are emitted from [builder].
+ *
+ * [builder] is run in its own coroutine, allowing for ongoing work that can emit to the provided
+ * [MutableState].
+ *
+ * By default, [builder] is only running while the returned [Events] is being
+ * [observed][BuildScope.observe]. If you want it to run at all times, simply add a no-op observer:
+ * ``` kotlin
+ * events { ... }.apply { observe() }
+ * ```
+ *
+ * In the event of backpressure, emissions are *coalesced* into batches. When a value is
+ * [emitted][CoalescingEventProducerScope.emit] from [builder], it is merged into the batch via
+ * [coalesce]. Once the batch is consumed by the Kairos network in the next transaction, the batch
+ * is reset back to [initialValue].
+ */
+@ExperimentalKairosApi
+fun <In, Out> BuildScope.coalescingEvents(
+    initialValue: Out,
+    coalesce: (old: Out, new: In) -> Out,
+    builder: suspend CoalescingEventProducerScope<In>.() -> Unit,
+): Events<Out> = coalescingEvents(getInitialValue = { initialValue }, coalesce, builder)
+
+/**
+ * Creates an instance of an [Events] with elements that are emitted from [builder].
+ *
+ * [builder] is run in its own coroutine, allowing for ongoing work that can emit to the provided
+ * [MutableState].
+ *
+ * By default, [builder] is only running while the returned [Events] is being
+ * [observed][BuildScope.observe]. If you want it to run at all times, simply add a no-op observer:
+ * ``` kotlin
+ * events { ... }.apply { observe() }
+ * ```
+ *
+ * In the event of backpressure, emissions are *conflated*; any older emissions are dropped and only
+ * the most recent emission will be used when the Kairos network is ready.
+ */
+@ExperimentalKairosApi
+fun <T> BuildScope.conflatedEvents(
+    builder: suspend CoalescingEventProducerScope<T>.() -> Unit
+): Events<T> =
+    coalescingEvents<T, Any?>(initialValue = Any(), coalesce = { _, new -> new }, builder = builder)
+        .mapCheap {
+            @Suppress("UNCHECKED_CAST")
+            it as T
+        }
+
+/** Scope for emitting to a [BuildScope.coalescingEvents]. */
+fun interface CoalescingEventProducerScope<in T> {
+    /**
+     * Inserts [value] into the current batch, enqueueing it for emission from this [Events] if not
+     * already pending.
+     *
+     * Backpressure occurs when [emit] is called while the Kairos network is currently in a
+     * transaction; if called multiple times, then emissions will be coalesced into a single batch
+     * that is then processed when the network is ready.
+     */
+    fun emit(value: T)
+}
+
+/** Scope for emitting to a [BuildScope.events]. */
+fun interface EventProducerScope<in T> {
+    /**
+     * Emits a [value] to this [Events], suspending the caller until the Kairos transaction
+     * containing the emission has completed.
+     */
+    suspend fun emit(value: T)
+}
+
+/**
+ * Suspends forever. Upon cancellation, runs [block]. Useful for unregistering callbacks inside of
+ * [BuildScope.events] and [BuildScope.coalescingEvents].
+ */
+suspend fun awaitClose(block: () -> Unit): Nothing =
+    try {
+        awaitCancellation()
+    } finally {
+        block()
+    }
+
+/**
+ * Runs [spec] in this [BuildScope], and then re-runs it whenever [rebuildSignal] emits. Returns a
+ * [State] that holds the result of the currently-active [BuildSpec].
+ */
+@ExperimentalKairosApi
+fun <A> BuildScope.rebuildOn(rebuildSignal: Events<*>, spec: BuildSpec<A>): State<A> =
+    rebuildSignal.map { spec }.holdLatestSpec(spec)
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Combinators.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Combinators.kt
deleted file mode 100644
index ae9b8c8..0000000
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Combinators.kt
+++ /dev/null
@@ -1,273 +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.kairos
-
-import com.android.systemui.kairos.util.These
-import com.android.systemui.kairos.util.WithPrev
-import com.android.systemui.kairos.util.just
-import com.android.systemui.kairos.util.none
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.channelFlow
-import kotlinx.coroutines.flow.conflate
-
-/**
- * Returns a [TFlow] that emits the value sampled from the [Transactional] produced by each emission
- * of the original [TFlow], within the same transaction of the original emission.
- */
-@ExperimentalFrpApi
-fun <A> TFlow<Transactional<A>>.sampleTransactionals(): TFlow<A> = map { it.sample() }
-
-/** @see FrpTransactionScope.sample */
-@ExperimentalFrpApi
-fun <A, B, C> TFlow<A>.sample(
-    state: TState<B>,
-    transform: suspend FrpTransactionScope.(A, B) -> C,
-): TFlow<C> = map { transform(it, state.sample()) }
-
-/** @see FrpTransactionScope.sample */
-@ExperimentalFrpApi
-fun <A, B, C> TFlow<A>.sample(
-    transactional: Transactional<B>,
-    transform: suspend FrpTransactionScope.(A, B) -> C,
-): TFlow<C> = map { transform(it, transactional.sample()) }
-
-/**
- * Like [sample], but if [state] is changing at the time it is sampled ([stateChanges] is emitting),
- * then the new value is passed to [transform].
- *
- * Note that [sample] is both more performant, and safer to use with recursive definitions. You will
- * generally want to use it rather than this.
- *
- * @see sample
- */
-@ExperimentalFrpApi
-fun <A, B, C> TFlow<A>.samplePromptly(
-    state: TState<B>,
-    transform: suspend FrpTransactionScope.(A, B) -> C,
-): TFlow<C> =
-    sample(state) { a, b -> These.thiz<Pair<A, B>, B>(a to b) }
-        .mergeWith(state.stateChanges.map { These.that(it) }) { thiz, that ->
-            These.both((thiz as These.This).thiz, (that as These.That).that)
-        }
-        .mapMaybe { these ->
-            when (these) {
-                // both present, transform the upstream value and the new value
-                is These.Both -> just(transform(these.thiz.first, these.that))
-                // no upstream present, so don't perform the sample
-                is These.That -> none()
-                // just the upstream, so transform the upstream and the old value
-                is These.This -> just(transform(these.thiz.first, these.thiz.second))
-            }
-        }
-
-/**
- * Returns a cold [Flow] that, when collected, emits from this [TFlow]. [network] is needed to
- * transactionally connect to / disconnect from the [TFlow] when collection starts/stops.
- */
-@ExperimentalFrpApi
-fun <A> TFlow<A>.toColdConflatedFlow(network: FrpNetwork): Flow<A> =
-    channelFlow { network.activateSpec { observe { trySend(it) } } }.conflate()
-
-/**
- * Returns a cold [Flow] that, when collected, emits from this [TState]. [network] is needed to
- * transactionally connect to / disconnect from the [TState] when collection starts/stops.
- */
-@ExperimentalFrpApi
-fun <A> TState<A>.toColdConflatedFlow(network: FrpNetwork): Flow<A> =
-    channelFlow { network.activateSpec { observe { trySend(it) } } }.conflate()
-
-/**
- * Returns a cold [Flow] that, when collected, applies this [FrpSpec] in a new transaction in this
- * [network], and then emits from the returned [TFlow].
- *
- * When collection is cancelled, so is the [FrpSpec]. This means all ongoing work is cleaned up.
- */
-@ExperimentalFrpApi
-@JvmName("flowSpecToColdConflatedFlow")
-fun <A> FrpSpec<TFlow<A>>.toColdConflatedFlow(network: FrpNetwork): Flow<A> =
-    channelFlow { network.activateSpec { applySpec().observe { trySend(it) } } }.conflate()
-
-/**
- * Returns a cold [Flow] that, when collected, applies this [FrpSpec] in a new transaction in this
- * [network], and then emits from the returned [TState].
- *
- * When collection is cancelled, so is the [FrpSpec]. This means all ongoing work is cleaned up.
- */
-@ExperimentalFrpApi
-@JvmName("stateSpecToColdConflatedFlow")
-fun <A> FrpSpec<TState<A>>.toColdConflatedFlow(network: FrpNetwork): Flow<A> =
-    channelFlow { network.activateSpec { applySpec().observe { trySend(it) } } }.conflate()
-
-/**
- * Returns a cold [Flow] that, when collected, applies this [Transactional] in a new transaction in
- * this [network], and then emits from the returned [TFlow].
- */
-@ExperimentalFrpApi
-@JvmName("transactionalFlowToColdConflatedFlow")
-fun <A> Transactional<TFlow<A>>.toColdConflatedFlow(network: FrpNetwork): Flow<A> =
-    channelFlow { network.activateSpec { sample().observe { trySend(it) } } }.conflate()
-
-/**
- * Returns a cold [Flow] that, when collected, applies this [Transactional] in a new transaction in
- * this [network], and then emits from the returned [TState].
- */
-@ExperimentalFrpApi
-@JvmName("transactionalStateToColdConflatedFlow")
-fun <A> Transactional<TState<A>>.toColdConflatedFlow(network: FrpNetwork): Flow<A> =
-    channelFlow { network.activateSpec { sample().observe { trySend(it) } } }.conflate()
-
-/**
- * Returns a cold [Flow] that, when collected, applies this [FrpStateful] in a new transaction in
- * this [network], and then emits from the returned [TFlow].
- *
- * When collection is cancelled, so is the [FrpStateful]. This means all ongoing work is cleaned up.
- */
-@ExperimentalFrpApi
-@JvmName("statefulFlowToColdConflatedFlow")
-fun <A> FrpStateful<TFlow<A>>.toColdConflatedFlow(network: FrpNetwork): Flow<A> =
-    channelFlow { network.activateSpec { applyStateful().observe { trySend(it) } } }.conflate()
-
-/**
- * Returns a cold [Flow] that, when collected, applies this [Transactional] in a new transaction in
- * this [network], and then emits from the returned [TState].
- *
- * When collection is cancelled, so is the [FrpStateful]. This means all ongoing work is cleaned up.
- */
-@ExperimentalFrpApi
-@JvmName("statefulStateToColdConflatedFlow")
-fun <A> FrpStateful<TState<A>>.toColdConflatedFlow(network: FrpNetwork): Flow<A> =
-    channelFlow { network.activateSpec { applyStateful().observe { trySend(it) } } }.conflate()
-
-/** Return a [TFlow] that emits from the original [TFlow] only when [state] is `true`. */
-@ExperimentalFrpApi
-fun <A> TFlow<A>.filter(state: TState<Boolean>): TFlow<A> = filter { state.sample() }
-
-private fun Iterable<Boolean>.allTrue() = all { it }
-
-private fun Iterable<Boolean>.anyTrue() = any { it }
-
-/** Returns a [TState] that is `true` only when all of [states] are `true`. */
-@ExperimentalFrpApi
-fun allOf(vararg states: TState<Boolean>): TState<Boolean> = combine(*states) { it.allTrue() }
-
-/** Returns a [TState] that is `true` when any of [states] are `true`. */
-@ExperimentalFrpApi
-fun anyOf(vararg states: TState<Boolean>): TState<Boolean> = combine(*states) { it.anyTrue() }
-
-/** Returns a [TState] containing the inverse of the Boolean held by the original [TState]. */
-@ExperimentalFrpApi fun not(state: TState<Boolean>): TState<Boolean> = state.mapCheapUnsafe { !it }
-
-/**
- * Represents a modal FRP sub-network.
- *
- * When [enabled][enableMode], all network modifications are applied immediately to the FRP network.
- * When the returned [TFlow] emits a [FrpBuildMode], that mode is enabled and replaces this mode,
- * undoing all modifications in the process (any registered [observers][FrpBuildScope.observe] are
- * unregistered, and any pending [side-effects][FrpBuildScope.effect] are cancelled).
- *
- * Use [compiledFrpSpec] to compile and stand-up a mode graph.
- *
- * @see FrpStatefulMode
- */
-@ExperimentalFrpApi
-fun interface FrpBuildMode<out A> {
-    /**
-     * Invoked when this mode is enabled. Returns a value and a [TFlow] that signals a switch to a
-     * new mode.
-     */
-    suspend fun FrpBuildScope.enableMode(): Pair<A, TFlow<FrpBuildMode<A>>>
-}
-
-/**
- * Returns an [FrpSpec] that, when [applied][FrpBuildScope.applySpec], stands up a modal-transition
- * graph starting with this [FrpBuildMode], automatically switching to new modes as they are
- * produced.
- *
- * @see FrpBuildMode
- */
-@ExperimentalFrpApi
-val <A> FrpBuildMode<A>.compiledFrpSpec: FrpSpec<TState<A>>
-    get() = frpSpec {
-        var modeChangeEvents by TFlowLoop<FrpBuildMode<A>>()
-        val activeMode: TState<Pair<A, TFlow<FrpBuildMode<A>>>> =
-            modeChangeEvents
-                .map { it.run { frpSpec { enableMode() } } }
-                .holdLatestSpec(frpSpec { enableMode() })
-        modeChangeEvents =
-            activeMode.map { statefully { it.second.nextOnly() } }.applyLatestStateful().switch()
-        activeMode.map { it.first }
-    }
-
-/**
- * Represents a modal FRP sub-network.
- *
- * When [enabled][enableMode], all state accumulation is immediately started. When the returned
- * [TFlow] emits a [FrpBuildMode], that mode is enabled and replaces this mode, stopping all state
- * accumulation in the process.
- *
- * Use [compiledStateful] to compile and stand-up a mode graph.
- *
- * @see FrpBuildMode
- */
-@ExperimentalFrpApi
-fun interface FrpStatefulMode<out A> {
-    /**
-     * Invoked when this mode is enabled. Returns a value and a [TFlow] that signals a switch to a
-     * new mode.
-     */
-    suspend fun FrpStateScope.enableMode(): Pair<A, TFlow<FrpStatefulMode<A>>>
-}
-
-/**
- * Returns an [FrpStateful] that, when [applied][FrpStateScope.applyStateful], stands up a
- * modal-transition graph starting with this [FrpStatefulMode], automatically switching to new modes
- * as they are produced.
- *
- * @see FrpBuildMode
- */
-@ExperimentalFrpApi
-val <A> FrpStatefulMode<A>.compiledStateful: FrpStateful<TState<A>>
-    get() = statefully {
-        var modeChangeEvents by TFlowLoop<FrpStatefulMode<A>>()
-        val activeMode: TState<Pair<A, TFlow<FrpStatefulMode<A>>>> =
-            modeChangeEvents
-                .map { it.run { statefully { enableMode() } } }
-                .holdLatestStateful(statefully { enableMode() })
-        modeChangeEvents =
-            activeMode.map { statefully { it.second.nextOnly() } }.applyLatestStateful().switch()
-        activeMode.map { it.first }
-    }
-
-/**
- * Runs [spec] in this [FrpBuildScope], and then re-runs it whenever [rebuildSignal] emits. Returns
- * a [TState] that holds the result of the currently-active [FrpSpec].
- */
-@ExperimentalFrpApi
-fun <A> FrpBuildScope.rebuildOn(rebuildSignal: TFlow<*>, spec: FrpSpec<A>): TState<A> =
-    rebuildSignal.map { spec }.holdLatestSpec(spec)
-
-/**
- * Like [stateChanges] but also includes the old value of this [TState].
- *
- * Shorthand for:
- * ``` kotlin
- *     stateChanges.map { WithPrev(previousValue = sample(), newValue = it) }
- * ```
- */
-@ExperimentalFrpApi
-val <A> TState<A>.transitions: TFlow<WithPrev<A, A>>
-    get() = stateChanges.map { WithPrev(previousValue = sample(), newValue = it) }
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Combine.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Combine.kt
new file mode 100644
index 0000000..b3d89c3
--- /dev/null
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Combine.kt
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.kairos
+
+import com.android.systemui.kairos.internal.NoScope
+import com.android.systemui.kairos.internal.init
+import com.android.systemui.kairos.internal.zipStates
+
+/**
+ * Returns a [State] whose value is generated with [transform] by combining the current values of
+ * each given [State].
+ *
+ * @sample com.android.systemui.kairos.KairosSamples.combineState
+ */
+@ExperimentalKairosApi
+@JvmName(name = "stateCombine")
+fun <A, B, C> State<A>.combine(other: State<B>, transform: KairosScope.(A, B) -> C): State<C> =
+    combine(this, other, transform)
+
+/**
+ * Returns a [State] by combining the values held inside the given [States][State] into a [List].
+ *
+ * @see State.combine
+ */
+@ExperimentalKairosApi
+fun <A> Iterable<State<A>>.combine(): State<List<A>> {
+    val operatorName = "combine"
+    val name = operatorName
+    return StateInit(
+        init(name) {
+            val states = map { it.init }
+            zipStates(
+                name,
+                operatorName,
+                states.size,
+                states = init(null) { states.map { it.connect(this) } },
+            )
+        }
+    )
+}
+
+/**
+ * Returns a [State] by combining the values held inside the given [States][State] into a [Map].
+ *
+ * @see State.combine
+ */
+@ExperimentalKairosApi
+fun <K, A> Map<K, State<A>>.combine(): State<Map<K, A>> =
+    asIterable().map { (k, state) -> state.map { v -> k to v } }.combine().map { it.toMap() }
+
+/**
+ * Returns a [State] whose value is generated with [transform] by combining the current values of
+ * each given [State].
+ *
+ * @see State.combine
+ */
+@ExperimentalKairosApi
+fun <A, B> Iterable<State<A>>.combine(transform: KairosScope.(List<A>) -> B): State<B> =
+    combine().map(transform)
+
+/**
+ * Returns a [State] by combining the values held inside the given [State]s into a [List].
+ *
+ * @see State.combine
+ */
+@ExperimentalKairosApi
+fun <A> combine(vararg states: State<A>): State<List<A>> = states.asIterable().combine()
+
+/**
+ * Returns a [State] whose value is generated with [transform] by combining the current values of
+ * each given [State].
+ *
+ * @see State.combine
+ */
+@ExperimentalKairosApi
+fun <A, B> combine(vararg states: State<A>, transform: KairosScope.(List<A>) -> B): State<B> =
+    states.asIterable().combine(transform)
+
+/**
+ * Returns a [State] whose value is generated with [transform] by combining the current values of
+ * each given [State].
+ *
+ * @see State.combine
+ */
+@ExperimentalKairosApi
+fun <A, B, Z> combine(
+    stateA: State<A>,
+    stateB: State<B>,
+    transform: KairosScope.(A, B) -> Z,
+): State<Z> {
+    val operatorName = "combine"
+    val name = operatorName
+    return StateInit(
+        init(name) {
+            zipStates(name, operatorName, stateA.init, stateB.init) { a, b ->
+                NoScope.transform(a, b)
+            }
+        }
+    )
+}
+
+/**
+ * Returns a [State] whose value is generated with [transform] by combining the current values of
+ * each given [State].
+ *
+ * @see State.combine
+ */
+@ExperimentalKairosApi
+fun <A, B, C, Z> combine(
+    stateA: State<A>,
+    stateB: State<B>,
+    stateC: State<C>,
+    transform: KairosScope.(A, B, C) -> Z,
+): State<Z> {
+    val operatorName = "combine"
+    val name = operatorName
+    return StateInit(
+        init(name) {
+            zipStates(name, operatorName, stateA.init, stateB.init, stateC.init) { a, b, c ->
+                NoScope.transform(a, b, c)
+            }
+        }
+    )
+}
+
+/**
+ * Returns a [State] whose value is generated with [transform] by combining the current values of
+ * each given [State].
+ *
+ * @see State.combine
+ */
+@ExperimentalKairosApi
+fun <A, B, C, D, Z> combine(
+    stateA: State<A>,
+    stateB: State<B>,
+    stateC: State<C>,
+    stateD: State<D>,
+    transform: KairosScope.(A, B, C, D) -> Z,
+): State<Z> {
+    val operatorName = "combine"
+    val name = operatorName
+    return StateInit(
+        init(name) {
+            zipStates(name, operatorName, stateA.init, stateB.init, stateC.init, stateD.init) {
+                a,
+                b,
+                c,
+                d ->
+                NoScope.transform(a, b, c, d)
+            }
+        }
+    )
+}
+
+/**
+ * Returns a [State] whose value is generated with [transform] by combining the current values of
+ * each given [State].
+ *
+ * @see State.combine
+ */
+@ExperimentalKairosApi
+fun <A, B, C, D, E, Z> combine(
+    stateA: State<A>,
+    stateB: State<B>,
+    stateC: State<C>,
+    stateD: State<D>,
+    stateE: State<E>,
+    transform: KairosScope.(A, B, C, D, E) -> Z,
+): State<Z> {
+    val operatorName = "combine"
+    val name = operatorName
+    return StateInit(
+        init(name) {
+            zipStates(
+                name,
+                operatorName,
+                stateA.init,
+                stateB.init,
+                stateC.init,
+                stateD.init,
+                stateE.init,
+            ) { a, b, c, d, e ->
+                NoScope.transform(a, b, c, d, e)
+            }
+        }
+    )
+}
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/DeferredValue.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/DeferredValue.kt
new file mode 100644
index 0000000..4b9bb0e
--- /dev/null
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/DeferredValue.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.kairos
+
+import com.android.systemui.kairos.internal.CompletableLazy
+
+/**
+ * A value that may not be immediately (synchronously) available, but is guaranteed to be available
+ * before this transaction is completed.
+ */
+@ExperimentalKairosApi
+class DeferredValue<out A> internal constructor(internal val unwrapped: Lazy<A>) {
+    /**
+     * Returns the value held by this [DeferredValue], or throws [IllegalStateException] if it is
+     * not yet available.
+     */
+    val value: A
+        get() = unwrapped.value
+}
+
+/** Returns an already-available [DeferredValue] containing [value]. */
+@ExperimentalKairosApi
+fun <A> deferredOf(value: A): DeferredValue<A> = DeferredValue(CompletableLazy(value))
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/EffectScope.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/EffectScope.kt
new file mode 100644
index 0000000..14d45d4
--- /dev/null
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/EffectScope.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.kairos
+
+import kotlin.coroutines.CoroutineContext
+import kotlin.coroutines.EmptyCoroutineContext
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.CoroutineStart
+import kotlinx.coroutines.Deferred
+import kotlinx.coroutines.Job
+
+/**
+ * Scope for external side-effects triggered by the Kairos network.
+ *
+ * This still occurs within the context of a transaction, so general suspending calls are disallowed
+ * to prevent blocking the transaction. You can [launch] new coroutines to perform long-running
+ * asynchronous work. These coroutines are kept alive for the duration of the containing
+ * [BuildScope] that this side-effect scope is running in.
+ */
+@ExperimentalKairosApi
+interface EffectScope : HasNetwork, TransactionScope {
+    /**
+     * Creates a coroutine that is a child of this [EffectScope], and returns its future result as a
+     * [Deferred].
+     *
+     * @see kotlinx.coroutines.async
+     */
+    fun <R> async(
+        context: CoroutineContext = EmptyCoroutineContext,
+        start: CoroutineStart = CoroutineStart.DEFAULT,
+        block: suspend KairosCoroutineScope.() -> R,
+    ): Deferred<R>
+
+    /**
+     * Launches a new coroutine that is a child of this [EffectScope] without blocking the current
+     * thread and returns a reference to the coroutine as a [Job].
+     *
+     * @see kotlinx.coroutines.launch
+     */
+    fun launch(
+        context: CoroutineContext = EmptyCoroutineContext,
+        start: CoroutineStart = CoroutineStart.DEFAULT,
+        block: suspend KairosCoroutineScope.() -> Unit,
+    ): Job = async(context, start, block)
+}
+
+@ExperimentalKairosApi interface KairosCoroutineScope : HasNetwork, CoroutineScope
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Events.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Events.kt
new file mode 100644
index 0000000..8f468c1
--- /dev/null
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Events.kt
@@ -0,0 +1,331 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.kairos
+
+import com.android.systemui.kairos.internal.CompletableLazy
+import com.android.systemui.kairos.internal.EventsImpl
+import com.android.systemui.kairos.internal.Init
+import com.android.systemui.kairos.internal.InitScope
+import com.android.systemui.kairos.internal.InputNode
+import com.android.systemui.kairos.internal.Network
+import com.android.systemui.kairos.internal.NoScope
+import com.android.systemui.kairos.internal.activated
+import com.android.systemui.kairos.internal.cached
+import com.android.systemui.kairos.internal.constInit
+import com.android.systemui.kairos.internal.init
+import com.android.systemui.kairos.internal.mapImpl
+import com.android.systemui.kairos.internal.neverImpl
+import com.android.systemui.kairos.internal.util.hashString
+import com.android.systemui.kairos.util.Maybe
+import com.android.systemui.kairos.util.toMaybe
+import java.util.concurrent.atomic.AtomicReference
+import kotlin.reflect.KProperty
+import kotlinx.coroutines.CoroutineStart
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.async
+import kotlinx.coroutines.coroutineScope
+
+/**
+ * A series of values of type [A] available at discrete points in time.
+ *
+ * [Events] follow these rules:
+ * 1. Within a single Kairos network transaction, an [Events] instance will only emit *once*.
+ * 2. The order that different [Events] instances emit values within a transaction is undefined, and
+ *    are conceptually *simultaneous*.
+ * 3. [Events] emissions are *ephemeral* and do not last beyond the transaction they are emitted,
+ *    unless explicitly [observed][BuildScope.observe] or [held][StateScope.holdState] as a [State].
+ */
+@ExperimentalKairosApi
+sealed class Events<out A> {
+    companion object {
+        /** An [Events] with no values. */
+        val empty: Events<Nothing> = EmptyEvents
+    }
+}
+
+/** An [Events] with no values. */
+@ExperimentalKairosApi val emptyEvents: Events<Nothing> = Events.empty
+
+/**
+ * A forward-reference to an [Events]. Useful for recursive definitions.
+ *
+ * This reference can be used like a standard [Events], but will throw an error if its [loopback] is
+ * unset before it is [observed][BuildScope.observe].
+ *
+ * @sample com.android.systemui.kairos.KairosSamples.eventsLoop
+ */
+@ExperimentalKairosApi
+class EventsLoop<A> : Events<A>() {
+    private val deferred = CompletableLazy<Events<A>>()
+
+    internal val init: Init<EventsImpl<A>> =
+        init(name = null) { deferred.value.init.connect(evalScope = this) }
+
+    /**
+     * The [Events] this reference is referring to. Must be set before this [EventsLoop] is
+     * [observed][BuildScope.observe].
+     */
+    var loopback: Events<A>? = null
+        set(value) {
+            value?.let {
+                check(!deferred.isInitialized()) { "EventsLoop.loopback has already been set." }
+                deferred.setValue(value)
+                field = value
+            }
+        }
+
+    operator fun getValue(thisRef: Any?, property: KProperty<*>): Events<A> = this
+
+    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Events<A>) {
+        loopback = value
+    }
+
+    override fun toString(): String = "${this::class.simpleName}@$hashString"
+}
+
+/**
+ * Returns an [Events] that acts as a deferred-reference to the [Events] produced by this [Lazy].
+ *
+ * When the returned [Events] is accessed by the Kairos network, the [Lazy]'s [value][Lazy.value]
+ * will be queried and used.
+ *
+ * Useful for recursive definitions.
+ *
+ * ``` kotlin
+ *   fun <A> Lazy<Events<A>>.defer() = deferredEvents { value }
+ * ```
+ *
+ * @see deferredEvents
+ */
+@ExperimentalKairosApi fun <A> Lazy<Events<A>>.defer(): Events<A> = deferInline { value }
+
+/**
+ * Returns an [Events] that acts as a deferred-reference to the [Events] produced by this
+ * [DeferredValue].
+ *
+ * When the returned [Events] is accessed by the Kairos network, the [DeferredValue] will be queried
+ * and used.
+ *
+ * Useful for recursive definitions.
+ *
+ * ``` kotlin
+ *   fun <A> DeferredValue<Events<A>>.defer() = deferredEvents { get() }
+ * ```
+ *
+ * @see deferredEvents
+ */
+@ExperimentalKairosApi
+fun <A> DeferredValue<Events<A>>.defer(): Events<A> = deferInline { unwrapped.value }
+
+/**
+ * Returns an [Events] that acts as a deferred-reference to the [Events] produced by [block].
+ *
+ * When the returned [Events] is accessed by the Kairos network, [block] will be invoked and the
+ * returned [Events] will be used.
+ *
+ * Useful for recursive definitions.
+ */
+@ExperimentalKairosApi
+fun <A> deferredEvents(block: KairosScope.() -> Events<A>): Events<A> = deferInline {
+    NoScope.block()
+}
+
+/**
+ * Returns an [Events] that contains only the
+ * [present][com.android.systemui.kairos.util.Maybe.present] results of applying [transform] to each
+ * value of the original [Events].
+ *
+ * @sample com.android.systemui.kairos.KairosSamples.mapMaybe
+ * @see mapNotNull
+ */
+@ExperimentalKairosApi
+fun <A, B> Events<A>.mapMaybe(transform: TransactionScope.(A) -> Maybe<B>): Events<B> =
+    map(transform).filterPresent()
+
+/**
+ * Returns an [Events] that contains only the non-null results of applying [transform] to each value
+ * of the original [Events].
+ *
+ * ``` kotlin
+ *  fun <A> Events<A>.mapNotNull(transform: TransactionScope.(A) -> B?): Events<B> =
+ *      mapMaybe { if (it == null) absent else present(it) }
+ * ```
+ *
+ * @see mapMaybe
+ */
+@ExperimentalKairosApi
+fun <A, B> Events<A>.mapNotNull(transform: TransactionScope.(A) -> B?): Events<B> = mapMaybe {
+    transform(it).toMaybe()
+}
+
+/**
+ * Returns an [Events] containing the results of applying [transform] to each value of the original
+ * [Events].
+ *
+ * @sample com.android.systemui.kairos.KairosSamples.mapEvents
+ */
+@ExperimentalKairosApi
+fun <A, B> Events<A>.map(transform: TransactionScope.(A) -> B): Events<B> {
+    val mapped: EventsImpl<B> = mapImpl({ init.connect(evalScope = this) }) { a, _ -> transform(a) }
+    return EventsInit(constInit(name = null, mapped.cached()))
+}
+
+/**
+ * Like [map], but the emission is not cached during the transaction. Use only if [transform] is
+ * fast and pure.
+ *
+ * @sample com.android.systemui.kairos.KairosSamples.mapCheap
+ * @see map
+ */
+@ExperimentalKairosApi
+fun <A, B> Events<A>.mapCheap(transform: TransactionScope.(A) -> B): Events<B> =
+    EventsInit(
+        constInit(name = null, mapImpl({ init.connect(evalScope = this) }) { a, _ -> transform(a) })
+    )
+
+/**
+ * Returns an [Events] that invokes [action] before each value of the original [Events] is emitted.
+ * Useful for logging and debugging.
+ *
+ * ``` kotlin
+ *   fun <A> Events<A>.onEach(action: TransactionScope.(A) -> Unit): Events<A> =
+ *       map { it.also { action(it) } }
+ * ```
+ *
+ * Note that the side effects performed in [onEach] are only performed while the resulting [Events]
+ * is connected to an output of the Kairos network. If your goal is to reliably perform side effects
+ * in response to an [Events], use the output combinators available in [BuildScope], such as
+ * [BuildScope.toSharedFlow] or [BuildScope.observe].
+ */
+@ExperimentalKairosApi
+fun <A> Events<A>.onEach(action: TransactionScope.(A) -> Unit): Events<A> = map {
+    it.also { action(it) }
+}
+
+/**
+ * Splits an [Events] of pairs into a pair of [Events], where each returned [Events] emits half of
+ * the original.
+ *
+ * ``` kotlin
+ *   fun <A, B> Events<Pair<A, B>>.unzip(): Pair<Events<A>, Events<B>> {
+ *       val lefts = map { it.first }
+ *       val rights = map { it.second }
+ *       return lefts to rights
+ *   }
+ * ```
+ */
+@ExperimentalKairosApi
+fun <A, B> Events<Pair<A, B>>.unzip(): Pair<Events<A>, Events<B>> {
+    val lefts = map { it.first }
+    val rights = map { it.second }
+    return lefts to rights
+}
+
+/**
+ * A mutable [Events] that provides the ability to [emit] values to the network, handling
+ * backpressure by coalescing all emissions into batches.
+ *
+ * @see KairosNetwork.coalescingMutableEvents
+ */
+@ExperimentalKairosApi
+class CoalescingMutableEvents<in In, Out>
+internal constructor(
+    internal val name: String?,
+    internal val coalesce: (old: Lazy<Out>, new: In) -> Out,
+    internal val network: Network,
+    private val getInitialValue: () -> Out,
+    internal val impl: InputNode<Out> = InputNode(),
+) : Events<Out>() {
+    private val storage = AtomicReference(false to lazy { getInitialValue() })
+
+    override fun toString(): String = "${this::class.simpleName}@$hashString"
+
+    /**
+     * Inserts [value] into the current batch, enqueueing it for emission from this [Events] if not
+     * already pending.
+     *
+     * Backpressure occurs when [emit] is called while the Kairos network is currently in a
+     * transaction; if called multiple times, then emissions will be coalesced into a single batch
+     * that is then processed when the network is ready.
+     */
+    fun emit(value: In) {
+        val (scheduled, _) =
+            storage.getAndUpdate { (_, batch) -> true to CompletableLazy(coalesce(batch, value)) }
+        if (!scheduled) {
+            @Suppress("DeferredResultUnused")
+            network.transaction(
+                "CoalescingMutableEvents${name?.let { "($name)" }.orEmpty()}.emit"
+            ) {
+                val (_, batch) = storage.getAndSet(false to lazy { getInitialValue() })
+                impl.visit(this, batch.value)
+            }
+        }
+    }
+}
+
+/**
+ * A mutable [Events] that provides the ability to [emit] values to the network, handling
+ * backpressure by suspending the emitter.
+ *
+ * @see KairosNetwork.coalescingMutableEvents
+ */
+@ExperimentalKairosApi
+class MutableEvents<T>
+internal constructor(internal val network: Network, internal val impl: InputNode<T> = InputNode()) :
+    Events<T>() {
+    internal val name: String? = null
+
+    private val storage = AtomicReference<Job?>(null)
+
+    override fun toString(): String = "${this::class.simpleName}@$hashString"
+
+    /**
+     * Emits a [value] to this [Events], suspending the caller until the Kairos transaction
+     * containing the emission has completed.
+     */
+    suspend fun emit(value: T) {
+        coroutineScope {
+            var jobOrNull: Job? = null
+            val newEmit =
+                async(start = CoroutineStart.LAZY) {
+                    jobOrNull?.join()
+                    network.transaction("MutableEvents.emit") { impl.visit(this, value) }.await()
+                }
+            jobOrNull = storage.getAndSet(newEmit)
+            newEmit.await()
+        }
+    }
+}
+
+private data object EmptyEvents : Events<Nothing>()
+
+internal class EventsInit<out A>(val init: Init<EventsImpl<A>>) : Events<A>() {
+    override fun toString(): String = "${this::class.simpleName}@$hashString"
+}
+
+internal val <A> Events<A>.init: Init<EventsImpl<A>>
+    get() =
+        when (this) {
+            is EmptyEvents -> constInit("EmptyEvents", neverImpl)
+            is EventsInit -> init
+            is EventsLoop -> init
+            is CoalescingMutableEvents<*, A> -> constInit(name, impl.activated())
+            is MutableEvents -> constInit(name, impl.activated())
+        }
+
+private inline fun <A> deferInline(crossinline block: InitScope.() -> Events<A>): Events<A> =
+    EventsInit(init(name = null) { block().init.connect(evalScope = this) })
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Filter.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Filter.kt
new file mode 100644
index 0000000..8ca5ac8
--- /dev/null
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Filter.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.kairos
+
+import com.android.systemui.kairos.internal.constInit
+import com.android.systemui.kairos.internal.filterImpl
+import com.android.systemui.kairos.internal.filterPresentImpl
+import com.android.systemui.kairos.util.Maybe
+import com.android.systemui.kairos.util.toMaybe
+
+/** Return an [Events] that emits from the original [Events] only when [state] is `true`. */
+@ExperimentalKairosApi
+fun <A> Events<A>.filter(state: State<Boolean>): Events<A> = filter { state.sample() }
+
+/**
+ * Returns an [Events] containing only values of the original [Events] that are not null.
+ *
+ * ``` kotlin
+ *  fun <A> Events<A?>.filterNotNull(): Events<A> = mapNotNull { it }
+ * ```
+ *
+ * @see mapNotNull
+ */
+@ExperimentalKairosApi
+fun <A> Events<A?>.filterNotNull(): Events<A> = mapCheap { it.toMaybe() }.filterPresent()
+
+/**
+ * Returns an [Events] containing only values of the original [Events] that are instances of [A].
+ *
+ * ``` kotlin
+ *   inline fun <reified A> Events<*>.filterIsInstance(): Events<A> =
+ *       mapNotNull { it as? A }
+ * ```
+ *
+ * @see mapNotNull
+ */
+@ExperimentalKairosApi
+inline fun <reified A> Events<*>.filterIsInstance(): Events<A> =
+    mapCheap { it as? A }.filterNotNull()
+
+/**
+ * Returns an [Events] containing only values of the original [Events] that are present.
+ *
+ * ``` kotlin
+ *  fun <A> Events<Maybe<A>>.filterPresent(): Events<A> = mapMaybe { it }
+ * ```
+ *
+ * @see mapMaybe
+ */
+@ExperimentalKairosApi
+fun <A> Events<Maybe<A>>.filterPresent(): Events<A> =
+    EventsInit(constInit(name = null, filterPresentImpl { init.connect(evalScope = this) }))
+
+/**
+ * Returns an [Events] containing only values of the original [Events] that satisfy the given
+ * [predicate].
+ *
+ * ``` kotlin
+ *   fun <A> Events<A>.filter(predicate: TransactionScope.(A) -> Boolean): Events<A> =
+ *       mapMaybe { if (predicate(it)) present(it) else absent }
+ * ```
+ *
+ * @see mapMaybe
+ */
+@ExperimentalKairosApi
+fun <A> Events<A>.filter(predicate: TransactionScope.(A) -> Boolean): Events<A> {
+    val pulse = filterImpl({ init.connect(evalScope = this) }) { predicate(it) }
+    return EventsInit(constInit(name = null, pulse))
+}
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/FrpBuildScope.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/FrpBuildScope.kt
deleted file mode 100644
index 209a402..0000000
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/FrpBuildScope.kt
+++ /dev/null
@@ -1,885 +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.kairos
-
-import com.android.systemui.kairos.util.Maybe
-import com.android.systemui.kairos.util.just
-import com.android.systemui.kairos.util.map
-import kotlin.coroutines.CoroutineContext
-import kotlin.coroutines.EmptyCoroutineContext
-import kotlin.coroutines.RestrictsSuspension
-import kotlinx.coroutines.CompletableDeferred
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Deferred
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.awaitCancellation
-import kotlinx.coroutines.coroutineScope
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.FlowCollector
-import kotlinx.coroutines.flow.MutableSharedFlow
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.SharedFlow
-import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.dropWhile
-import kotlinx.coroutines.flow.scan
-import kotlinx.coroutines.launch
-
-/** A function that modifies the FrpNetwork. */
-typealias FrpSpec<A> = suspend FrpBuildScope.() -> A
-
-/**
- * Constructs an [FrpSpec]. The passed [block] will be invoked with an [FrpBuildScope] that can be
- * used to perform network-building operations, including adding new inputs and outputs to the
- * network, as well as all operations available in [FrpTransactionScope].
- */
-@ExperimentalFrpApi
-@Suppress("NOTHING_TO_INLINE")
-inline fun <A> frpSpec(noinline block: suspend FrpBuildScope.() -> A): FrpSpec<A> = block
-
-/** Applies the [FrpSpec] within this [FrpBuildScope]. */
-@ExperimentalFrpApi
-inline operator fun <A> FrpBuildScope.invoke(block: FrpBuildScope.() -> A) = run(block)
-
-/** Operations that add inputs and outputs to an FRP network. */
-@ExperimentalFrpApi
-@RestrictsSuspension
-interface FrpBuildScope : FrpStateScope {
-
-    /** TODO: Javadoc */
-    @ExperimentalFrpApi
-    fun <R> deferredBuildScope(block: suspend FrpBuildScope.() -> R): FrpDeferredValue<R>
-
-    /** TODO: Javadoc */
-    @ExperimentalFrpApi fun deferredBuildScopeAction(block: suspend FrpBuildScope.() -> Unit)
-
-    /**
-     * Returns a [TFlow] containing the results of applying [transform] to each value of the
-     * original [TFlow].
-     *
-     * [transform] can perform modifications to the FRP network via its [FrpBuildScope] receiver.
-     * Unlike [mapLatestBuild], these modifications are not undone with each subsequent emission of
-     * the original [TFlow].
-     *
-     * **NOTE:** This API does not [observe] the original [TFlow], meaning that unless the returned
-     * (or a downstream) [TFlow] is observed separately, [transform] will not be invoked, and no
-     * internal side-effects will occur.
-     */
-    @ExperimentalFrpApi
-    fun <A, B> TFlow<A>.mapBuild(transform: suspend FrpBuildScope.(A) -> B): TFlow<B>
-
-    /**
-     * Invokes [block] whenever this [TFlow] emits a value, allowing side-effects to be safely
-     * performed in reaction to the emission.
-     *
-     * Specifically, [block] is deferred to the end of the transaction, and is only actually
-     * executed if this [FrpBuildScope] is still active by that time. It can be deactivated due to a
-     * -Latest combinator, for example.
-     *
-     * Shorthand for:
-     * ```kotlin
-     *   tFlow.observe { effect { ... } }
-     * ```
-     */
-    @ExperimentalFrpApi
-    fun <A> TFlow<A>.observe(
-        coroutineContext: CoroutineContext = EmptyCoroutineContext,
-        block: suspend FrpEffectScope.(A) -> Unit = {},
-    ): Job
-
-    /**
-     * Returns a [TFlow] containing the results of applying each [FrpSpec] emitted from the original
-     * [TFlow], and a [FrpDeferredValue] containing the result of applying [initialSpecs]
-     * immediately.
-     *
-     * When each [FrpSpec] is applied, changes from the previously-active [FrpSpec] with the same
-     * key are undone (any registered [observers][observe] are unregistered, and any pending
-     * [side-effects][effect] are cancelled).
-     *
-     * If the [Maybe] contained within the value for an associated key is [none], then the
-     * previously-active [FrpSpec] will be undone with no replacement.
-     */
-    @ExperimentalFrpApi
-    fun <K, A, B> TFlow<Map<K, Maybe<FrpSpec<A>>>>.applyLatestSpecForKey(
-        initialSpecs: FrpDeferredValue<Map<K, FrpSpec<B>>>,
-        numKeys: Int? = null,
-    ): Pair<TFlow<Map<K, Maybe<A>>>, FrpDeferredValue<Map<K, B>>>
-
-    /**
-     * Creates an instance of a [TFlow] with elements that are from [builder].
-     *
-     * [builder] is run in its own coroutine, allowing for ongoing work that can emit to the
-     * provided [MutableTFlow].
-     *
-     * By default, [builder] is only running while the returned [TFlow] is being
-     * [observed][observe]. If you want it to run at all times, simply add a no-op observer:
-     * ```kotlin
-     * tFlow { ... }.apply { observe() }
-     * ```
-     */
-    @ExperimentalFrpApi fun <T> tFlow(builder: suspend FrpProducerScope<T>.() -> Unit): TFlow<T>
-
-    /**
-     * Creates an instance of a [TFlow] with elements that are emitted from [builder].
-     *
-     * [builder] is run in its own coroutine, allowing for ongoing work that can emit to the
-     * provided [MutableTFlow].
-     *
-     * By default, [builder] is only running while the returned [TFlow] is being
-     * [observed][observe]. If you want it to run at all times, simply add a no-op observer:
-     * ```kotlin
-     * tFlow { ... }.apply { observe() }
-     * ```
-     *
-     * In the event of backpressure, emissions are *coalesced* into batches. When a value is
-     * [emitted][FrpCoalescingProducerScope.emit] from [builder], it is merged into the batch via
-     * [coalesce]. Once the batch is consumed by the frp network in the next transaction, the batch
-     * is reset back to [getInitialValue].
-     */
-    @ExperimentalFrpApi
-    fun <In, Out> coalescingTFlow(
-        getInitialValue: () -> Out,
-        coalesce: (old: Out, new: In) -> Out,
-        builder: suspend FrpCoalescingProducerScope<In>.() -> Unit,
-    ): TFlow<Out>
-
-    /**
-     * Creates a new [FrpBuildScope] that is a child of this one.
-     *
-     * This new scope can be manually cancelled via the returned [Job], or will be cancelled
-     * automatically when its parent is cancelled. Cancellation will unregister all
-     * [observers][observe] and cancel all scheduled [effects][effect].
-     *
-     * The return value from [block] can be accessed via the returned [FrpDeferredValue].
-     */
-    @ExperimentalFrpApi fun <A> asyncScope(block: FrpSpec<A>): Pair<FrpDeferredValue<A>, Job>
-
-    // TODO: once we have context params, these can all become extensions:
-
-    /**
-     * Returns a [TFlow] containing the results of applying the given [transform] function to each
-     * value of the original [TFlow].
-     *
-     * Unlike [TFlow.map], [transform] can perform arbitrary asynchronous code. This code is run
-     * outside of the current FRP transaction; when [transform] returns, the returned value is
-     * emitted from the result [TFlow] in a new transaction.
-     *
-     * Shorthand for:
-     * ```kotlin
-     * tflow.mapLatestBuild { a -> asyncTFlow { transform(a) } }.flatten()
-     * ```
-     */
-    @ExperimentalFrpApi
-    fun <A, B> TFlow<A>.mapAsyncLatest(transform: suspend (A) -> B): TFlow<B> =
-        mapLatestBuild { a -> asyncTFlow { transform(a) } }.flatten()
-
-    /**
-     * Invokes [block] whenever this [TFlow] emits a value. [block] receives an [FrpBuildScope] that
-     * can be used to make further modifications to the FRP network, and/or perform side-effects via
-     * [effect].
-     *
-     * @see observe
-     */
-    @ExperimentalFrpApi
-    fun <A> TFlow<A>.observeBuild(block: suspend FrpBuildScope.(A) -> Unit = {}): Job =
-        mapBuild(block).observe()
-
-    /**
-     * Returns a [StateFlow] whose [value][StateFlow.value] tracks the current
-     * [value of this TState][TState.sample], and will emit at the same rate as
-     * [TState.stateChanges].
-     *
-     * Note that the [value][StateFlow.value] is not available until the *end* of the current
-     * transaction. If you need the current value before this time, then use [TState.sample].
-     */
-    @ExperimentalFrpApi
-    fun <A> TState<A>.toStateFlow(): StateFlow<A> {
-        val uninitialized = Any()
-        var initialValue: Any? = uninitialized
-        val innerStateFlow = MutableStateFlow<Any?>(uninitialized)
-        deferredBuildScope {
-            initialValue = sample()
-            stateChanges.observe {
-                innerStateFlow.value = it
-                initialValue = null
-            }
-        }
-
-        @Suppress("UNCHECKED_CAST")
-        fun getValue(innerValue: Any?): A =
-            when {
-                innerValue !== uninitialized -> innerValue as A
-                initialValue !== uninitialized -> initialValue as A
-                else ->
-                    error(
-                        "Attempted to access StateFlow.value before FRP transaction has completed."
-                    )
-            }
-
-        return object : StateFlow<A> {
-            override val replayCache: List<A>
-                get() = innerStateFlow.replayCache.map(::getValue)
-
-            override val value: A
-                get() = getValue(innerStateFlow.value)
-
-            override suspend fun collect(collector: FlowCollector<A>): Nothing {
-                innerStateFlow.collect { collector.emit(getValue(it)) }
-            }
-        }
-    }
-
-    /**
-     * Returns a [SharedFlow] configured with a replay cache of size [replay] that emits the current
-     * [value][TState.sample] of this [TState] followed by all [stateChanges].
-     */
-    @ExperimentalFrpApi
-    fun <A> TState<A>.toSharedFlow(replay: Int = 0): SharedFlow<A> {
-        val result = MutableSharedFlow<A>(replay, extraBufferCapacity = 1)
-        deferredBuildScope {
-            result.tryEmit(sample())
-            stateChanges.observe { a -> result.tryEmit(a) }
-        }
-        return result
-    }
-
-    /**
-     * Returns a [SharedFlow] configured with a replay cache of size [replay] that emits values
-     * whenever this [TFlow] emits.
-     */
-    @ExperimentalFrpApi
-    fun <A> TFlow<A>.toSharedFlow(replay: Int = 0): SharedFlow<A> {
-        val result = MutableSharedFlow<A>(replay, extraBufferCapacity = 1)
-        observe { a -> result.tryEmit(a) }
-        return result
-    }
-
-    /**
-     * Returns a [TState] that holds onto the value returned by applying the most recently emitted
-     * [FrpSpec] from the original [TFlow], or the value returned by applying [initialSpec] if
-     * nothing has been emitted since it was constructed.
-     *
-     * When each [FrpSpec] is applied, changes from the previously-active [FrpSpec] are undone (any
-     * registered [observers][observe] are unregistered, and any pending [side-effects][effect] are
-     * cancelled).
-     */
-    @ExperimentalFrpApi
-    fun <A> TFlow<FrpSpec<A>>.holdLatestSpec(initialSpec: FrpSpec<A>): TState<A> {
-        val (changes: TFlow<A>, initApplied: FrpDeferredValue<A>) = applyLatestSpec(initialSpec)
-        return changes.holdDeferred(initApplied)
-    }
-
-    /**
-     * Returns a [TState] containing the value returned by applying the [FrpSpec] held by the
-     * original [TState].
-     *
-     * When each [FrpSpec] is applied, changes from the previously-active [FrpSpec] are undone (any
-     * registered [observers][observe] are unregistered, and any pending [side-effects][effect] are
-     * cancelled).
-     */
-    @ExperimentalFrpApi
-    fun <A> TState<FrpSpec<A>>.applyLatestSpec(): TState<A> {
-        val (appliedChanges: TFlow<A>, init: FrpDeferredValue<A>) =
-            stateChanges.applyLatestSpec(frpSpec { sample().applySpec() })
-        return appliedChanges.holdDeferred(init)
-    }
-
-    /**
-     * Returns a [TFlow] containing the results of applying each [FrpSpec] emitted from the original
-     * [TFlow].
-     *
-     * When each [FrpSpec] is applied, changes from the previously-active [FrpSpec] are undone (any
-     * registered [observers][observe] are unregistered, and any pending [side-effects][effect] are
-     * cancelled).
-     */
-    @ExperimentalFrpApi
-    fun <A> TFlow<FrpSpec<A>>.applyLatestSpec(): TFlow<A> = applyLatestSpec(frpSpec {}).first
-
-    /**
-     * Returns a [TFlow] that switches to a new [TFlow] produced by [transform] every time the
-     * original [TFlow] emits a value.
-     *
-     * [transform] can perform modifications to the FRP network via its [FrpBuildScope] receiver.
-     * When the original [TFlow] emits a new value, those changes are undone (any registered
-     * [observers][observe] are unregistered, and any pending [effects][effect] are cancelled).
-     */
-    @ExperimentalFrpApi
-    fun <A, B> TFlow<A>.flatMapLatestBuild(
-        transform: suspend FrpBuildScope.(A) -> TFlow<B>
-    ): TFlow<B> = mapCheap { frpSpec { transform(it) } }.applyLatestSpec().flatten()
-
-    /**
-     * Returns a [TState] by applying [transform] to the value held by the original [TState].
-     *
-     * [transform] can perform modifications to the FRP network via its [FrpBuildScope] receiver.
-     * When the value held by the original [TState] changes, those changes are undone (any
-     * registered [observers][observe] are unregistered, and any pending [effects][effect] are
-     * cancelled).
-     */
-    @ExperimentalFrpApi
-    fun <A, B> TState<A>.flatMapLatestBuild(
-        transform: suspend FrpBuildScope.(A) -> TState<B>
-    ): TState<B> = mapLatestBuild { transform(it) }.flatten()
-
-    /**
-     * Returns a [TState] that transforms the value held inside this [TState] by applying it to the
-     * [transform].
-     *
-     * [transform] can perform modifications to the FRP network via its [FrpBuildScope] receiver.
-     * When the value held by the original [TState] changes, those changes are undone (any
-     * registered [observers][observe] are unregistered, and any pending [effects][effect] are
-     * cancelled).
-     */
-    @ExperimentalFrpApi
-    fun <A, B> TState<A>.mapLatestBuild(transform: suspend FrpBuildScope.(A) -> B): TState<B> =
-        mapCheapUnsafe { frpSpec { transform(it) } }.applyLatestSpec()
-
-    /**
-     * Returns a [TFlow] containing the results of applying each [FrpSpec] emitted from the original
-     * [TFlow], and a [FrpDeferredValue] containing the result of applying [initialSpec]
-     * immediately.
-     *
-     * When each [FrpSpec] is applied, changes from the previously-active [FrpSpec] are undone (any
-     * registered [observers][observe] are unregistered, and any pending [side-effects][effect] are
-     * cancelled).
-     */
-    @ExperimentalFrpApi
-    fun <A : Any?, B> TFlow<FrpSpec<B>>.applyLatestSpec(
-        initialSpec: FrpSpec<A>
-    ): Pair<TFlow<B>, FrpDeferredValue<A>> {
-        val (flow, result) =
-            mapCheap { spec -> mapOf(Unit to just(spec)) }
-                .applyLatestSpecForKey(initialSpecs = mapOf(Unit to initialSpec), numKeys = 1)
-        val outFlow: TFlow<B> =
-            flow.mapMaybe {
-                checkNotNull(it[Unit]) { "applyLatest: expected result, but none present in: $it" }
-            }
-        val outInit: FrpDeferredValue<A> = deferredBuildScope {
-            val initResult: Map<Unit, A> = result.get()
-            check(Unit in initResult) {
-                "applyLatest: expected initial result, but none present in: $initResult"
-            }
-            @Suppress("UNCHECKED_CAST")
-            initResult.getOrDefault(Unit) { null } as A
-        }
-        return Pair(outFlow, outInit)
-    }
-
-    /**
-     * Returns a [TFlow] containing the results of applying [transform] to each value of the
-     * original [TFlow].
-     *
-     * [transform] can perform modifications to the FRP network via its [FrpBuildScope] receiver.
-     * With each invocation of [transform], changes from the previous invocation are undone (any
-     * registered [observers][observe] are unregistered, and any pending [side-effects][effect] are
-     * cancelled).
-     */
-    @ExperimentalFrpApi
-    fun <A, B> TFlow<A>.mapLatestBuild(transform: suspend FrpBuildScope.(A) -> B): TFlow<B> =
-        mapCheap { frpSpec { transform(it) } }.applyLatestSpec()
-
-    /**
-     * Returns a [TFlow] containing the results of applying [transform] to each value of the
-     * original [TFlow], and a [FrpDeferredValue] containing the result of applying [transform] to
-     * [initialValue] immediately.
-     *
-     * [transform] can perform modifications to the FRP network via its [FrpBuildScope] receiver.
-     * With each invocation of [transform], changes from the previous invocation are undone (any
-     * registered [observers][observe] are unregistered, and any pending [side-effects][effect] are
-     * cancelled).
-     */
-    @ExperimentalFrpApi
-    fun <A, B> TFlow<A>.mapLatestBuild(
-        initialValue: A,
-        transform: suspend FrpBuildScope.(A) -> B,
-    ): Pair<TFlow<B>, FrpDeferredValue<B>> =
-        mapLatestBuildDeferred(deferredOf(initialValue), transform)
-
-    /**
-     * Returns a [TFlow] containing the results of applying [transform] to each value of the
-     * original [TFlow], and a [FrpDeferredValue] containing the result of applying [transform] to
-     * [initialValue] immediately.
-     *
-     * [transform] can perform modifications to the FRP network via its [FrpBuildScope] receiver.
-     * With each invocation of [transform], changes from the previous invocation are undone (any
-     * registered [observers][observe] are unregistered, and any pending [side-effects][effect] are
-     * cancelled).
-     */
-    @ExperimentalFrpApi
-    fun <A, B> TFlow<A>.mapLatestBuildDeferred(
-        initialValue: FrpDeferredValue<A>,
-        transform: suspend FrpBuildScope.(A) -> B,
-    ): Pair<TFlow<B>, FrpDeferredValue<B>> =
-        mapCheap { frpSpec { transform(it) } }
-            .applyLatestSpec(initialSpec = frpSpec { transform(initialValue.get()) })
-
-    /**
-     * Returns a [TFlow] containing the results of applying each [FrpSpec] emitted from the original
-     * [TFlow], and a [FrpDeferredValue] containing the result of applying [initialSpecs]
-     * immediately.
-     *
-     * When each [FrpSpec] is applied, changes from the previously-active [FrpSpec] with the same
-     * key are undone (any registered [observers][observe] are unregistered, and any pending
-     * [side-effects][effect] are cancelled).
-     *
-     * If the [Maybe] contained within the value for an associated key is [none], then the
-     * previously-active [FrpSpec] will be undone with no replacement.
-     */
-    @ExperimentalFrpApi
-    fun <K, A, B> TFlow<Map<K, Maybe<FrpSpec<A>>>>.applyLatestSpecForKey(
-        initialSpecs: Map<K, FrpSpec<B>>,
-        numKeys: Int? = null,
-    ): Pair<TFlow<Map<K, Maybe<A>>>, FrpDeferredValue<Map<K, B>>> =
-        applyLatestSpecForKey(deferredOf(initialSpecs), numKeys)
-
-    /**
-     * Returns a [TFlow] containing the results of applying each [FrpSpec] emitted from the original
-     * [TFlow].
-     *
-     * When each [FrpSpec] is applied, changes from the previously-active [FrpSpec] with the same
-     * key are undone (any registered [observers][observe] are unregistered, and any pending
-     * [side-effects][effect] are cancelled).
-     *
-     * If the [Maybe] contained within the value for an associated key is [none], then the
-     * previously-active [FrpSpec] will be undone with no replacement.
-     */
-    @ExperimentalFrpApi
-    fun <K, A> TFlow<Map<K, Maybe<FrpSpec<A>>>>.applyLatestSpecForKey(
-        numKeys: Int? = null
-    ): TFlow<Map<K, Maybe<A>>> =
-        applyLatestSpecForKey<K, A, Nothing>(deferredOf(emptyMap()), numKeys).first
-
-    /**
-     * Returns a [TState] containing the latest results of applying each [FrpSpec] emitted from the
-     * original [TFlow].
-     *
-     * When each [FrpSpec] is applied, changes from the previously-active [FrpSpec] with the same
-     * key are undone (any registered [observers][observe] are unregistered, and any pending
-     * [side-effects][effect] are cancelled).
-     *
-     * If the [Maybe] contained within the value for an associated key is [none], then the
-     * previously-active [FrpSpec] will be undone with no replacement.
-     */
-    @ExperimentalFrpApi
-    fun <K, A> TFlow<Map<K, Maybe<FrpSpec<A>>>>.holdLatestSpecForKey(
-        initialSpecs: FrpDeferredValue<Map<K, FrpSpec<A>>>,
-        numKeys: Int? = null,
-    ): TState<Map<K, A>> {
-        val (changes, initialValues) = applyLatestSpecForKey(initialSpecs, numKeys)
-        return changes.foldMapIncrementally(initialValues)
-    }
-
-    /**
-     * Returns a [TState] containing the latest results of applying each [FrpSpec] emitted from the
-     * original [TFlow].
-     *
-     * When each [FrpSpec] is applied, changes from the previously-active [FrpSpec] with the same
-     * key are undone (any registered [observers][observe] are unregistered, and any pending
-     * [side-effects][effect] are cancelled).
-     *
-     * If the [Maybe] contained within the value for an associated key is [none], then the
-     * previously-active [FrpSpec] will be undone with no replacement.
-     */
-    @ExperimentalFrpApi
-    fun <K, A> TFlow<Map<K, Maybe<FrpSpec<A>>>>.holdLatestSpecForKey(
-        initialSpecs: Map<K, FrpSpec<A>> = emptyMap(),
-        numKeys: Int? = null,
-    ): TState<Map<K, A>> = holdLatestSpecForKey(deferredOf(initialSpecs), numKeys)
-
-    /**
-     * Returns a [TFlow] containing the results of applying [transform] to each value of the
-     * original [TFlow], and a [FrpDeferredValue] containing the result of applying [transform] to
-     * [initialValues] immediately.
-     *
-     * [transform] can perform modifications to the FRP network via its [FrpBuildScope] receiver.
-     * With each invocation of [transform], changes from the previous invocation are undone (any
-     * registered [observers][observe] are unregistered, and any pending [side-effects][effect] are
-     * cancelled).
-     *
-     * If the [Maybe] contained within the value for an associated key is [none], then the
-     * previously-active [FrpBuildScope] will be undone with no replacement.
-     */
-    @ExperimentalFrpApi
-    fun <K, A, B> TFlow<Map<K, Maybe<A>>>.mapLatestBuildForKey(
-        initialValues: FrpDeferredValue<Map<K, A>>,
-        numKeys: Int? = null,
-        transform: suspend FrpBuildScope.(A) -> B,
-    ): Pair<TFlow<Map<K, Maybe<B>>>, FrpDeferredValue<Map<K, B>>> =
-        map { patch -> patch.mapValues { (_, v) -> v.map { frpSpec { transform(it) } } } }
-            .applyLatestSpecForKey(
-                deferredBuildScope {
-                    initialValues.get().mapValues { (_, v) -> frpSpec { transform(v) } }
-                },
-                numKeys = numKeys,
-            )
-
-    /**
-     * Returns a [TFlow] containing the results of applying [transform] to each value of the
-     * original [TFlow], and a [FrpDeferredValue] containing the result of applying [transform] to
-     * [initialValues] immediately.
-     *
-     * [transform] can perform modifications to the FRP network via its [FrpBuildScope] receiver.
-     * With each invocation of [transform], changes from the previous invocation are undone (any
-     * registered [observers][observe] are unregistered, and any pending [side-effects][effect] are
-     * cancelled).
-     *
-     * If the [Maybe] contained within the value for an associated key is [none], then the
-     * previously-active [FrpBuildScope] will be undone with no replacement.
-     */
-    @ExperimentalFrpApi
-    fun <K, A, B> TFlow<Map<K, Maybe<A>>>.mapLatestBuildForKey(
-        initialValues: Map<K, A>,
-        numKeys: Int? = null,
-        transform: suspend FrpBuildScope.(A) -> B,
-    ): Pair<TFlow<Map<K, Maybe<B>>>, FrpDeferredValue<Map<K, B>>> =
-        mapLatestBuildForKey(deferredOf(initialValues), numKeys, transform)
-
-    /**
-     * Returns a [TFlow] containing the results of applying [transform] to each value of the
-     * original [TFlow].
-     *
-     * [transform] can perform modifications to the FRP network via its [FrpBuildScope] receiver.
-     * With each invocation of [transform], changes from the previous invocation are undone (any
-     * registered [observers][observe] are unregistered, and any pending [side-effects][effect] are
-     * cancelled).
-     *
-     * If the [Maybe] contained within the value for an associated key is [none], then the
-     * previously-active [FrpBuildScope] will be undone with no replacement.
-     */
-    @ExperimentalFrpApi
-    fun <K, A, B> TFlow<Map<K, Maybe<A>>>.mapLatestBuildForKey(
-        numKeys: Int? = null,
-        transform: suspend FrpBuildScope.(A) -> B,
-    ): TFlow<Map<K, Maybe<B>>> = mapLatestBuildForKey(emptyMap(), numKeys, transform).first
-
-    /** Returns a [Deferred] containing the next value to be emitted from this [TFlow]. */
-    @ExperimentalFrpApi
-    fun <R> TFlow<R>.nextDeferred(): Deferred<R> {
-        lateinit var next: CompletableDeferred<R>
-        val job = nextOnly().observe { next.complete(it) }
-        next = CompletableDeferred<R>(parent = job)
-        return next
-    }
-
-    /** Returns a [TState] that reflects the [StateFlow.value] of this [StateFlow]. */
-    @ExperimentalFrpApi
-    fun <A> StateFlow<A>.toTState(): TState<A> {
-        val initial = value
-        return tFlow { dropWhile { it == initial }.collect { emit(it) } }.hold(initial)
-    }
-
-    /** Returns a [TFlow] that emits whenever this [Flow] emits. */
-    @ExperimentalFrpApi fun <A> Flow<A>.toTFlow(): TFlow<A> = tFlow { collect { emit(it) } }
-
-    /**
-     * Shorthand for:
-     * ```kotlin
-     * flow.toTFlow().hold(initialValue)
-     * ```
-     */
-    @ExperimentalFrpApi
-    fun <A> Flow<A>.toTState(initialValue: A): TState<A> = toTFlow().hold(initialValue)
-
-    /**
-     * Shorthand for:
-     * ```kotlin
-     * flow.scan(initialValue, operation).toTFlow().hold(initialValue)
-     * ```
-     */
-    @ExperimentalFrpApi
-    fun <A, B> Flow<A>.scanToTState(initialValue: B, operation: (B, A) -> B): TState<B> =
-        scan(initialValue, operation).toTFlow().hold(initialValue)
-
-    /**
-     * Shorthand for:
-     * ```kotlin
-     * flow.scan(initialValue) { a, f -> f(a) }.toTFlow().hold(initialValue)
-     * ```
-     */
-    @ExperimentalFrpApi
-    fun <A> Flow<(A) -> A>.scanToTState(initialValue: A): TState<A> =
-        scanToTState(initialValue) { a, f -> f(a) }
-
-    /**
-     * Invokes [block] whenever this [TFlow] emits a value. [block] receives an [FrpBuildScope] that
-     * can be used to make further modifications to the FRP network, and/or perform side-effects via
-     * [effect].
-     *
-     * With each invocation of [block], changes from the previous invocation are undone (any
-     * registered [observers][observe] are unregistered, and any pending [side-effects][effect] are
-     * cancelled).
-     */
-    @ExperimentalFrpApi
-    fun <A> TFlow<A>.observeLatestBuild(block: suspend FrpBuildScope.(A) -> Unit = {}): Job =
-        mapLatestBuild { block(it) }.observe()
-
-    /**
-     * Invokes [block] whenever this [TFlow] emits a value, allowing side-effects to be safely
-     * performed in reaction to the emission.
-     *
-     * With each invocation of [block], running effects from the previous invocation are cancelled.
-     */
-    @ExperimentalFrpApi
-    fun <A> TFlow<A>.observeLatest(block: suspend FrpEffectScope.(A) -> Unit = {}): Job {
-        var innerJob: Job? = null
-        return observeBuild {
-            innerJob?.cancel()
-            innerJob = effect { block(it) }
-        }
-    }
-
-    /**
-     * Invokes [block] with the value held by this [TState], allowing side-effects to be safely
-     * performed in reaction to the state changing.
-     *
-     * With each invocation of [block], running effects from the previous invocation are cancelled.
-     */
-    @ExperimentalFrpApi
-    fun <A> TState<A>.observeLatest(block: suspend FrpEffectScope.(A) -> Unit = {}): Job =
-        launchScope {
-            var innerJob = effect { block(sample()) }
-            stateChanges.observeBuild {
-                innerJob.cancel()
-                innerJob = effect { block(it) }
-            }
-        }
-
-    /**
-     * Applies [block] to the value held by this [TState]. [block] receives an [FrpBuildScope] that
-     * can be used to make further modifications to the FRP network, and/or perform side-effects via
-     * [effect].
-     *
-     * [block] can perform modifications to the FRP network via its [FrpBuildScope] receiver. With
-     * each invocation of [block], changes from the previous invocation are undone (any registered
-     * [observers][observe] are unregistered, and any pending [side-effects][effect] are cancelled).
-     */
-    @ExperimentalFrpApi
-    fun <A> TState<A>.observeLatestBuild(block: suspend FrpBuildScope.(A) -> Unit = {}): Job =
-        launchScope {
-            var innerJob: Job = launchScope { block(sample()) }
-            stateChanges.observeBuild {
-                innerJob.cancel()
-                innerJob = launchScope { block(it) }
-            }
-        }
-
-    /** Applies the [FrpSpec] within this [FrpBuildScope]. */
-    @ExperimentalFrpApi suspend fun <A> FrpSpec<A>.applySpec(): A = this()
-
-    /**
-     * Applies the [FrpSpec] within this [FrpBuildScope], returning the result as an
-     * [FrpDeferredValue].
-     */
-    @ExperimentalFrpApi
-    fun <A> FrpSpec<A>.applySpecDeferred(): FrpDeferredValue<A> = deferredBuildScope { applySpec() }
-
-    /**
-     * Invokes [block] on the value held in this [TState]. [block] receives an [FrpBuildScope] that
-     * can be used to make further modifications to the FRP network, and/or perform side-effects via
-     * [effect].
-     */
-    @ExperimentalFrpApi
-    fun <A> TState<A>.observeBuild(block: suspend FrpBuildScope.(A) -> Unit = {}): Job =
-        launchScope {
-            block(sample())
-            stateChanges.observeBuild(block)
-        }
-
-    /**
-     * Invokes [block] with the current value of this [TState], re-invoking whenever it changes,
-     * allowing side-effects to be safely performed in reaction value changing.
-     *
-     * Specifically, [block] is deferred to the end of the transaction, and is only actually
-     * executed if this [FrpBuildScope] is still active by that time. It can be deactivated due to a
-     * -Latest combinator, for example.
-     *
-     * If the [TState] is changing within the *current* transaction (i.e. [stateChanges] is
-     * presently emitting) then [block] will be invoked for the first time with the new value;
-     * otherwise, it will be invoked with the [current][sample] value.
-     */
-    @ExperimentalFrpApi
-    fun <A> TState<A>.observe(block: suspend FrpEffectScope.(A) -> Unit = {}): Job =
-        now.map { sample() }.mergeWith(stateChanges) { _, new -> new }.observe { block(it) }
-}
-
-/**
- * Returns a [TFlow] that emits the result of [block] once it completes. [block] is evaluated
- * outside of the current FRP transaction; when it completes, the returned [TFlow] emits in a new
- * transaction.
- *
- * Shorthand for:
- * ```
- * tFlow { emitter: MutableTFlow<A> ->
- *     val a = block()
- *     emitter.emit(a)
- * }
- * ```
- */
-@ExperimentalFrpApi
-fun <A> FrpBuildScope.asyncTFlow(block: suspend () -> A): TFlow<A> =
-    tFlow {
-            // TODO: if block completes synchronously, it would be nice to emit within this
-            //  transaction
-            emit(block())
-        }
-        .apply { observe() }
-
-/**
- * Performs a side-effect in a safe manner w/r/t the current FRP transaction.
- *
- * Specifically, [block] is deferred to the end of the current transaction, and is only actually
- * executed if this [FrpBuildScope] is still active by that time. It can be deactivated due to a
- * -Latest combinator, for example.
- *
- * Shorthand for:
- * ```kotlin
- *   now.observe { block() }
- * ```
- */
-@ExperimentalFrpApi
-fun FrpBuildScope.effect(block: suspend FrpEffectScope.() -> Unit): Job = now.observe { block() }
-
-/**
- * Launches [block] in a new coroutine, returning a [Job] bound to the coroutine.
- *
- * This coroutine is not actually started until the *end* of the current FRP transaction. This is
- * done because the current [FrpBuildScope] might be deactivated within this transaction, perhaps
- * due to a -Latest combinator. If this happens, then the coroutine will never actually be started.
- *
- * Shorthand for:
- * ```kotlin
- *   effect { frpCoroutineScope.launch { block() } }
- * ```
- */
-@ExperimentalFrpApi
-fun FrpBuildScope.launchEffect(block: suspend CoroutineScope.() -> Unit): Job = asyncEffect(block)
-
-/**
- * Launches [block] in a new coroutine, returning the result as a [Deferred].
- *
- * This coroutine is not actually started until the *end* of the current FRP transaction. This is
- * done because the current [FrpBuildScope] might be deactivated within this transaction, perhaps
- * due to a -Latest combinator. If this happens, then the coroutine will never actually be started.
- *
- * Shorthand for:
- * ```kotlin
- *   CompletableDeferred<R>.apply {
- *       effect { frpCoroutineScope.launch { complete(coroutineScope { block() }) } }
- *     }
- *     .await()
- * ```
- */
-@ExperimentalFrpApi
-fun <R> FrpBuildScope.asyncEffect(block: suspend CoroutineScope.() -> R): Deferred<R> {
-    val result = CompletableDeferred<R>()
-    val job = now.observe { frpCoroutineScope.launch { result.complete(coroutineScope(block)) } }
-    val handle = job.invokeOnCompletion { result.cancel() }
-    result.invokeOnCompletion {
-        handle.dispose()
-        job.cancel()
-    }
-    return result
-}
-
-/** Like [FrpBuildScope.asyncScope], but ignores the result of [block]. */
-@ExperimentalFrpApi fun FrpBuildScope.launchScope(block: FrpSpec<*>): Job = asyncScope(block).second
-
-/**
- * Creates an instance of a [TFlow] with elements that are emitted from [builder].
- *
- * [builder] is run in its own coroutine, allowing for ongoing work that can emit to the provided
- * [MutableTFlow].
- *
- * By default, [builder] is only running while the returned [TFlow] is being
- * [observed][FrpBuildScope.observe]. If you want it to run at all times, simply add a no-op
- * observer:
- * ```kotlin
- * tFlow { ... }.apply { observe() }
- * ```
- *
- * In the event of backpressure, emissions are *coalesced* into batches. When a value is
- * [emitted][FrpCoalescingProducerScope.emit] from [builder], it is merged into the batch via
- * [coalesce]. Once the batch is consumed by the FRP network in the next transaction, the batch is
- * reset back to [initialValue].
- */
-@ExperimentalFrpApi
-fun <In, Out> FrpBuildScope.coalescingTFlow(
-    initialValue: Out,
-    coalesce: (old: Out, new: In) -> Out,
-    builder: suspend FrpCoalescingProducerScope<In>.() -> Unit,
-): TFlow<Out> = coalescingTFlow(getInitialValue = { initialValue }, coalesce, builder)
-
-/**
- * Creates an instance of a [TFlow] with elements that are emitted from [builder].
- *
- * [builder] is run in its own coroutine, allowing for ongoing work that can emit to the provided
- * [MutableTFlow].
- *
- * By default, [builder] is only running while the returned [TFlow] is being
- * [observed][FrpBuildScope.observe]. If you want it to run at all times, simply add a no-op
- * observer:
- * ```kotlin
- * tFlow { ... }.apply { observe() }
- * ```
- *
- * In the event of backpressure, emissions are *conflated*; any older emissions are dropped and only
- * the most recent emission will be used when the FRP network is ready.
- */
-@ExperimentalFrpApi
-fun <T> FrpBuildScope.conflatedTFlow(
-    builder: suspend FrpCoalescingProducerScope<T>.() -> Unit
-): TFlow<T> =
-    coalescingTFlow<T, Any?>(initialValue = Any(), coalesce = { _, new -> new }, builder = builder)
-        .mapCheap {
-            @Suppress("UNCHECKED_CAST")
-            it as T
-        }
-
-/** Scope for emitting to a [FrpBuildScope.coalescingTFlow]. */
-interface FrpCoalescingProducerScope<in T> {
-    /**
-     * Inserts [value] into the current batch, enqueueing it for emission from this [TFlow] if not
-     * already pending.
-     *
-     * Backpressure occurs when [emit] is called while the FRP network is currently in a
-     * transaction; if called multiple times, then emissions will be coalesced into a single batch
-     * that is then processed when the network is ready.
-     */
-    fun emit(value: T)
-}
-
-/** Scope for emitting to a [FrpBuildScope.tFlow]. */
-interface FrpProducerScope<in T> {
-    /**
-     * Emits a [value] to this [TFlow], suspending the caller until the FRP transaction containing
-     * the emission has completed.
-     */
-    suspend fun emit(value: T)
-}
-
-/**
- * Suspends forever. Upon cancellation, runs [block]. Useful for unregistering callbacks inside of
- * [FrpBuildScope.tFlow] and [FrpBuildScope.coalescingTFlow].
- */
-suspend fun awaitClose(block: () -> Unit): Nothing =
-    try {
-        awaitCancellation()
-    } finally {
-        block()
-    }
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/FrpEffectScope.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/FrpEffectScope.kt
deleted file mode 100644
index b39dcc1..0000000
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/FrpEffectScope.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.
- */
-
-package com.android.systemui.kairos
-
-import kotlin.coroutines.RestrictsSuspension
-import kotlinx.coroutines.CoroutineScope
-
-/**
- * Scope for external side-effects triggered by the Frp network. This still occurs within the
- * context of a transaction, so general suspending calls are disallowed to prevent blocking the
- * transaction. You can use [frpCoroutineScope] to [launch][kotlinx.coroutines.launch] new
- * coroutines to perform long-running asynchronous work. This scope is alive for the duration of the
- * containing [FrpBuildScope] that this side-effect scope is running in.
- */
-@RestrictsSuspension
-@ExperimentalFrpApi
-interface FrpEffectScope : FrpTransactionScope {
-    /**
-     * A [CoroutineScope] whose lifecycle lives for as long as this [FrpEffectScope] is alive. This
-     * is generally until the [Job][kotlinx.coroutines.Job] returned by [FrpBuildScope.effect] is
-     * cancelled.
-     */
-    @ExperimentalFrpApi val frpCoroutineScope: CoroutineScope
-
-    /**
-     * A [FrpNetwork] instance that can be used to transactionally query / modify the FRP network.
-     *
-     * The lambda passed to [FrpNetwork.transact] on this instance will receive an [FrpBuildScope]
-     * that is lifetime-bound to this [FrpEffectScope]. Once this [FrpEffectScope] is no longer
-     * alive, any modifications to the FRP network performed via this [FrpNetwork] instance will be
-     * undone (any registered [observers][FrpBuildScope.observe] are unregistered, and any pending
-     * [side-effects][FrpBuildScope.effect] are cancelled).
-     */
-    @ExperimentalFrpApi val frpNetwork: FrpNetwork
-}
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/FrpNetwork.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/FrpNetwork.kt
deleted file mode 100644
index 97252b4..0000000
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/FrpNetwork.kt
+++ /dev/null
@@ -1,196 +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.kairos
-
-import com.android.systemui.kairos.internal.BuildScopeImpl
-import com.android.systemui.kairos.internal.Network
-import com.android.systemui.kairos.internal.StateScopeImpl
-import com.android.systemui.kairos.internal.util.awaitCancellationAndThen
-import com.android.systemui.kairos.internal.util.childScope
-import kotlin.coroutines.CoroutineContext
-import kotlin.coroutines.EmptyCoroutineContext
-import kotlin.coroutines.coroutineContext
-import kotlinx.coroutines.CompletableDeferred
-import kotlinx.coroutines.CoroutineName
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.job
-import kotlinx.coroutines.launch
-
-/**
- * Marks declarations that are still **experimental** and shouldn't be used in general production
- * code.
- */
-@RequiresOptIn(
-    message = "This API is experimental and should not be used in general production code."
-)
-@Retention(AnnotationRetention.BINARY)
-annotation class ExperimentalFrpApi
-
-/**
- * External interface to an FRP network. Can be used to make transactional queries and modifications
- * to the network.
- */
-@ExperimentalFrpApi
-interface FrpNetwork {
-    /**
-     * Runs [block] inside of a transaction, suspending until the transaction is complete.
-     *
-     * The [FrpBuildScope] receiver exposes methods that can be used to query or modify the network.
-     * If the network is cancelled while the caller of [transact] is suspended, then the call will
-     * be cancelled.
-     */
-    @ExperimentalFrpApi suspend fun <R> transact(block: suspend FrpTransactionScope.() -> R): R
-
-    /**
-     * Activates [spec] in a transaction, suspending indefinitely. While suspended, all observers
-     * and long-running effects are kept alive. When cancelled, observers are unregistered and
-     * effects are cancelled.
-     */
-    @ExperimentalFrpApi suspend fun activateSpec(spec: FrpSpec<*>)
-
-    /** Returns a [CoalescingMutableTFlow] that can emit values into this [FrpNetwork]. */
-    @ExperimentalFrpApi
-    fun <In, Out> coalescingMutableTFlow(
-        coalesce: (old: Out, new: In) -> Out,
-        getInitialValue: () -> Out,
-    ): CoalescingMutableTFlow<In, Out>
-
-    /** Returns a [MutableTFlow] that can emit values into this [FrpNetwork]. */
-    @ExperimentalFrpApi fun <T> mutableTFlow(): MutableTFlow<T>
-
-    /** Returns a [MutableTState]. with initial state [initialValue]. */
-    @ExperimentalFrpApi
-    fun <T> mutableTStateDeferred(initialValue: FrpDeferredValue<T>): MutableTState<T>
-}
-
-/** Returns a [CoalescingMutableTFlow] that can emit values into this [FrpNetwork]. */
-@ExperimentalFrpApi
-fun <In, Out> FrpNetwork.coalescingMutableTFlow(
-    coalesce: (old: Out, new: In) -> Out,
-    initialValue: Out,
-): CoalescingMutableTFlow<In, Out> =
-    coalescingMutableTFlow(coalesce, getInitialValue = { initialValue })
-
-/** Returns a [MutableTState]. with initial state [initialValue]. */
-@ExperimentalFrpApi
-fun <T> FrpNetwork.mutableTState(initialValue: T): MutableTState<T> =
-    mutableTStateDeferred(deferredOf(initialValue))
-
-/** Returns a [MutableTState]. with initial state [initialValue]. */
-@ExperimentalFrpApi
-fun <T> MutableTState(network: FrpNetwork, initialValue: T): MutableTState<T> =
-    network.mutableTState(initialValue)
-
-/** Returns a [MutableTFlow] that can emit values into this [FrpNetwork]. */
-@ExperimentalFrpApi
-fun <T> MutableTFlow(network: FrpNetwork): MutableTFlow<T> = network.mutableTFlow()
-
-/** Returns a [CoalescingMutableTFlow] that can emit values into this [FrpNetwork]. */
-@ExperimentalFrpApi
-fun <In, Out> CoalescingMutableTFlow(
-    network: FrpNetwork,
-    coalesce: (old: Out, new: In) -> Out,
-    initialValue: Out,
-): CoalescingMutableTFlow<In, Out> = network.coalescingMutableTFlow(coalesce) { initialValue }
-
-/** Returns a [CoalescingMutableTFlow] that can emit values into this [FrpNetwork]. */
-@ExperimentalFrpApi
-fun <In, Out> CoalescingMutableTFlow(
-    network: FrpNetwork,
-    coalesce: (old: Out, new: In) -> Out,
-    getInitialValue: () -> Out,
-): CoalescingMutableTFlow<In, Out> = network.coalescingMutableTFlow(coalesce, getInitialValue)
-
-/**
- * Activates [spec] in a transaction and invokes [block] with the result, suspending indefinitely.
- * While suspended, all observers and long-running effects are kept alive. When cancelled, observers
- * are unregistered and effects are cancelled.
- */
-@ExperimentalFrpApi
-suspend fun <R> FrpNetwork.activateSpec(spec: FrpSpec<R>, block: suspend (R) -> Unit) {
-    activateSpec {
-        val result = spec.applySpec()
-        launchEffect { block(result) }
-    }
-}
-
-internal class LocalFrpNetwork(
-    private val network: Network,
-    private val scope: CoroutineScope,
-    private val endSignal: TFlow<Any>,
-) : FrpNetwork {
-    override suspend fun <R> transact(block: suspend FrpTransactionScope.() -> R): R {
-        val result = CompletableDeferred<R>(coroutineContext[Job])
-        @Suppress("DeferredResultUnused")
-        network.transaction("FrpNetwork.transact") {
-            val buildScope =
-                BuildScopeImpl(
-                    stateScope = StateScopeImpl(evalScope = this, endSignal = endSignal),
-                    coroutineScope = scope,
-                )
-            buildScope.runInBuildScope { effect { result.complete(block()) } }
-        }
-        return result.await()
-    }
-
-    override suspend fun activateSpec(spec: FrpSpec<*>) {
-        val job =
-            network
-                .transaction("FrpNetwork.activateSpec") {
-                    val buildScope =
-                        BuildScopeImpl(
-                            stateScope = StateScopeImpl(evalScope = this, endSignal = endSignal),
-                            coroutineScope = scope,
-                        )
-                    buildScope.runInBuildScope { launchScope(spec) }
-                }
-                .await()
-        awaitCancellationAndThen { job.cancel() }
-    }
-
-    override fun <In, Out> coalescingMutableTFlow(
-        coalesce: (old: Out, new: In) -> Out,
-        getInitialValue: () -> Out,
-    ): CoalescingMutableTFlow<In, Out> =
-        CoalescingMutableTFlow(null, coalesce, network, getInitialValue)
-
-    override fun <T> mutableTFlow(): MutableTFlow<T> = MutableTFlow(network)
-
-    override fun <T> mutableTStateDeferred(initialValue: FrpDeferredValue<T>): MutableTState<T> =
-        MutableTState(network, initialValue.unwrapped)
-}
-
-/**
- * Combination of an [FrpNetwork] and a [Job] that, when cancelled, will cancel the entire FRP
- * network.
- */
-@ExperimentalFrpApi
-class RootFrpNetwork
-internal constructor(private val network: Network, private val scope: CoroutineScope, job: Job) :
-    Job by job, FrpNetwork by LocalFrpNetwork(network, scope, emptyTFlow)
-
-/** Constructs a new [RootFrpNetwork] in the given [CoroutineScope]. */
-@ExperimentalFrpApi
-fun CoroutineScope.newFrpNetwork(
-    context: CoroutineContext = EmptyCoroutineContext
-): RootFrpNetwork {
-    val scope = childScope(context)
-    val network = Network(scope)
-    scope.launch(CoroutineName("newFrpNetwork scheduler")) { network.runInputScheduler() }
-    return RootFrpNetwork(network, scope, scope.coroutineContext.job)
-}
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/FrpScope.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/FrpScope.kt
deleted file mode 100644
index ad6b2c8..0000000
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/FrpScope.kt
+++ /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.
- */
-
-package com.android.systemui.kairos
-
-import kotlin.coroutines.RestrictsSuspension
-import kotlin.coroutines.resume
-import kotlin.coroutines.resumeWithException
-import kotlinx.coroutines.CompletableDeferred
-import kotlinx.coroutines.Deferred
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.suspendCancellableCoroutine
-
-/** Denotes [FrpScope] interfaces as [DSL markers][DslMarker]. */
-@DslMarker annotation class FrpScopeMarker
-
-/**
- * Base scope for all FRP scopes. Used to prevent implicitly capturing other scopes from in lambdas.
- */
-@FrpScopeMarker
-@RestrictsSuspension
-@ExperimentalFrpApi
-interface FrpScope {
-    /**
-     * Returns the value held by the [FrpDeferredValue], suspending until available if necessary.
-     */
-    @ExperimentalFrpApi
-    @OptIn(ExperimentalCoroutinesApi::class)
-    suspend fun <A> FrpDeferredValue<A>.get(): A = suspendCancellableCoroutine { k ->
-        unwrapped.invokeOnCompletion { ex ->
-            ex?.let { k.resumeWithException(ex) } ?: k.resume(unwrapped.getCompleted())
-        }
-    }
-}
-
-/**
- * A value that may not be immediately (synchronously) available, but is guaranteed to be available
- * before this transaction is completed.
- *
- * @see FrpScope.get
- */
-@ExperimentalFrpApi
-class FrpDeferredValue<out A> internal constructor(internal val unwrapped: Deferred<A>)
-
-/**
- * Returns the value held by this [FrpDeferredValue], or throws [IllegalStateException] if it is not
- * yet available.
- *
- * This API is not meant for general usage within the FRP network. It is made available mainly for
- * debugging and logging. You should always prefer [get][FrpScope.get] if possible.
- *
- * @see FrpScope.get
- */
-@ExperimentalFrpApi
-@OptIn(ExperimentalCoroutinesApi::class)
-fun <A> FrpDeferredValue<A>.getUnsafe(): A = unwrapped.getCompleted()
-
-/** Returns an already-available [FrpDeferredValue] containing [value]. */
-@ExperimentalFrpApi
-fun <A> deferredOf(value: A): FrpDeferredValue<A> = FrpDeferredValue(CompletableDeferred(value))
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/FrpStateScope.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/FrpStateScope.kt
deleted file mode 100644
index c7ea680..0000000
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/FrpStateScope.kt
+++ /dev/null
@@ -1,780 +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.kairos
-
-import com.android.systemui.kairos.combine as combinePure
-import com.android.systemui.kairos.map as mapPure
-import com.android.systemui.kairos.util.Just
-import com.android.systemui.kairos.util.Left
-import com.android.systemui.kairos.util.Maybe
-import com.android.systemui.kairos.util.Right
-import com.android.systemui.kairos.util.WithPrev
-import com.android.systemui.kairos.util.just
-import com.android.systemui.kairos.util.map
-import com.android.systemui.kairos.util.none
-import com.android.systemui.kairos.util.partitionEithers
-import com.android.systemui.kairos.util.zipWith
-import kotlin.coroutines.RestrictsSuspension
-
-typealias FrpStateful<R> = suspend FrpStateScope.() -> R
-
-/**
- * Returns a [FrpStateful] that, when [applied][FrpStateScope.applyStateful], invokes [block] with
- * the applier's [FrpStateScope].
- */
-// TODO: caching story? should each Scope have a cache of applied FrpStateful instances?
-@ExperimentalFrpApi
-@Suppress("NOTHING_TO_INLINE")
-inline fun <A> statefully(noinline block: suspend FrpStateScope.() -> A): FrpStateful<A> = block
-
-/**
- * Operations that accumulate state within the FRP network.
- *
- * State accumulation is an ongoing process that has a lifetime. Use `-Latest` combinators, such as
- * [mapLatestStateful], to create smaller, nested lifecycles so that accumulation isn't running
- * longer than needed.
- */
-@ExperimentalFrpApi
-@RestrictsSuspension
-interface FrpStateScope : FrpTransactionScope {
-
-    /** TODO */
-    @ExperimentalFrpApi
-    // TODO: wish this could just be `deferred` but alas
-    fun <A> deferredStateScope(block: suspend FrpStateScope.() -> A): FrpDeferredValue<A>
-
-    /**
-     * Returns a [TState] that holds onto the most recently emitted value from this [TFlow], or
-     * [initialValue] if nothing has been emitted since it was constructed.
-     *
-     * Note that the value contained within the [TState] is not updated until *after* all [TFlow]s
-     * have been processed; this keeps the value of the [TState] consistent during the entire FRP
-     * transaction.
-     */
-    @ExperimentalFrpApi fun <A> TFlow<A>.holdDeferred(initialValue: FrpDeferredValue<A>): TState<A>
-
-    /**
-     * Returns a [TFlow] that emits from a merged, incrementally-accumulated collection of [TFlow]s
-     * emitted from this, following the same "patch" rules as outlined in [foldMapIncrementally].
-     *
-     * Conceptually this is equivalent to:
-     * ```kotlin
-     *   fun <K, V> TFlow<Map<K, Maybe<TFlow<V>>>>.mergeIncrementally(
-     *     initialTFlows: Map<K, TFlow<V>>,
-     *   ): TFlow<Map<K, V>> =
-     *     foldMapIncrementally(initialTFlows).map { it.merge() }.switch()
-     * ```
-     *
-     * While the behavior is equivalent to the conceptual definition above, the implementation is
-     * significantly more efficient.
-     *
-     * @see merge
-     */
-    @ExperimentalFrpApi
-    fun <K : Any, V> TFlow<Map<K, Maybe<TFlow<V>>>>.mergeIncrementally(
-        initialTFlows: FrpDeferredValue<Map<K, TFlow<V>>>
-    ): TFlow<Map<K, V>>
-
-    /**
-     * Returns a [TFlow] that emits from a merged, incrementally-accumulated collection of [TFlow]s
-     * emitted from this, following the same "patch" rules as outlined in [foldMapIncrementally].
-     *
-     * Conceptually this is equivalent to:
-     * ```kotlin
-     *   fun <K, V> TFlow<Map<K, Maybe<TFlow<V>>>>.mergeIncrementallyPrompt(
-     *     initialTFlows: Map<K, TFlow<V>>,
-     *   ): TFlow<Map<K, V>> =
-     *     foldMapIncrementally(initialTFlows).map { it.merge() }.switchPromptly()
-     * ```
-     *
-     * While the behavior is equivalent to the conceptual definition above, the implementation is
-     * significantly more efficient.
-     *
-     * @see merge
-     */
-    @ExperimentalFrpApi
-    fun <K : Any, V> TFlow<Map<K, Maybe<TFlow<V>>>>.mergeIncrementallyPromptly(
-        initialTFlows: FrpDeferredValue<Map<K, TFlow<V>>>
-    ): TFlow<Map<K, V>>
-
-    // TODO: everything below this comment can be made into extensions once we have context params
-
-    /**
-     * Returns a [TFlow] that emits from a merged, incrementally-accumulated collection of [TFlow]s
-     * emitted from this, following the same "patch" rules as outlined in [foldMapIncrementally].
-     *
-     * Conceptually this is equivalent to:
-     * ```kotlin
-     *   fun <K, V> TFlow<Map<K, Maybe<TFlow<V>>>>.mergeIncrementally(
-     *     initialTFlows: Map<K, TFlow<V>>,
-     *   ): TFlow<Map<K, V>> =
-     *     foldMapIncrementally(initialTFlows).map { it.merge() }.switch()
-     * ```
-     *
-     * While the behavior is equivalent to the conceptual definition above, the implementation is
-     * significantly more efficient.
-     *
-     * @see merge
-     */
-    @ExperimentalFrpApi
-    fun <K : Any, V> TFlow<Map<K, Maybe<TFlow<V>>>>.mergeIncrementally(
-        initialTFlows: Map<K, TFlow<V>> = emptyMap()
-    ): TFlow<Map<K, V>> = mergeIncrementally(deferredOf(initialTFlows))
-
-    /**
-     * Returns a [TFlow] that emits from a merged, incrementally-accumulated collection of [TFlow]s
-     * emitted from this, following the same "patch" rules as outlined in [foldMapIncrementally].
-     *
-     * Conceptually this is equivalent to:
-     * ```kotlin
-     *   fun <K, V> TFlow<Map<K, Maybe<TFlow<V>>>>.mergeIncrementallyPrompt(
-     *     initialTFlows: Map<K, TFlow<V>>,
-     *   ): TFlow<Map<K, V>> =
-     *     foldMapIncrementally(initialTFlows).map { it.merge() }.switchPromptly()
-     * ```
-     *
-     * While the behavior is equivalent to the conceptual definition above, the implementation is
-     * significantly more efficient.
-     *
-     * @see merge
-     */
-    @ExperimentalFrpApi
-    fun <K : Any, V> TFlow<Map<K, Maybe<TFlow<V>>>>.mergeIncrementallyPromptly(
-        initialTFlows: Map<K, TFlow<V>> = emptyMap()
-    ): TFlow<Map<K, V>> = mergeIncrementallyPromptly(deferredOf(initialTFlows))
-
-    /** Applies the [FrpStateful] within this [FrpStateScope]. */
-    @ExperimentalFrpApi suspend fun <A> FrpStateful<A>.applyStateful(): A = this()
-
-    /**
-     * Applies the [FrpStateful] within this [FrpStateScope], returning the result as an
-     * [FrpDeferredValue].
-     */
-    @ExperimentalFrpApi
-    fun <A> FrpStateful<A>.applyStatefulDeferred(): FrpDeferredValue<A> = deferredStateScope {
-        applyStateful()
-    }
-
-    /**
-     * Returns a [TState] that holds onto the most recently emitted value from this [TFlow], or
-     * [initialValue] if nothing has been emitted since it was constructed.
-     *
-     * Note that the value contained within the [TState] is not updated until *after* all [TFlow]s
-     * have been processed; this keeps the value of the [TState] consistent during the entire FRP
-     * transaction.
-     */
-    @ExperimentalFrpApi
-    fun <A> TFlow<A>.hold(initialValue: A): TState<A> = holdDeferred(deferredOf(initialValue))
-
-    /**
-     * Returns a [TFlow] the emits the result of applying [FrpStatefuls][FrpStateful] emitted from
-     * the original [TFlow].
-     *
-     * Unlike [applyLatestStateful], state accumulation is not stopped with each subsequent emission
-     * of the original [TFlow].
-     */
-    @ExperimentalFrpApi fun <A> TFlow<FrpStateful<A>>.applyStatefuls(): TFlow<A>
-
-    /**
-     * Returns a [TFlow] containing the results of applying [transform] to each value of the
-     * original [TFlow].
-     *
-     * [transform] can perform state accumulation via its [FrpStateScope] receiver. Unlike
-     * [mapLatestStateful], accumulation is not stopped with each subsequent emission of the
-     * original [TFlow].
-     */
-    @ExperimentalFrpApi
-    fun <A, B> TFlow<A>.mapStateful(transform: suspend FrpStateScope.(A) -> B): TFlow<B> =
-        mapPure { statefully { transform(it) } }.applyStatefuls()
-
-    /**
-     * Returns a [TState] the holds the result of applying the [FrpStateful] held by the original
-     * [TState].
-     *
-     * Unlike [applyLatestStateful], state accumulation is not stopped with each state change.
-     */
-    @ExperimentalFrpApi
-    fun <A> TState<FrpStateful<A>>.applyStatefuls(): TState<A> =
-        stateChanges
-            .applyStatefuls()
-            .holdDeferred(initialValue = deferredStateScope { sampleDeferred().get()() })
-
-    /** Returns a [TFlow] that switches to the [TFlow] emitted by the original [TFlow]. */
-    @ExperimentalFrpApi fun <A> TFlow<TFlow<A>>.flatten() = hold(emptyTFlow).switch()
-
-    /**
-     * Returns a [TFlow] containing the results of applying [transform] to each value of the
-     * original [TFlow].
-     *
-     * [transform] can perform state accumulation via its [FrpStateScope] receiver. With each
-     * invocation of [transform], state accumulation from previous invocation is stopped.
-     */
-    @ExperimentalFrpApi
-    fun <A, B> TFlow<A>.mapLatestStateful(transform: suspend FrpStateScope.(A) -> B): TFlow<B> =
-        mapPure { statefully { transform(it) } }.applyLatestStateful()
-
-    /**
-     * Returns a [TFlow] that switches to a new [TFlow] produced by [transform] every time the
-     * original [TFlow] emits a value.
-     *
-     * [transform] can perform state accumulation via its [FrpStateScope] receiver. With each
-     * invocation of [transform], state accumulation from previous invocation is stopped.
-     */
-    @ExperimentalFrpApi
-    fun <A, B> TFlow<A>.flatMapLatestStateful(
-        transform: suspend FrpStateScope.(A) -> TFlow<B>
-    ): TFlow<B> = mapLatestStateful(transform).flatten()
-
-    /**
-     * Returns a [TFlow] containing the results of applying each [FrpStateful] emitted from the
-     * original [TFlow].
-     *
-     * When each [FrpStateful] is applied, state accumulation from the previously-active
-     * [FrpStateful] is stopped.
-     */
-    @ExperimentalFrpApi
-    fun <A> TFlow<FrpStateful<A>>.applyLatestStateful(): TFlow<A> = applyLatestStateful {}.first
-
-    /**
-     * Returns a [TState] containing the value returned by applying the [FrpStateful] held by the
-     * original [TState].
-     *
-     * When each [FrpStateful] is applied, state accumulation from the previously-active
-     * [FrpStateful] is stopped.
-     */
-    @ExperimentalFrpApi
-    fun <A> TState<FrpStateful<A>>.applyLatestStateful(): TState<A> {
-        val (changes, init) = stateChanges.applyLatestStateful { sample()() }
-        return changes.holdDeferred(init)
-    }
-
-    /**
-     * Returns a [TFlow] containing the results of applying each [FrpStateful] emitted from the
-     * original [TFlow], and a [FrpDeferredValue] containing the result of applying [init]
-     * immediately.
-     *
-     * When each [FrpStateful] is applied, state accumulation from the previously-active
-     * [FrpStateful] is stopped.
-     */
-    @ExperimentalFrpApi
-    fun <A, B> TFlow<FrpStateful<B>>.applyLatestStateful(
-        init: FrpStateful<A>
-    ): Pair<TFlow<B>, FrpDeferredValue<A>> {
-        val (flow, result) =
-            mapCheap { spec -> mapOf(Unit to just(spec)) }
-                .applyLatestStatefulForKey(init = mapOf(Unit to init), numKeys = 1)
-        val outFlow: TFlow<B> =
-            flow.mapMaybe {
-                checkNotNull(it[Unit]) { "applyLatest: expected result, but none present in: $it" }
-            }
-        val outInit: FrpDeferredValue<A> = deferredTransactionScope {
-            val initResult: Map<Unit, A> = result.get()
-            check(Unit in initResult) {
-                "applyLatest: expected initial result, but none present in: $initResult"
-            }
-            @Suppress("UNCHECKED_CAST")
-            initResult.getOrDefault(Unit) { null } as A
-        }
-        return Pair(outFlow, outInit)
-    }
-
-    /**
-     * Returns a [TFlow] containing the results of applying each [FrpStateful] emitted from the
-     * original [TFlow], and a [FrpDeferredValue] containing the result of applying [init]
-     * immediately.
-     *
-     * If the [Maybe] contained within the value for an associated key is [none], then the
-     * previously-active [FrpStateful] will be stopped with no replacement.
-     *
-     * When each [FrpStateful] is applied, state accumulation from the previously-active
-     * [FrpStateful] with the same key is stopped.
-     */
-    @ExperimentalFrpApi
-    fun <K, A, B> TFlow<Map<K, Maybe<FrpStateful<A>>>>.applyLatestStatefulForKey(
-        init: FrpDeferredValue<Map<K, FrpStateful<B>>>,
-        numKeys: Int? = null,
-    ): Pair<TFlow<Map<K, Maybe<A>>>, FrpDeferredValue<Map<K, B>>>
-
-    /**
-     * Returns a [TFlow] containing the results of applying each [FrpStateful] emitted from the
-     * original [TFlow], and a [FrpDeferredValue] containing the result of applying [init]
-     * immediately.
-     *
-     * When each [FrpStateful] is applied, state accumulation from the previously-active
-     * [FrpStateful] with the same key is stopped.
-     *
-     * If the [Maybe] contained within the value for an associated key is [none], then the
-     * previously-active [FrpStateful] will be stopped with no replacement.
-     */
-    @ExperimentalFrpApi
-    fun <K, A, B> TFlow<Map<K, Maybe<FrpStateful<A>>>>.applyLatestStatefulForKey(
-        init: Map<K, FrpStateful<B>>,
-        numKeys: Int? = null,
-    ): Pair<TFlow<Map<K, Maybe<A>>>, FrpDeferredValue<Map<K, B>>> =
-        applyLatestStatefulForKey(deferredOf(init), numKeys)
-
-    /**
-     * Returns a [TState] containing the latest results of applying each [FrpStateful] emitted from
-     * the original [TFlow].
-     *
-     * When each [FrpStateful] is applied, state accumulation from the previously-active
-     * [FrpStateful] with the same key is stopped.
-     *
-     * If the [Maybe] contained within the value for an associated key is [none], then the
-     * previously-active [FrpStateful] will be stopped with no replacement.
-     */
-    @ExperimentalFrpApi
-    fun <K, A> TFlow<Map<K, Maybe<FrpStateful<A>>>>.holdLatestStatefulForKey(
-        init: FrpDeferredValue<Map<K, FrpStateful<A>>>,
-        numKeys: Int? = null,
-    ): TState<Map<K, A>> {
-        val (changes, initialValues) = applyLatestStatefulForKey(init, numKeys)
-        return changes.foldMapIncrementally(initialValues)
-    }
-
-    /**
-     * Returns a [TState] containing the latest results of applying each [FrpStateful] emitted from
-     * the original [TFlow].
-     *
-     * When each [FrpStateful] is applied, state accumulation from the previously-active
-     * [FrpStateful] with the same key is stopped.
-     *
-     * If the [Maybe] contained within the value for an associated key is [none], then the
-     * previously-active [FrpStateful] will be stopped with no replacement.
-     */
-    @ExperimentalFrpApi
-    fun <K, A> TFlow<Map<K, Maybe<FrpStateful<A>>>>.holdLatestStatefulForKey(
-        init: Map<K, FrpStateful<A>> = emptyMap(),
-        numKeys: Int? = null,
-    ): TState<Map<K, A>> = holdLatestStatefulForKey(deferredOf(init), numKeys)
-
-    /**
-     * Returns a [TFlow] containing the results of applying each [FrpStateful] emitted from the
-     * original [TFlow], and a [FrpDeferredValue] containing the result of applying [init]
-     * immediately.
-     *
-     * When each [FrpStateful] is applied, state accumulation from the previously-active
-     * [FrpStateful] with the same key is stopped.
-     *
-     * If the [Maybe] contained within the value for an associated key is [none], then the
-     * previously-active [FrpStateful] will be stopped with no replacement.
-     */
-    @ExperimentalFrpApi
-    fun <K, A> TFlow<Map<K, Maybe<FrpStateful<A>>>>.applyLatestStatefulForKey(
-        numKeys: Int? = null
-    ): TFlow<Map<K, Maybe<A>>> =
-        applyLatestStatefulForKey(init = emptyMap<K, FrpStateful<*>>(), numKeys = numKeys).first
-
-    /**
-     * Returns a [TFlow] containing the results of applying [transform] to each value of the
-     * original [TFlow], and a [FrpDeferredValue] containing the result of applying [transform] to
-     * [initialValues] immediately.
-     *
-     * [transform] can perform state accumulation via its [FrpStateScope] receiver. With each
-     * invocation of [transform], state accumulation from previous invocation is stopped.
-     *
-     * If the [Maybe] contained within the value for an associated key is [none], then the
-     * previously-active [FrpStateScope] will be stopped with no replacement.
-     */
-    @ExperimentalFrpApi
-    fun <K, A, B> TFlow<Map<K, Maybe<A>>>.mapLatestStatefulForKey(
-        initialValues: FrpDeferredValue<Map<K, A>>,
-        numKeys: Int? = null,
-        transform: suspend FrpStateScope.(A) -> B,
-    ): Pair<TFlow<Map<K, Maybe<B>>>, FrpDeferredValue<Map<K, B>>> =
-        mapPure { patch -> patch.mapValues { (_, v) -> v.map { statefully { transform(it) } } } }
-            .applyLatestStatefulForKey(
-                deferredStateScope {
-                    initialValues.get().mapValues { (_, v) -> statefully { transform(v) } }
-                },
-                numKeys = numKeys,
-            )
-
-    /**
-     * Returns a [TFlow] containing the results of applying [transform] to each value of the
-     * original [TFlow], and a [FrpDeferredValue] containing the result of applying [transform] to
-     * [initialValues] immediately.
-     *
-     * [transform] can perform state accumulation via its [FrpStateScope] receiver. With each
-     * invocation of [transform], state accumulation from previous invocation is stopped.
-     *
-     * If the [Maybe] contained within the value for an associated key is [none], then the
-     * previously-active [FrpStateScope] will be stopped with no replacement.
-     */
-    @ExperimentalFrpApi
-    fun <K, A, B> TFlow<Map<K, Maybe<A>>>.mapLatestStatefulForKey(
-        initialValues: Map<K, A>,
-        numKeys: Int? = null,
-        transform: suspend FrpStateScope.(A) -> B,
-    ): Pair<TFlow<Map<K, Maybe<B>>>, FrpDeferredValue<Map<K, B>>> =
-        mapLatestStatefulForKey(deferredOf(initialValues), numKeys, transform)
-
-    /**
-     * Returns a [TFlow] containing the results of applying [transform] to each value of the
-     * original [TFlow].
-     *
-     * [transform] can perform state accumulation via its [FrpStateScope] receiver. With each
-     * invocation of [transform], state accumulation from previous invocation is stopped.
-     *
-     * If the [Maybe] contained within the value for an associated key is [none], then the
-     * previously-active [FrpStateScope] will be stopped with no replacement.
-     */
-    @ExperimentalFrpApi
-    fun <K, A, B> TFlow<Map<K, Maybe<A>>>.mapLatestStatefulForKey(
-        numKeys: Int? = null,
-        transform: suspend FrpStateScope.(A) -> B,
-    ): TFlow<Map<K, Maybe<B>>> = mapLatestStatefulForKey(emptyMap(), numKeys, transform).first
-
-    /**
-     * Returns a [TFlow] that will only emit the next event of the original [TFlow], and then will
-     * act as [emptyTFlow].
-     *
-     * If the original [TFlow] is emitting an event at this exact time, then it will be the only
-     * even emitted from the result [TFlow].
-     */
-    @ExperimentalFrpApi
-    fun <A> TFlow<A>.nextOnly(): TFlow<A> =
-        if (this === emptyTFlow) {
-            this
-        } else {
-            TFlowLoop<A>().also {
-                it.loopback = it.mapCheap { emptyTFlow }.hold(this@nextOnly).switch()
-            }
-        }
-
-    /** Returns a [TFlow] that skips the next emission of the original [TFlow]. */
-    @ExperimentalFrpApi
-    fun <A> TFlow<A>.skipNext(): TFlow<A> =
-        if (this === emptyTFlow) {
-            this
-        } else {
-            nextOnly().mapCheap { this@skipNext }.hold(emptyTFlow).switch()
-        }
-
-    /**
-     * Returns a [TFlow] that emits values from the original [TFlow] up until [stop] emits a value.
-     *
-     * If the original [TFlow] emits at the same time as [stop], then the returned [TFlow] will emit
-     * that value.
-     */
-    @ExperimentalFrpApi
-    fun <A> TFlow<A>.takeUntil(stop: TFlow<*>): TFlow<A> =
-        if (stop === emptyTFlow) {
-            this
-        } else {
-            stop.mapCheap { emptyTFlow }.nextOnly().hold(this).switch()
-        }
-
-    /**
-     * Invokes [stateful] in a new [FrpStateScope] that is a child of this one.
-     *
-     * This new scope is stopped when [stop] first emits a value, or when the parent scope is
-     * stopped. Stopping will end all state accumulation; any [TStates][TState] returned from this
-     * scope will no longer update.
-     */
-    @ExperimentalFrpApi
-    fun <A> childStateScope(stop: TFlow<*>, stateful: FrpStateful<A>): FrpDeferredValue<A> {
-        val (_, init: FrpDeferredValue<Map<Unit, A>>) =
-            stop
-                .nextOnly()
-                .mapPure { mapOf(Unit to none<FrpStateful<A>>()) }
-                .applyLatestStatefulForKey(init = mapOf(Unit to stateful), numKeys = 1)
-        return deferredStateScope { init.get().getValue(Unit) }
-    }
-
-    /**
-     * Returns a [TFlow] that emits values from the original [TFlow] up to and including a value is
-     * emitted that satisfies [predicate].
-     */
-    @ExperimentalFrpApi
-    fun <A> TFlow<A>.takeUntil(predicate: suspend FrpTransactionScope.(A) -> Boolean): TFlow<A> =
-        takeUntil(filter(predicate))
-
-    /**
-     * Returns a [TState] that is incrementally updated when this [TFlow] emits a value, by applying
-     * [transform] to both the emitted value and the currently tracked state.
-     *
-     * Note that the value contained within the [TState] is not updated until *after* all [TFlow]s
-     * have been processed; this keeps the value of the [TState] consistent during the entire FRP
-     * transaction.
-     */
-    @ExperimentalFrpApi
-    fun <A, B> TFlow<A>.fold(
-        initialValue: B,
-        transform: suspend FrpTransactionScope.(A, B) -> B,
-    ): TState<B> {
-        lateinit var state: TState<B>
-        return mapPure { a -> transform(a, state.sample()) }.hold(initialValue).also { state = it }
-    }
-
-    /**
-     * Returns a [TState] that is incrementally updated when this [TFlow] emits a value, by applying
-     * [transform] to both the emitted value and the currently tracked state.
-     *
-     * Note that the value contained within the [TState] is not updated until *after* all [TFlow]s
-     * have been processed; this keeps the value of the [TState] consistent during the entire FRP
-     * transaction.
-     */
-    @ExperimentalFrpApi
-    fun <A, B> TFlow<A>.foldDeferred(
-        initialValue: FrpDeferredValue<B>,
-        transform: suspend FrpTransactionScope.(A, B) -> B,
-    ): TState<B> {
-        lateinit var state: TState<B>
-        return mapPure { a -> transform(a, state.sample()) }
-            .holdDeferred(initialValue)
-            .also { state = it }
-    }
-
-    /**
-     * Returns a [TState] that holds onto the result of applying the most recently emitted
-     * [FrpStateful] this [TFlow], or [init] if nothing has been emitted since it was constructed.
-     *
-     * When each [FrpStateful] is applied, state accumulation from the previously-active
-     * [FrpStateful] is stopped.
-     *
-     * Note that the value contained within the [TState] is not updated until *after* all [TFlow]s
-     * have been processed; this keeps the value of the [TState] consistent during the entire FRP
-     * transaction.
-     *
-     * Shorthand for:
-     * ```kotlin
-     * val (changes, initApplied) = applyLatestStateful(init)
-     * return changes.toTStateDeferred(initApplied)
-     * ```
-     */
-    @ExperimentalFrpApi
-    fun <A> TFlow<FrpStateful<A>>.holdLatestStateful(init: FrpStateful<A>): TState<A> {
-        val (changes, initApplied) = applyLatestStateful(init)
-        return changes.holdDeferred(initApplied)
-    }
-
-    /**
-     * Returns a [TFlow] that emits the two most recent emissions from the original [TFlow].
-     * [initialValue] is used as the previous value for the first emission.
-     *
-     * Shorthand for `sample(hold(init)) { new, old -> Pair(old, new) }`
-     */
-    @ExperimentalFrpApi
-    fun <S, T : S> TFlow<T>.pairwise(initialValue: S): TFlow<WithPrev<S, T>> {
-        val previous = hold(initialValue)
-        return mapCheap { new -> WithPrev(previousValue = previous.sample(), newValue = new) }
-    }
-
-    /**
-     * Returns a [TFlow] that emits the two most recent emissions from the original [TFlow]. Note
-     * that the returned [TFlow] will not emit until the original [TFlow] has emitted twice.
-     */
-    @ExperimentalFrpApi
-    fun <A> TFlow<A>.pairwise(): TFlow<WithPrev<A, A>> =
-        mapCheap { just(it) }
-            .pairwise(none)
-            .mapMaybe { (prev, next) -> prev.zipWith(next, ::WithPrev) }
-
-    /**
-     * Returns a [TState] that holds both the current and previous values of the original [TState].
-     * [initialPreviousValue] is used as the first previous value.
-     *
-     * Shorthand for `sample(hold(init)) { new, old -> Pair(old, new) }`
-     */
-    @ExperimentalFrpApi
-    fun <S, T : S> TState<T>.pairwise(initialPreviousValue: S): TState<WithPrev<S, T>> =
-        stateChanges
-            .pairwise(initialPreviousValue)
-            .holdDeferred(deferredTransactionScope { WithPrev(initialPreviousValue, sample()) })
-
-    /**
-     * Returns a [TState] holding a [Map] that is updated incrementally whenever this emits a value.
-     *
-     * The value emitted is used as a "patch" for the tracked [Map]; for each key [K] in the emitted
-     * map, an associated value of [Just] will insert or replace the value in the tracked [Map], and
-     * an associated value of [none] will remove the key from the tracked [Map].
-     */
-    @ExperimentalFrpApi
-    fun <K, V> TFlow<Map<K, Maybe<V>>>.foldMapIncrementally(
-        initialValues: FrpDeferredValue<Map<K, V>>
-    ): TState<Map<K, V>> =
-        foldDeferred(initialValues) { patch, map ->
-            val (adds: List<Pair<K, V>>, removes: List<K>) =
-                patch
-                    .asSequence()
-                    .map { (k, v) -> if (v is Just) Left(k to v.value) else Right(k) }
-                    .partitionEithers()
-            val removed: Map<K, V> = map - removes.toSet()
-            val updated: Map<K, V> = removed + adds
-            updated
-        }
-
-    /**
-     * Returns a [TState] holding a [Map] that is updated incrementally whenever this emits a value.
-     *
-     * The value emitted is used as a "patch" for the tracked [Map]; for each key [K] in the emitted
-     * map, an associated value of [Just] will insert or replace the value in the tracked [Map], and
-     * an associated value of [none] will remove the key from the tracked [Map].
-     */
-    @ExperimentalFrpApi
-    fun <K, V> TFlow<Map<K, Maybe<V>>>.foldMapIncrementally(
-        initialValues: Map<K, V> = emptyMap()
-    ): TState<Map<K, V>> = foldMapIncrementally(deferredOf(initialValues))
-
-    /**
-     * Returns a [TFlow] that wraps each emission of the original [TFlow] into an [IndexedValue],
-     * containing the emitted value and its index (starting from zero).
-     *
-     * Shorthand for:
-     * ```
-     *   val index = fold(0) { _, oldIdx -> oldIdx + 1 }
-     *   sample(index) { a, idx -> IndexedValue(idx, a) }
-     * ```
-     */
-    @ExperimentalFrpApi
-    fun <A> TFlow<A>.withIndex(): TFlow<IndexedValue<A>> {
-        val index = fold(0) { _, old -> old + 1 }
-        return sample(index) { a, idx -> IndexedValue(idx, a) }
-    }
-
-    /**
-     * Returns a [TFlow] containing the results of applying [transform] to each value of the
-     * original [TFlow] and its index (starting from zero).
-     *
-     * Shorthand for:
-     * ```
-     *   withIndex().map { (idx, a) -> transform(idx, a) }
-     * ```
-     */
-    @ExperimentalFrpApi
-    fun <A, B> TFlow<A>.mapIndexed(transform: suspend FrpTransactionScope.(Int, A) -> B): TFlow<B> {
-        val index = fold(0) { _, i -> i + 1 }
-        return sample(index) { a, idx -> transform(idx, a) }
-    }
-
-    /** Returns a [TFlow] where all subsequent repetitions of the same value are filtered out. */
-    @ExperimentalFrpApi
-    fun <A> TFlow<A>.distinctUntilChanged(): TFlow<A> {
-        val state: TState<Any?> = hold(Any())
-        return filter { it != state.sample() }
-    }
-
-    /**
-     * Returns a new [TFlow] that emits at the same rate as the original [TFlow], but combines the
-     * emitted value with the most recent emission from [other] using [transform].
-     *
-     * Note that the returned [TFlow] will not emit anything until [other] has emitted at least one
-     * value.
-     */
-    @ExperimentalFrpApi
-    fun <A, B, C> TFlow<A>.sample(
-        other: TFlow<B>,
-        transform: suspend FrpTransactionScope.(A, B) -> C,
-    ): TFlow<C> {
-        val state = other.mapCheap { just(it) }.hold(none)
-        return sample(state) { a, b -> b.map { transform(a, it) } }.filterJust()
-    }
-
-    /**
-     * Returns a [TState] that samples the [Transactional] held by the given [TState] within the
-     * same transaction that the state changes.
-     */
-    @ExperimentalFrpApi
-    fun <A> TState<Transactional<A>>.sampleTransactionals(): TState<A> =
-        stateChanges
-            .sampleTransactionals()
-            .holdDeferred(deferredTransactionScope { sample().sample() })
-
-    /**
-     * Returns a [TState] that transforms the value held inside this [TState] by applying it to the
-     * given function [transform].
-     */
-    @ExperimentalFrpApi
-    fun <A, B> TState<A>.map(transform: suspend FrpTransactionScope.(A) -> B): TState<B> =
-        mapPure { transactionally { transform(it) } }.sampleTransactionals()
-
-    /**
-     * Returns a [TState] whose value is generated with [transform] by combining the current values
-     * of each given [TState].
-     *
-     * @see TState.combineWith
-     */
-    @ExperimentalFrpApi
-    fun <A, B, Z> combine(
-        stateA: TState<A>,
-        stateB: TState<B>,
-        transform: suspend FrpTransactionScope.(A, B) -> Z,
-    ): TState<Z> =
-        com.android.systemui.kairos
-            .combine(stateA, stateB) { a, b -> transactionally { transform(a, b) } }
-            .sampleTransactionals()
-
-    /**
-     * Returns a [TState] whose value is generated with [transform] by combining the current values
-     * of each given [TState].
-     *
-     * @see TState.combineWith
-     */
-    @ExperimentalFrpApi
-    fun <A, B, C, D, Z> combine(
-        stateA: TState<A>,
-        stateB: TState<B>,
-        stateC: TState<C>,
-        stateD: TState<D>,
-        transform: suspend FrpTransactionScope.(A, B, C, D) -> Z,
-    ): TState<Z> =
-        com.android.systemui.kairos
-            .combine(stateA, stateB, stateC, stateD) { a, b, c, d ->
-                transactionally { transform(a, b, c, d) }
-            }
-            .sampleTransactionals()
-
-    /** Returns a [TState] by applying [transform] to the value held by the original [TState]. */
-    @ExperimentalFrpApi
-    fun <A, B> TState<A>.flatMap(
-        transform: suspend FrpTransactionScope.(A) -> TState<B>
-    ): TState<B> = mapPure { transactionally { transform(it) } }.sampleTransactionals().flatten()
-
-    /**
-     * Returns a [TState] whose value is generated with [transform] by combining the current values
-     * of each given [TState].
-     *
-     * @see TState.combineWith
-     */
-    @ExperimentalFrpApi
-    fun <A, Z> combine(
-        vararg states: TState<A>,
-        transform: suspend FrpTransactionScope.(List<A>) -> Z,
-    ): TState<Z> = combinePure(*states).map(transform)
-
-    /**
-     * Returns a [TState] whose value is generated with [transform] by combining the current values
-     * of each given [TState].
-     *
-     * @see TState.combineWith
-     */
-    @ExperimentalFrpApi
-    fun <A, Z> Iterable<TState<A>>.combine(
-        transform: suspend FrpTransactionScope.(List<A>) -> Z
-    ): TState<Z> = combinePure().map(transform)
-
-    /**
-     * Returns a [TState] by combining the values held inside the given [TState]s by applying them
-     * to the given function [transform].
-     */
-    @ExperimentalFrpApi
-    fun <A, B, C> TState<A>.combineWith(
-        other: TState<B>,
-        transform: suspend FrpTransactionScope.(A, B) -> C,
-    ): TState<C> = combine(this, other, transform)
-}
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/FrpTransactionScope.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/FrpTransactionScope.kt
deleted file mode 100644
index a7ae1d9..0000000
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/FrpTransactionScope.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.kairos
-
-import kotlin.coroutines.RestrictsSuspension
-
-/**
- * FRP operations that are available while a transaction is active.
- *
- * These operations do not accumulate state, which makes [FrpTransactionScope] weaker than
- * [FrpStateScope], but allows them to be used in more places.
- */
-@ExperimentalFrpApi
-@RestrictsSuspension
-interface FrpTransactionScope : FrpScope {
-
-    /**
-     * Returns the current value of this [Transactional] as a [FrpDeferredValue].
-     *
-     * @see sample
-     */
-    @ExperimentalFrpApi fun <A> Transactional<A>.sampleDeferred(): FrpDeferredValue<A>
-
-    /**
-     * Returns the current value of this [TState] as a [FrpDeferredValue].
-     *
-     * @see sample
-     */
-    @ExperimentalFrpApi fun <A> TState<A>.sampleDeferred(): FrpDeferredValue<A>
-
-    /** TODO */
-    @ExperimentalFrpApi
-    fun <A> deferredTransactionScope(
-        block: suspend FrpTransactionScope.() -> A
-    ): FrpDeferredValue<A>
-
-    /** A [TFlow] that emits once, within this transaction, and then never again. */
-    @ExperimentalFrpApi val now: TFlow<Unit>
-
-    /**
-     * Returns the current value held by this [TState]. Guaranteed to be consistent within the same
-     * transaction.
-     */
-    @ExperimentalFrpApi suspend fun <A> TState<A>.sample(): A = sampleDeferred().get()
-
-    /**
-     * Returns the current value held by this [Transactional]. Guaranteed to be consistent within
-     * the same transaction.
-     */
-    @ExperimentalFrpApi suspend fun <A> Transactional<A>.sample(): A = sampleDeferred().get()
-}
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/GroupBy.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/GroupBy.kt
new file mode 100644
index 0000000..45da34a
--- /dev/null
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/GroupBy.kt
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.kairos
+
+import com.android.systemui.kairos.internal.DemuxImpl
+import com.android.systemui.kairos.internal.constInit
+import com.android.systemui.kairos.internal.demuxMap
+import com.android.systemui.kairos.util.Either
+import com.android.systemui.kairos.util.These
+import com.android.systemui.kairos.util.maybeFirst
+import com.android.systemui.kairos.util.maybeSecond
+import com.android.systemui.kairos.util.orError
+
+/**
+ * Returns a [GroupedEvents] that can be used to efficiently split a single [Events] into multiple
+ * downstream [Events].
+ *
+ * The input [Events] emits [Map] instances that specify which downstream [Events] the associated
+ * value will be emitted from. These downstream [Events] can be obtained via
+ * [GroupedEvents.eventsForKey].
+ *
+ * An example:
+ * ```
+ *   val fooEvents: Events<Map<String, Foo>> = ...
+ *   val fooById: GroupedEvents<String, Foo> = fooEvents.groupByKey()
+ *   val fooBar: Events<Foo> = fooById["bar"]
+ * ```
+ *
+ * This is semantically equivalent to `val fooBar = fooEvents.mapNotNull { map -> map["bar"] }` but
+ * is significantly more efficient; specifically, using [mapNotNull] in this way incurs a `O(n)`
+ * performance hit, where `n` is the number of different [mapNotNull] operations used to filter on a
+ * specific key's presence in the emitted [Map]. [groupByKey] internally uses a [HashMap] to lookup
+ * the appropriate downstream [Events], and so operates in `O(1)`.
+ *
+ * The optional [numKeys] argument is an optimization used to initialize the internal [HashMap].
+ *
+ * Note that the returned [GroupedEvents] should be cached and re-used to gain the performance
+ * benefit.
+ *
+ * @sample com.android.systemui.kairos.KairosSamples.groupByKey
+ * @see selector
+ */
+@ExperimentalKairosApi
+fun <K, A> Events<Map<K, A>>.groupByKey(numKeys: Int? = null): GroupedEvents<K, A> =
+    GroupedEvents(demuxMap({ init.connect(this) }, numKeys))
+
+/**
+ * Returns a [GroupedEvents] that can be used to efficiently split a single [Events] into multiple
+ * downstream [Events]. The downstream [Events] are associated with a [key][K], which is derived
+ * from each emission of the original [Events] via [extractKey].
+ *
+ * ``` kotlin
+ *   fun <K, A> Events<A>.groupBy(
+ *       numKeys: Int? = null,
+ *       extractKey: TransactionScope.(A) -> K,
+ *   ): GroupedEvents<K, A> =
+ *       map { mapOf(extractKey(it) to it) }.groupByKey(numKeys)
+ * ```
+ *
+ * @see groupByKey
+ */
+@ExperimentalKairosApi
+fun <K, A> Events<A>.groupBy(
+    numKeys: Int? = null,
+    extractKey: TransactionScope.(A) -> K,
+): GroupedEvents<K, A> = map { mapOf(extractKey(it) to it) }.groupByKey(numKeys)
+
+/**
+ * A mapping from keys of type [K] to [Events] emitting values of type [A].
+ *
+ * @see groupByKey
+ */
+@ExperimentalKairosApi
+class GroupedEvents<in K, out A> internal constructor(internal val impl: DemuxImpl<K, A>) {
+    /**
+     * Returns an [Events] that emits values of type [A] that correspond to the given [key].
+     *
+     * @see groupByKey
+     */
+    fun eventsForKey(key: K): Events<A> = EventsInit(constInit(name = null, impl.eventsForKey(key)))
+
+    /**
+     * Returns an [Events] that emits values of type [A] that correspond to the given [key].
+     *
+     * @see groupByKey
+     */
+    operator fun get(key: K): Events<A> = eventsForKey(key)
+}
+
+/**
+ * Returns two new [Events] that contain elements from this [Events] that satisfy or don't satisfy
+ * [predicate].
+ *
+ * Using this is equivalent to `upstream.filter(predicate) to upstream.filter { !predicate(it) }`
+ * but is more efficient; specifically, [partition] will only invoke [predicate] once per element.
+ *
+ * ``` kotlin
+ *   fun <A> Events<A>.partition(
+ *       predicate: TransactionScope.(A) -> Boolean
+ *   ): Pair<Events<A>, Events<A>> =
+ *       map { if (predicate(it)) left(it) else right(it) }.partitionEither()
+ * ```
+ *
+ * @see partitionEither
+ */
+@ExperimentalKairosApi
+fun <A> Events<A>.partition(
+    predicate: TransactionScope.(A) -> Boolean
+): Pair<Events<A>, Events<A>> {
+    val grouped: GroupedEvents<Boolean, A> = groupBy(numKeys = 2, extractKey = predicate)
+    return Pair(grouped.eventsForKey(true), grouped.eventsForKey(false))
+}
+
+/**
+ * Returns two new [Events] that contain elements from this [Events]; [Pair.first] will contain
+ * [First] values, and [Pair.second] will contain [Second] values.
+ *
+ * Using this is equivalent to using [filterIsInstance] in conjunction with [map] twice, once for
+ * [First]s and once for [Second]s, but is slightly more efficient; specifically, the
+ * [filterIsInstance] check is only performed once per element.
+ *
+ * ``` kotlin
+ *   fun <A, B> Events<Either<A, B>>.partitionEither(): Pair<Events<A>, Events<B>> =
+ *     map { it.asThese() }.partitionThese()
+ * ```
+ *
+ * @see partitionThese
+ */
+@ExperimentalKairosApi
+fun <A, B> Events<Either<A, B>>.partitionEither(): Pair<Events<A>, Events<B>> {
+    val (left, right) = partition { it is Either.First }
+    return Pair(
+        left.mapCheap { (it as Either.First).value },
+        right.mapCheap { (it as Either.Second).value },
+    )
+}
+
+/**
+ * Returns two new [Events] that contain elements from this [Events]; [Pair.first] will contain
+ * [These.first] values, and [Pair.second] will contain [These.second] values. If the original
+ * emission was a [These.both], then both result [Events] will emit a value simultaneously.
+ *
+ * @sample com.android.systemui.kairos.KairosSamples.partitionThese
+ */
+@ExperimentalKairosApi
+fun <A, B> Events<These<A, B>>.partitionThese(): Pair<Events<A>, Events<B>> {
+    val grouped =
+        mapCheap {
+                when (it) {
+                    is These.Both -> mapOf(true to it, false to it)
+                    is These.Second -> mapOf(false to it)
+                    is These.First -> mapOf(true to it)
+                }
+            }
+            .groupByKey(numKeys = 2)
+    return Pair(
+        grouped.eventsForKey(true).mapCheap {
+            it.maybeFirst().orError { "unexpected missing value" }
+        },
+        grouped.eventsForKey(false).mapCheap {
+            it.maybeSecond().orError { "unexpected missing value" }
+        },
+    )
+}
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Incremental.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Incremental.kt
new file mode 100644
index 0000000..d88ae3b8
--- /dev/null
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Incremental.kt
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.kairos
+
+import com.android.systemui.kairos.internal.CompletableLazy
+import com.android.systemui.kairos.internal.IncrementalImpl
+import com.android.systemui.kairos.internal.Init
+import com.android.systemui.kairos.internal.InitScope
+import com.android.systemui.kairos.internal.NoScope
+import com.android.systemui.kairos.internal.constIncremental
+import com.android.systemui.kairos.internal.constInit
+import com.android.systemui.kairos.internal.init
+import com.android.systemui.kairos.internal.mapValuesImpl
+import com.android.systemui.kairos.internal.util.hashString
+import com.android.systemui.kairos.util.MapPatch
+import com.android.systemui.kairos.util.mapPatchFromFullDiff
+import kotlin.reflect.KProperty
+
+/**
+ * A [State] tracking a [Map] that receives incremental updates.
+ *
+ * [Incremental] allows one to react to the [subset of changes][updates] to the held map, without
+ * having to perform a manual diff of the map to determine what changed.
+ *
+ * @sample com.android.systemui.kairos.KairosSamples.incrementals
+ */
+sealed class Incremental<K, out V> : State<Map<K, V>>() {
+    abstract override val init: Init<IncrementalImpl<K, V>>
+}
+
+/**
+ * Returns a constant [Incremental] that never changes. [changes] and [updates] are both equivalent
+ * to [emptyEvents], and [TransactionScope.sample] will always produce [value].
+ */
+@ExperimentalKairosApi
+fun <K, V> incrementalOf(value: Map<K, V>): Incremental<K, V> {
+    val operatorName = "stateOf"
+    val name = "$operatorName($value)"
+    return IncrementalInit(constInit(name, constIncremental(name, operatorName, value)))
+}
+
+/**
+ * Returns an [Incremental] that acts as a deferred-reference to the [Incremental] produced by this
+ * [Lazy].
+ *
+ * When the returned [Incremental] is accessed by the Kairos network, the [Lazy]'s
+ * [value][Lazy.value] will be queried and used.
+ *
+ * Useful for recursive definitions.
+ *
+ * ``` kotlin
+ *   fun <A> Lazy<Incremental<K, V>>.defer() = deferredIncremental { value }
+ * ```
+ */
+@ExperimentalKairosApi
+fun <K, V> Lazy<Incremental<K, V>>.defer(): Incremental<K, V> = deferInline { value }
+
+/**
+ * Returns an [Incremental] that acts as a deferred-reference to the [Incremental] produced by this
+ * [DeferredValue].
+ *
+ * When the returned [Incremental] is accessed by the Kairos network, the [DeferredValue] will be
+ * queried and used.
+ *
+ * Useful for recursive definitions.
+ *
+ * ``` kotlin
+ *   fun <A> DeferredValue<Incremental<K, V>>.defer() = deferredIncremental { get() }
+ * ```
+ */
+@ExperimentalKairosApi
+fun <K, V> DeferredValue<Incremental<K, V>>.defer(): Incremental<K, V> = deferInline {
+    unwrapped.value
+}
+
+/**
+ * Returns an [Incremental] that acts as a deferred-reference to the [Incremental] produced by
+ * [block].
+ *
+ * When the returned [Incremental] is accessed by the Kairos network, [block] will be invoked and
+ * the returned [Incremental] will be used.
+ *
+ * Useful for recursive definitions.
+ */
+@ExperimentalKairosApi
+fun <K, V> deferredIncremental(block: KairosScope.() -> Incremental<K, V>): Incremental<K, V> =
+    deferInline {
+        NoScope.block()
+    }
+
+/**
+ * An [Events] that emits every time this [Incremental] changes, containing the subset of the map
+ * that has changed.
+ *
+ * @see MapPatch
+ */
+val <K, V> Incremental<K, V>.updates: Events<MapPatch<K, V>>
+    get() = EventsInit(init("patches") { init.connect(this).patches })
+
+internal class IncrementalInit<K, V>(override val init: Init<IncrementalImpl<K, V>>) :
+    Incremental<K, V>()
+
+/**
+ * Returns an [Incremental] that tracks the entries of the original incremental, but values replaced
+ * with those obtained by applying [transform] to each original entry.
+ */
+fun <K, V, U> Incremental<K, V>.mapValues(
+    transform: KairosScope.(Map.Entry<K, V>) -> U
+): Incremental<K, U> {
+    val operatorName = "mapValues"
+    val name = operatorName
+    return IncrementalInit(
+        init(name) {
+            mapValuesImpl({ init.connect(this) }, name, operatorName) { NoScope.transform(it) }
+        }
+    )
+}
+
+/**
+ * A forward-reference to an [Incremental]. Useful for recursive definitions.
+ *
+ * This reference can be used like a standard [Incremental], but will throw an error if its
+ * [loopback] is unset before it is [observed][BuildScope.observe] or
+ * [sampled][TransactionScope.sample]. Note that it is safe to invoke
+ * [TransactionScope.sampleDeferred] before [loopback] is set, provided the [DeferredValue] is not
+ * [queried][KairosScope.get].
+ */
+@ExperimentalKairosApi
+class IncrementalLoop<K, V>(private val name: String? = null) : Incremental<K, V>() {
+
+    private val deferred = CompletableLazy<Incremental<K, V>>(name = name)
+
+    override val init: Init<IncrementalImpl<K, V>> =
+        init(name) { deferred.value.init.connect(evalScope = this) }
+
+    /**
+     * The [Incremental] this reference is referring to. Must be set before this [IncrementalLoop]
+     * is [observed][BuildScope.observe].
+     */
+    var loopback: Incremental<K, V>? = null
+        set(value) {
+            value?.let {
+                check(!deferred.isInitialized()) {
+                    "IncrementalLoop($name).loopback has already been set."
+                }
+                deferred.setValue(value)
+                field = value
+            }
+        }
+
+    operator fun getValue(thisRef: Any?, property: KProperty<*>): Incremental<K, V> = this
+
+    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Incremental<K, V>) {
+        loopback = value
+    }
+
+    override fun toString(): String = "${this::class.simpleName}($name)@$hashString"
+}
+
+/**
+ * Returns an [Incremental] whose [updates] are calculated by [diffing][mapPatchFromFullDiff] the
+ * given [State]'s [transitions].
+ */
+fun <K, V> State<Map<K, V>>.asIncremental(): Incremental<K, V> {
+    if (this is Incremental<K, V>) return this
+
+    val hashState = map { if (it is HashMap) it else HashMap(it) }
+
+    val patches =
+        transitions.mapNotNull { (old, new) ->
+            mapPatchFromFullDiff(old, new).takeIf { it.isNotEmpty() }
+        }
+
+    return IncrementalInit(
+        init("asIncremental") {
+            val upstream = hashState.init.connect(this)
+            IncrementalImpl(
+                upstream.name,
+                upstream.operatorName,
+                upstream.changes,
+                patches.init.connect(this),
+                upstream.store,
+            )
+        }
+    )
+}
+
+private inline fun <K, V> deferInline(
+    crossinline block: InitScope.() -> Incremental<K, V>
+): Incremental<K, V> = IncrementalInit(init(name = null) { block().init.connect(evalScope = this) })
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/KairosNetwork.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/KairosNetwork.kt
new file mode 100644
index 0000000..19e3fcd
--- /dev/null
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/KairosNetwork.kt
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.kairos
+
+import com.android.systemui.kairos.internal.BuildScopeImpl
+import com.android.systemui.kairos.internal.Network
+import com.android.systemui.kairos.internal.StateScopeImpl
+import com.android.systemui.kairos.internal.util.awaitCancellationAndThen
+import com.android.systemui.kairos.internal.util.childScope
+import kotlin.coroutines.CoroutineContext
+import kotlin.coroutines.EmptyCoroutineContext
+import kotlinx.coroutines.CancellationException
+import kotlinx.coroutines.CoroutineName
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Deferred
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.job
+import kotlinx.coroutines.launch
+
+/** Marks APIs that are still **experimental** and shouldn't be used in general production code. */
+@RequiresOptIn(
+    message = "This API is experimental and should not be used in general production code."
+)
+@Retention(AnnotationRetention.BINARY)
+annotation class ExperimentalKairosApi
+
+/**
+ * External interface to a Kairos network of reactive components. Can be used to make transactional
+ * queries and modifications to the network.
+ */
+@ExperimentalKairosApi
+interface KairosNetwork {
+    /**
+     * Runs [block] inside of a transaction, suspending until the transaction is complete.
+     *
+     * The [BuildScope] receiver exposes methods that can be used to query or modify the network. If
+     * the network is cancelled while the caller of [transact] is suspended, then the call will be
+     * cancelled.
+     */
+    suspend fun <R> transact(block: TransactionScope.() -> R): R
+
+    /**
+     * Activates [spec] in a transaction, suspending indefinitely. While suspended, all observers
+     * and long-running effects are kept alive. When cancelled, observers are unregistered and
+     * effects are cancelled.
+     */
+    suspend fun activateSpec(spec: BuildSpec<*>)
+
+    /** Returns a [CoalescingMutableEvents] that can emit values into this [KairosNetwork]. */
+    fun <In, Out> coalescingMutableEvents(
+        coalesce: (old: Out, new: In) -> Out,
+        getInitialValue: () -> Out,
+    ): CoalescingMutableEvents<In, Out>
+
+    /** Returns a [MutableState] that can emit values into this [KairosNetwork]. */
+    fun <T> mutableEvents(): MutableEvents<T>
+
+    /** Returns a [CoalescingMutableEvents] that can emit values into this [KairosNetwork]. */
+    fun <T> conflatedMutableEvents(): CoalescingMutableEvents<T, T>
+
+    /** Returns a [MutableState]. with initial state [initialValue]. */
+    fun <T> mutableStateDeferred(initialValue: DeferredValue<T>): MutableState<T>
+}
+
+/** Returns a [CoalescingMutableEvents] that can emit values into this [KairosNetwork]. */
+@ExperimentalKairosApi
+fun <In, Out> KairosNetwork.coalescingMutableEvents(
+    coalesce: (old: Out, new: In) -> Out,
+    initialValue: Out,
+): CoalescingMutableEvents<In, Out> =
+    coalescingMutableEvents(coalesce, getInitialValue = { initialValue })
+
+/** Returns a [MutableState] with initial state [initialValue]. */
+@ExperimentalKairosApi
+fun <T> KairosNetwork.mutableState(initialValue: T): MutableState<T> =
+    mutableStateDeferred(deferredOf(initialValue))
+
+/** Returns a [MutableState] with initial state [initialValue]. */
+@ExperimentalKairosApi
+fun <T> MutableState(network: KairosNetwork, initialValue: T): MutableState<T> =
+    network.mutableState(initialValue)
+
+/** Returns a [MutableEvents] that can emit values into this [KairosNetwork]. */
+@ExperimentalKairosApi
+fun <T> MutableEvents(network: KairosNetwork): MutableEvents<T> = network.mutableEvents()
+
+/** Returns a [CoalescingMutableEvents] that can emit values into this [KairosNetwork]. */
+@ExperimentalKairosApi
+fun <In, Out> CoalescingMutableEvents(
+    network: KairosNetwork,
+    coalesce: (old: Out, new: In) -> Out,
+    initialValue: Out,
+): CoalescingMutableEvents<In, Out> = network.coalescingMutableEvents(coalesce) { initialValue }
+
+/** Returns a [CoalescingMutableEvents] that can emit values into this [KairosNetwork]. */
+@ExperimentalKairosApi
+fun <In, Out> CoalescingMutableEvents(
+    network: KairosNetwork,
+    coalesce: (old: Out, new: In) -> Out,
+    getInitialValue: () -> Out,
+): CoalescingMutableEvents<In, Out> = network.coalescingMutableEvents(coalesce, getInitialValue)
+
+/** Returns a [CoalescingMutableEvents] that can emit values into this [KairosNetwork]. */
+@ExperimentalKairosApi
+fun <T> ConflatedMutableEvents(network: KairosNetwork): CoalescingMutableEvents<T, T> =
+    network.conflatedMutableEvents()
+
+/**
+ * Activates [spec] in a transaction and invokes [block] with the result, suspending indefinitely.
+ * While suspended, all observers and long-running effects are kept alive. When cancelled, observers
+ * are unregistered and effects are cancelled.
+ */
+@ExperimentalKairosApi
+suspend fun <R> KairosNetwork.activateSpec(spec: BuildSpec<R>, block: suspend (R) -> Unit) {
+    activateSpec {
+        val result = spec.applySpec()
+        launchEffect { block(result) }
+    }
+}
+
+internal class LocalNetwork(
+    private val network: Network,
+    private val scope: CoroutineScope,
+    private val endSignal: Events<Any>,
+) : KairosNetwork {
+    override suspend fun <R> transact(block: TransactionScope.() -> R): R =
+        network.transaction("KairosNetwork.transact") { block() }.awaitOrCancel()
+
+    override suspend fun activateSpec(spec: BuildSpec<*>) {
+        val stopEmitter = conflatedMutableEvents<Unit>()
+        network
+            .transaction("KairosNetwork.activateSpec") {
+                val buildScope =
+                    BuildScopeImpl(
+                        stateScope =
+                            StateScopeImpl(
+                                evalScope = this,
+                                endSignalLazy = lazy { mergeLeft(stopEmitter, endSignal) },
+                            ),
+                        coroutineScope = scope,
+                    )
+                buildScope.launchScope {
+                    spec.applySpec()
+                    launchEffect { awaitCancellationAndThen { stopEmitter.emit(Unit) } }
+                }
+            }
+            .awaitOrCancel()
+            .joinOrCancel()
+    }
+
+    private suspend fun <T> Deferred<T>.awaitOrCancel(): T =
+        try {
+            await()
+        } catch (ex: CancellationException) {
+            cancel(ex)
+            throw ex
+        }
+
+    private suspend fun Job.joinOrCancel(): Unit =
+        try {
+            join()
+        } catch (ex: CancellationException) {
+            cancel(ex)
+            throw ex
+        }
+
+    override fun <In, Out> coalescingMutableEvents(
+        coalesce: (old: Out, new: In) -> Out,
+        getInitialValue: () -> Out,
+    ): CoalescingMutableEvents<In, Out> =
+        CoalescingMutableEvents(
+            null,
+            coalesce = { old, new -> coalesce(old.value, new) },
+            network,
+            getInitialValue,
+        )
+
+    override fun <T> conflatedMutableEvents(): CoalescingMutableEvents<T, T> =
+        CoalescingMutableEvents(
+            null,
+            coalesce = { _, new -> new },
+            network,
+            { error("WTF: init value accessed for conflatedMutableEvents") },
+        )
+
+    override fun <T> mutableEvents(): MutableEvents<T> = MutableEvents(network)
+
+    override fun <T> mutableStateDeferred(initialValue: DeferredValue<T>): MutableState<T> =
+        MutableState(network, initialValue.unwrapped)
+}
+
+/**
+ * Combination of an [KairosNetwork] and a [Job] that, when cancelled, will cancel the entire Kairos
+ * network.
+ */
+@ExperimentalKairosApi
+class RootKairosNetwork
+internal constructor(private val network: Network, private val scope: CoroutineScope, job: Job) :
+    Job by job, KairosNetwork by LocalNetwork(network, scope, emptyEvents)
+
+/** Constructs a new [RootKairosNetwork] in the given [CoroutineScope]. */
+@ExperimentalKairosApi
+fun CoroutineScope.launchKairosNetwork(
+    context: CoroutineContext = EmptyCoroutineContext
+): RootKairosNetwork {
+    val scope = childScope(context)
+    val network = Network(scope)
+    scope.launch(CoroutineName("launchKairosNetwork scheduler")) { network.runInputScheduler() }
+    return RootKairosNetwork(network, scope, scope.coroutineContext.job)
+}
+
+@ExperimentalKairosApi
+interface HasNetwork : KairosScope {
+    /**
+     * A [KairosNetwork] handle that is bound to the lifetime of a [BuildScope].
+     *
+     * It supports all of the standard functionality by which external code can interact with this
+     * Kairos network, but all [activated][KairosNetwork.activateSpec] [BuildSpec]s are bound as
+     * children to the [BuildScope], such that when the [BuildScope] is destroyed, all children are
+     * also destroyed.
+     */
+    val kairosNetwork: KairosNetwork
+}
+
+/** Returns a [MutableEvents] that can emit values into this [KairosNetwork]. */
+@ExperimentalKairosApi
+fun <T> HasNetwork.MutableEvents(): MutableEvents<T> = MutableEvents(kairosNetwork)
+
+/** Returns a [MutableState] with initial state [initialValue]. */
+@ExperimentalKairosApi
+fun <T> HasNetwork.MutableState(initialValue: T): MutableState<T> =
+    MutableState(kairosNetwork, initialValue)
+
+/** Returns a [CoalescingMutableEvents] that can emit values into this [KairosNetwork]. */
+@ExperimentalKairosApi
+fun <In, Out> HasNetwork.CoalescingMutableEvents(
+    coalesce: (old: Out, new: In) -> Out,
+    initialValue: Out,
+): CoalescingMutableEvents<In, Out> = CoalescingMutableEvents(kairosNetwork, coalesce, initialValue)
+
+/** Returns a [CoalescingMutableEvents] that can emit values into this [KairosNetwork]. */
+@ExperimentalKairosApi
+fun <In, Out> HasNetwork.CoalescingMutableEvents(
+    coalesce: (old: Out, new: In) -> Out,
+    getInitialValue: () -> Out,
+): CoalescingMutableEvents<In, Out> =
+    CoalescingMutableEvents(kairosNetwork, coalesce, getInitialValue)
+
+/** Returns a [CoalescingMutableEvents] that can emit values into this [KairosNetwork]. */
+@ExperimentalKairosApi
+fun <T> HasNetwork.ConflatedMutableEvents(): CoalescingMutableEvents<T, T> =
+    ConflatedMutableEvents(kairosNetwork)
diff --git a/media/java/android/media/quality/ParamCapability.aidl b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/KairosScope.kt
similarity index 64%
copy from media/java/android/media/quality/ParamCapability.aidl
copy to packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/KairosScope.kt
index b43409d..e526f45 100644
--- a/media/java/android/media/quality/ParamCapability.aidl
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/KairosScope.kt
@@ -14,6 +14,13 @@
  * limitations under the License.
  */
 
-package android.media.quality;
+package com.android.systemui.kairos
 
-parcelable ParamCapability;
+/** Denotes [KairosScope] interfaces as [DSL markers][DslMarker]. */
+@DslMarker annotation class KairosScopeMarker
+
+/**
+ * Base scope for all Kairos scopes. Used to prevent implicitly capturing other scopes from inner
+ * lambdas.
+ */
+@KairosScopeMarker @ExperimentalKairosApi interface KairosScope
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Merge.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Merge.kt
new file mode 100644
index 0000000..de9dca4
--- /dev/null
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Merge.kt
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.kairos
+
+import com.android.systemui.kairos.internal.awaitValues
+import com.android.systemui.kairos.internal.constInit
+import com.android.systemui.kairos.internal.mapImpl
+import com.android.systemui.kairos.internal.mergeNodes
+import com.android.systemui.kairos.internal.mergeNodesLeft
+import com.android.systemui.kairos.internal.store.ConcurrentHashMapK
+import com.android.systemui.kairos.internal.switchDeferredImpl
+import com.android.systemui.kairos.internal.switchPromptImpl
+import com.android.systemui.kairos.util.map
+
+/**
+ * Merges the given [Events] into a single [Events] that emits events from both.
+ *
+ * Because [Events] can only emit one value per transaction, the provided [transformCoincidence]
+ * function is used to combine coincident emissions to produce the result value to be emitted by the
+ * merged [Events].
+ *
+ * ``` kotlin
+ * fun <A> Events<A>.mergeWith(
+ *     other: Events<A>,
+ *     transformCoincidence: TransactionScope.(A, A) -> A = { a, _ -> a },
+ * ): Events<A> =
+ *     listOf(this, other).merge().map { it.reduce(transformCoincidence) }
+ * ```
+ *
+ * @see merge
+ */
+@ExperimentalKairosApi
+fun <A> Events<A>.mergeWith(
+    other: Events<A>,
+    transformCoincidence: TransactionScope.(A, A) -> A = { a, _ -> a },
+): Events<A> {
+    val node =
+        mergeNodes(
+            getPulse = { init.connect(evalScope = this) },
+            getOther = { other.init.connect(evalScope = this) },
+        ) { a, b ->
+            transformCoincidence(a, b)
+        }
+    return EventsInit(constInit(name = null, node))
+}
+
+/**
+ * Merges the given [Events] into a single [Events] that emits events from all. All coincident
+ * emissions are collected into the emitted [List], preserving the input ordering.
+ *
+ * ``` kotlin
+ *   fun <A> merge(vararg events: Events<A>): Events<List<A>> = events.asIterable().merge()
+ * ```
+ *
+ * @see mergeWith
+ * @see mergeLeft
+ */
+@ExperimentalKairosApi
+fun <A> merge(vararg events: Events<A>): Events<List<A>> = events.asIterable().merge()
+
+/**
+ * Merges the given [Events] into a single [Events] that emits events from all. In the case of
+ * coincident emissions, the emission from the left-most [Events] is emitted.
+ *
+ * ``` kotlin
+ *   fun <A> mergeLeft(vararg events: Events<A>): Events<A> = events.asIterable().mergeLeft()
+ * ```
+ *
+ * @see merge
+ */
+@ExperimentalKairosApi
+fun <A> mergeLeft(vararg events: Events<A>): Events<A> = events.asIterable().mergeLeft()
+
+/**
+ * Merges the given [Events] into a single [Events] that emits events from all.
+ *
+ * Because [Events] can only emit one value per transaction, the provided [transformCoincidence]
+ * function is used to combine coincident emissions to produce the result value to be emitted by the
+ * merged [Events].
+ *
+ * ``` kotlin
+ *   fun <A> merge(vararg events: Events<A>, transformCoincidence: (A, A) -> A): Events<A> =
+ *       merge(*events).map { l -> l.reduce(transformCoincidence) }
+ * ```
+ */
+fun <A> merge(vararg events: Events<A>, transformCoincidence: (A, A) -> A): Events<A> =
+    merge(*events).map { l -> l.reduce(transformCoincidence) }
+
+/**
+ * Merges the given [Events] into a single [Events] that emits events from all. All coincident
+ * emissions are collected into the emitted [List], preserving the input ordering.
+ *
+ * @sample com.android.systemui.kairos.KairosSamples.merge
+ * @see mergeWith
+ * @see mergeLeft
+ */
+@ExperimentalKairosApi
+fun <A> Iterable<Events<A>>.merge(): Events<List<A>> =
+    EventsInit(constInit(name = null, mergeNodes { map { it.init.connect(evalScope = this) } }))
+
+/**
+ * Merges the given [Events] into a single [Events] that emits events from all. In the case of
+ * coincident emissions, the emission from the left-most [Events] is emitted.
+ *
+ * Semantically equivalent to the following definition:
+ * ``` kotlin
+ *   fun <A> Iterable<Events<A>>.mergeLeft(): Events<A> =
+ *       merge().mapCheap { it.first() }
+ * ```
+ *
+ * In reality, the implementation avoids allocating the intermediate list of all coincident
+ * emissions.
+ *
+ * @see merge
+ */
+@ExperimentalKairosApi
+fun <A> Iterable<Events<A>>.mergeLeft(): Events<A> =
+    EventsInit(constInit(name = null, mergeNodesLeft { map { it.init.connect(evalScope = this) } }))
+
+/**
+ * Creates a new [Events] that emits events from all given [Events]. All simultaneous emissions are
+ * collected into the emitted [List], preserving the input ordering.
+ *
+ * ``` kotlin
+ *   fun <A> Sequence<Events<A>>.merge(): Events<List<A>> = asIterable().merge()
+ * ```
+ *
+ * @see mergeWith
+ */
+@ExperimentalKairosApi fun <A> Sequence<Events<A>>.merge(): Events<List<A>> = asIterable().merge()
+
+/**
+ * Creates a new [Events] that emits events from all given [Events]. All simultaneous emissions are
+ * collected into the emitted [Map], and are given the same key of the associated [Events] in the
+ * input [Map].
+ *
+ * ``` kotlin
+ *   fun <K, A> Map<K, Events<A>>.merge(): Events<Map<K, A>> =
+ *       asSequence()
+ *           .map { (k, events) -> events.map { a -> k to a } }
+ *           .toList()
+ *           .merge()
+ *           .map { it.toMap() }
+ * ```
+ *
+ * @see merge
+ */
+@ExperimentalKairosApi
+fun <K, A> Map<K, Events<A>>.merge(): Events<Map<K, A>> =
+    asSequence()
+        .map { (k, events) -> events.map { a -> k to a } }
+        .toList()
+        .merge()
+        .map { it.toMap() }
+
+/**
+ * Returns an [Events] that emits from a merged, incrementally-accumulated collection of [Events]
+ * emitted from this, following the patch rules outlined in
+ * [Map.applyPatch][com.android.systemui.kairos.util.applyPatch].
+ *
+ * Conceptually this is equivalent to:
+ * ``` kotlin
+ *   fun <K, V> State<Map<K, V>>.mergeEventsIncrementally(): Events<Map<K, V>> =
+ *       map { it.merge() }.switchEvents()
+ * ```
+ *
+ * While the behavior is equivalent to the conceptual definition above, the implementation is
+ * significantly more efficient.
+ *
+ * @sample com.android.systemui.kairos.KairosSamples.mergeEventsIncrementally
+ * @see merge
+ */
+fun <K, V> Incremental<K, Events<V>>.mergeEventsIncrementally(): Events<Map<K, V>> {
+    val operatorName = "mergeEventsIncrementally"
+    val name = operatorName
+    val patches =
+        mapImpl({ init.connect(this).patches }) { patch, _ ->
+            patch.mapValues { (_, m) -> m.map { events -> events.init.connect(this) } }.asIterable()
+        }
+    return EventsInit(
+        constInit(
+            name,
+            switchDeferredImpl(
+                    name = name,
+                    getStorage = {
+                        init
+                            .connect(this)
+                            .getCurrentWithEpoch(this)
+                            .first
+                            .mapValues { (_, events) -> events.init.connect(this) }
+                            .asIterable()
+                    },
+                    getPatches = { patches },
+                    storeFactory = ConcurrentHashMapK.Factory(),
+                )
+                .awaitValues(),
+        )
+    )
+}
+
+/**
+ * Returns an [Events] that emits from a merged, incrementally-accumulated collection of [Events]
+ * emitted from this, following the patch rules outlined in
+ * [Map.applyPatch][com.android.systemui.kairos.util.applyPatch].
+ *
+ * Conceptually this is equivalent to:
+ * ``` kotlin
+ *   fun <K, V> State<Map<K, V>>.mergeEventsIncrementallyPromptly(): Events<Map<K, V>> =
+ *       map { it.merge() }.switchEventsPromptly()
+ * ```
+ *
+ * While the behavior is equivalent to the conceptual definition above, the implementation is
+ * significantly more efficient.
+ *
+ * @sample com.android.systemui.kairos.KairosSamples.mergeEventsIncrementallyPromptly
+ * @see merge
+ */
+fun <K, V> Incremental<K, Events<V>>.mergeEventsIncrementallyPromptly(): Events<Map<K, V>> {
+    val operatorName = "mergeEventsIncrementallyPromptly"
+    val name = operatorName
+    val patches =
+        mapImpl({ init.connect(this).patches }) { patch, _ ->
+            patch.mapValues { (_, m) -> m.map { events -> events.init.connect(this) } }.asIterable()
+        }
+    return EventsInit(
+        constInit(
+            name,
+            switchPromptImpl(
+                    name = name,
+                    getStorage = {
+                        init
+                            .connect(this)
+                            .getCurrentWithEpoch(this)
+                            .first
+                            .mapValues { (_, events) -> events.init.connect(this) }
+                            .asIterable()
+                    },
+                    getPatches = { patches },
+                    storeFactory = ConcurrentHashMapK.Factory(),
+                )
+                .awaitValues(),
+        )
+    )
+}
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Modes.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Modes.kt
new file mode 100644
index 0000000..6c070a6
--- /dev/null
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Modes.kt
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.kairos
+
+/**
+ * A modal Kairos sub-network.
+ *
+ * When [enabled][enableMode], all network modifications are applied immediately to the Kairos
+ * network. When the returned [Events] emits a [BuildMode], that mode is enabled and replaces this
+ * mode, undoing all modifications in the process (any registered [observers][BuildScope.observe]
+ * are unregistered, and any pending [side-effects][BuildScope.effect] are cancelled).
+ *
+ * Use [compiledBuildSpec] to compile and stand-up a mode graph.
+ *
+ * @see StatefulMode
+ */
+@ExperimentalKairosApi
+fun interface BuildMode<out A> {
+    /**
+     * Invoked when this mode is enabled. Returns a value and an [Events] that signals a switch to a
+     * new mode.
+     */
+    fun BuildScope.enableMode(): Pair<A, Events<BuildMode<A>>>
+}
+
+/**
+ * Returns a [BuildSpec] that, when [applied][BuildScope.applySpec], stands up a modal-transition
+ * graph starting with this [BuildMode], automatically switching to new modes as they are produced.
+ *
+ * @see BuildMode
+ */
+@ExperimentalKairosApi
+val <A> BuildMode<A>.compiledBuildSpec: BuildSpec<State<A>>
+    get() = buildSpec {
+        var modeChangeEvents by EventsLoop<BuildMode<A>>()
+        val activeMode: State<Pair<A, Events<BuildMode<A>>>> =
+            modeChangeEvents
+                .map { it.run { buildSpec { enableMode() } } }
+                .holdLatestSpec(buildSpec { enableMode() })
+        modeChangeEvents =
+            activeMode
+                .map { statefully { it.second.nextOnly() } }
+                .applyLatestStateful()
+                .switchEvents()
+        activeMode.map { it.first }
+    }
+
+/**
+ * A modal Kairos sub-network.
+ *
+ * When [enabled][enableMode], all state accumulation is immediately started. When the returned
+ * [Events] emits a [BuildMode], that mode is enabled and replaces this mode, stopping all state
+ * accumulation in the process.
+ *
+ * Use [compiledStateful] to compile and stand-up a mode graph.
+ *
+ * @see BuildMode
+ */
+@ExperimentalKairosApi
+fun interface StatefulMode<out A> {
+    /**
+     * Invoked when this mode is enabled. Returns a value and an [Events] that signals a switch to a
+     * new mode.
+     */
+    fun StateScope.enableMode(): Pair<A, Events<StatefulMode<A>>>
+}
+
+/**
+ * Returns a [Stateful] that, when [applied][StateScope.applyStateful], stands up a modal-transition
+ * graph starting with this [StatefulMode], automatically switching to new modes as they are
+ * produced.
+ *
+ * @see StatefulMode
+ */
+@ExperimentalKairosApi
+val <A> StatefulMode<A>.compiledStateful: Stateful<State<A>>
+    get() = statefully {
+        var modeChangeEvents by EventsLoop<StatefulMode<A>>()
+        val activeMode: State<Pair<A, Events<StatefulMode<A>>>> =
+            modeChangeEvents
+                .map { it.run { statefully { enableMode() } } }
+                .holdLatestStateful(statefully { enableMode() })
+        modeChangeEvents =
+            activeMode
+                .map { statefully { it.second.nextOnly() } }
+                .applyLatestStateful()
+                .switchEvents()
+        activeMode.map { it.first }
+    }
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Selector.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Selector.kt
new file mode 100644
index 0000000..f7decbb
--- /dev/null
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Selector.kt
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.kairos
+
+import com.android.systemui.kairos.internal.DerivedMapCheap
+import com.android.systemui.kairos.internal.StateImpl
+import com.android.systemui.kairos.internal.init
+
+/**
+ * Returns a [StateSelector] that can be used to efficiently check if the input [State] is currently
+ * holding a specific value.
+ *
+ * An example:
+ * ```
+ *   val intState: State<Int> = ...
+ *   val intSelector: StateSelector<Int> = intState.selector()
+ *   // Tracks if intState is holding 1
+ *   val isOne: State<Boolean> = intSelector.whenSelected(1)
+ * ```
+ *
+ * This is semantically equivalent to `val isOne = intState.map { i -> i == 1 }`, but is
+ * significantly more efficient; specifically, using [State.map] in this way incurs a `O(n)`
+ * performance hit, where `n` is the number of different [State.map] operations used to track a
+ * specific value. [selector] internally uses a [HashMap] to lookup the appropriate downstream
+ * [State] to update, and so operates in `O(1)`.
+ *
+ * Note that the returned [StateSelector] should be cached and re-used to gain the performance
+ * benefit.
+ *
+ * @see groupByKey
+ */
+@ExperimentalKairosApi
+fun <A> State<A>.selector(numDistinctValues: Int? = null): StateSelector<A> =
+    StateSelector(
+        this,
+        changes
+            .map { new -> mapOf(new to true, sampleDeferred().value to false) }
+            .groupByKey(numDistinctValues),
+    )
+
+/**
+ * Tracks the currently selected value of type [A] from an upstream [State].
+ *
+ * @see selector
+ */
+@ExperimentalKairosApi
+class StateSelector<in A>
+internal constructor(
+    private val upstream: State<A>,
+    private val groupedChanges: GroupedEvents<A, Boolean>,
+) {
+    /**
+     * Returns a [State] that tracks whether the upstream [State] is currently holding the given
+     * [value].
+     *
+     * @see selector
+     */
+    fun whenSelected(value: A): State<Boolean> {
+        val operatorName = "StateSelector#whenSelected"
+        val name = "$operatorName[$value]"
+        return StateInit(
+            init(name) {
+                StateImpl(
+                    name,
+                    operatorName,
+                    groupedChanges.impl.eventsForKey(value),
+                    DerivedMapCheap(upstream.init) { it == value },
+                )
+            }
+        )
+    }
+
+    operator fun get(value: A): State<Boolean> = whenSelected(value)
+}
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/State.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/State.kt
new file mode 100644
index 0000000..22ca83c
--- /dev/null
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/State.kt
@@ -0,0 +1,346 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.kairos
+
+import com.android.systemui.kairos.internal.CompletableLazy
+import com.android.systemui.kairos.internal.EventsImpl
+import com.android.systemui.kairos.internal.Init
+import com.android.systemui.kairos.internal.InitScope
+import com.android.systemui.kairos.internal.Network
+import com.android.systemui.kairos.internal.NoScope
+import com.android.systemui.kairos.internal.Schedulable
+import com.android.systemui.kairos.internal.StateImpl
+import com.android.systemui.kairos.internal.StateSource
+import com.android.systemui.kairos.internal.activated
+import com.android.systemui.kairos.internal.cached
+import com.android.systemui.kairos.internal.constInit
+import com.android.systemui.kairos.internal.constState
+import com.android.systemui.kairos.internal.filterImpl
+import com.android.systemui.kairos.internal.flatMapStateImpl
+import com.android.systemui.kairos.internal.init
+import com.android.systemui.kairos.internal.mapImpl
+import com.android.systemui.kairos.internal.mapStateImpl
+import com.android.systemui.kairos.internal.mapStateImplCheap
+import com.android.systemui.kairos.internal.util.hashString
+import com.android.systemui.kairos.util.WithPrev
+import kotlin.reflect.KProperty
+
+/**
+ * A time-varying value with discrete changes. Conceptually, a combination of a [Transactional] that
+ * holds a value, and an [Events] that emits when the value [changes].
+ *
+ * [States][State] follow these rules:
+ * 1. In the same transaction that [changes] emits a new value, [sample] will continue to return the
+ *    previous value.
+ * 2. Unless it is [constant][stateOf], [States][State] can only be created via [StateScope]
+ *    operations, or derived from other existing [States][State] via [State.map], [combine], etc.
+ * 3. [States][State] can only be [sampled][TransactionScope.sample] within a [TransactionScope].
+ *
+ * @sample com.android.systemui.kairos.KairosSamples.states
+ */
+@ExperimentalKairosApi
+sealed class State<out A> {
+    internal abstract val init: Init<StateImpl<A>>
+}
+
+/**
+ * Returns a constant [State] that never changes. [changes] is equivalent to [emptyEvents] and
+ * [TransactionScope.sample] will always produce [value].
+ */
+@ExperimentalKairosApi
+fun <A> stateOf(value: A): State<A> {
+    val operatorName = "stateOf"
+    val name = "$operatorName($value)"
+    return StateInit(constInit(name, constState(name, operatorName, value)))
+}
+
+/**
+ * Returns a [State] that acts as a deferred-reference to the [State] produced by this [Lazy].
+ *
+ * When the returned [State] is accessed by the Kairos network, the [Lazy]'s [value][Lazy.value]
+ * will be queried and used.
+ *
+ * Useful for recursive definitions.
+ *
+ * ``` kotlin
+ *   fun <A> Lazy<State<A>>.defer() = deferredState { value }
+ * ```
+ */
+@ExperimentalKairosApi fun <A> Lazy<State<A>>.defer(): State<A> = deferInline { value }
+
+/**
+ * Returns a [State] that acts as a deferred-reference to the [State] produced by this
+ * [DeferredValue].
+ *
+ * When the returned [State] is accessed by the Kairos network, the [DeferredValue] will be queried
+ * and used.
+ *
+ * Useful for recursive definitions.
+ *
+ * ``` kotlin
+ *   fun <A> DeferredValue<State<A>>.defer() = deferredState { get() }
+ * ```
+ */
+@ExperimentalKairosApi
+fun <A> DeferredValue<State<A>>.defer(): State<A> = deferInline { unwrapped.value }
+
+/**
+ * Returns a [State] that acts as a deferred-reference to the [State] produced by [block].
+ *
+ * When the returned [State] is accessed by the Kairos network, [block] will be invoked and the
+ * returned [State] will be used.
+ *
+ * Useful for recursive definitions.
+ */
+@ExperimentalKairosApi
+fun <A> deferredState(block: KairosScope.() -> State<A>): State<A> = deferInline { NoScope.block() }
+
+/**
+ * Returns a [State] containing the results of applying [transform] to the value held by the
+ * original [State].
+ *
+ * @sample com.android.systemui.kairos.KairosSamples.mapState
+ */
+@ExperimentalKairosApi
+fun <A, B> State<A>.map(transform: KairosScope.(A) -> B): State<B> {
+    val operatorName = "map"
+    val name = operatorName
+    return StateInit(
+        init(name) {
+            mapStateImpl({ init.connect(this) }, name, operatorName) { NoScope.transform(it) }
+        }
+    )
+}
+
+/**
+ * Returns a [State] that transforms the value held inside this [State] by applying it to the
+ * [transform].
+ *
+ * Note that unlike [State.map], the result is not cached. This means that not only should
+ * [transform] be fast and pure, it should be *monomorphic* (1-to-1). Failure to do this means that
+ * [changes] for the returned [State] will operate unexpectedly, emitting at rates that do not
+ * reflect an observable change to the returned [State].
+ *
+ * @see State.map
+ */
+@ExperimentalKairosApi
+fun <A, B> State<A>.mapCheapUnsafe(transform: KairosScope.(A) -> B): State<B> {
+    val operatorName = "map"
+    val name = operatorName
+    return StateInit(
+        init(name) { mapStateImplCheap(init, name, operatorName) { NoScope.transform(it) } }
+    )
+}
+
+/**
+ * Splits a [State] of pairs into a pair of [Events][State], where each returned [State] holds half
+ * of the original.
+ *
+ * ``` kotlin
+ *   fun <A, B> State<Pair<A, B>>.unzip(): Pair<State<A>, State<B>> {
+ *       val first = map { it.first }
+ *       val second = map { it.second }
+ *       return first to second
+ *   }
+ * ```
+ */
+@ExperimentalKairosApi
+fun <A, B> State<Pair<A, B>>.unzip(): Pair<State<A>, State<B>> {
+    val first = map { it.first }
+    val second = map { it.second }
+    return first to second
+}
+
+/**
+ * Returns a [State] by applying [transform] to the value held by the original [State].
+ *
+ * @sample com.android.systemui.kairos.KairosSamples.flatMap
+ */
+@ExperimentalKairosApi
+fun <A, B> State<A>.flatMap(transform: KairosScope.(A) -> State<B>): State<B> {
+    val operatorName = "flatMap"
+    val name = operatorName
+    return StateInit(
+        init(name) {
+            flatMapStateImpl({ init.connect(this) }, name, operatorName) { a ->
+                NoScope.transform(a).init.connect(this)
+            }
+        }
+    )
+}
+
+/**
+ * Returns a [State] that behaves like the current value of the original [State].
+ *
+ * ``` kotlin
+ *   fun <A> State<State<A>>.flatten() = flatMap { it }
+ * ```
+ *
+ * @see flatMap
+ */
+@ExperimentalKairosApi fun <A> State<State<A>>.flatten() = flatMap { it }
+
+/**
+ * A mutable [State] that provides the ability to manually [set its value][setValue].
+ *
+ * Multiple invocations of [setValue] that occur before a transaction are conflated; only the most
+ * recent value is used.
+ *
+ * Effectively equivalent to:
+ * ``` kotlin
+ *     ConflatedMutableEvents(kairosNetwork).holdState(initialValue)
+ * ```
+ */
+@ExperimentalKairosApi
+class MutableState<T> internal constructor(internal val network: Network, initialValue: Lazy<T>) :
+    State<T>() {
+
+    private val input: CoalescingMutableEvents<Lazy<T>, Lazy<T>?> =
+        CoalescingMutableEvents(
+            name = null,
+            coalesce = { _, new -> new },
+            network = network,
+            getInitialValue = { null },
+        )
+
+    override val init: Init<StateImpl<T>>
+        get() = state.init
+
+    // TODO: not convinced this is totally safe
+    //  - at least for the BuildScope smart-constructor, we can avoid the network.transaction { }
+    //    call since we're already in a transaction
+    internal val state = run {
+        val changes = input.impl
+        val name = null
+        val operatorName = "MutableState"
+        val state: StateSource<T> = StateSource(initialValue)
+        val mapImpl = mapImpl(upstream = { changes.activated() }) { it, _ -> it!!.value }
+        val calm: EventsImpl<T> =
+            filterImpl({ mapImpl }) { new ->
+                    new != state.getCurrentWithEpoch(evalScope = this).first
+                }
+                .cached()
+        @Suppress("DeferredResultUnused")
+        network.transaction("MutableState.init") {
+            calm.activate(evalScope = this, downstream = Schedulable.S(state))?.let {
+                (connection, needsEval) ->
+                state.upstreamConnection = connection
+                if (needsEval) {
+                    schedule(state)
+                }
+            }
+        }
+        StateInit(constInit(name, StateImpl(name, operatorName, calm, state)))
+    }
+
+    /**
+     * Sets the value held by this [State].
+     *
+     * Invoking will cause a [state change event][State.changes] to emit with the new value, which
+     * will then be applied (and thus returned by [TransactionScope.sample]) after the transaction
+     * is complete.
+     *
+     * Multiple invocations of [setValue] that occur before a transaction are conflated; only the
+     * most recent value is used.
+     */
+    fun setValue(value: T) = input.emit(CompletableLazy(value))
+
+    /**
+     * Sets the value held by this [State]. The [DeferredValue] will not be queried until this
+     * [State] is explicitly [sampled][TransactionScope.sample] or [observed][BuildScope.observe].
+     *
+     * Invoking will cause a [state change event][State.changes] to emit with the new value, which
+     * will then be applied (and thus returned by [TransactionScope.sample]) after the transaction
+     * is complete.
+     *
+     * Multiple invocations of [setValue] that occur before a transaction are conflated; only the
+     * most recent value is used.
+     */
+    fun setValueDeferred(value: DeferredValue<T>) = input.emit(value.unwrapped)
+}
+
+/**
+ * A forward-reference to a [State]. Useful for recursive definitions.
+ *
+ * This reference can be used like a standard [State], but will throw an error if its [loopback] is
+ * unset before it is [observed][BuildScope.observe] or [sampled][TransactionScope.sample].
+ *
+ * Note that it is safe to invoke [TransactionScope.sampleDeferred] before [loopback] is set,
+ * provided the returned [DeferredValue] is not [queried][KairosScope.get].
+ *
+ * @sample com.android.systemui.kairos.KairosSamples.stateLoop
+ */
+@ExperimentalKairosApi
+class StateLoop<A> : State<A>() {
+
+    private val name: String? = null
+
+    private val deferred = CompletableLazy<State<A>>()
+
+    override val init: Init<StateImpl<A>> =
+        init(name) { deferred.value.init.connect(evalScope = this) }
+
+    /**
+     * The [State] this reference is referring to. Must be set before this [StateLoop] is
+     * [observed][BuildScope.observe] or [sampled][TransactionScope.sample].
+     */
+    var loopback: State<A>? = null
+        set(value) {
+            value?.let {
+                check(!deferred.isInitialized()) { "StateLoop.loopback has already been set." }
+                deferred.setValue(value)
+                field = value
+            }
+        }
+
+    operator fun getValue(thisRef: Any?, property: KProperty<*>): State<A> = this
+
+    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: State<A>) {
+        loopback = value
+    }
+
+    override fun toString(): String = "${this::class.simpleName}@$hashString"
+}
+
+internal class StateInit<A> internal constructor(override val init: Init<StateImpl<A>>) :
+    State<A>() {
+    override fun toString(): String = "${this::class.simpleName}@$hashString"
+}
+
+private inline fun <A> deferInline(crossinline block: InitScope.() -> State<A>): State<A> =
+    StateInit(init(name = null) { block().init.connect(evalScope = this) })
+
+/**
+ * Like [changes] but also includes the old value of this [State].
+ *
+ * Shorthand for:
+ * ``` kotlin
+ *     stateChanges.map { WithPrev(previousValue = sample(), newValue = it) }
+ * ```
+ */
+@ExperimentalKairosApi
+val <A> State<A>.transitions: Events<WithPrev<A, A>>
+    get() = changes.map { WithPrev(previousValue = sample(), newValue = it) }
+
+/**
+ * Returns an [Events] that emits the new value of this [State] when it changes.
+ *
+ * @sample com.android.systemui.kairos.KairosSamples.changes
+ */
+@ExperimentalKairosApi
+val <A> State<A>.changes: Events<A>
+    get() = EventsInit(init(name = null) { init.connect(evalScope = this).changes })
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/StateScope.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/StateScope.kt
new file mode 100644
index 0000000..faeffe8
--- /dev/null
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/StateScope.kt
@@ -0,0 +1,1040 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.kairos
+
+import com.android.systemui.kairos.util.MapPatch
+import com.android.systemui.kairos.util.Maybe
+import com.android.systemui.kairos.util.WithPrev
+import com.android.systemui.kairos.util.map
+import com.android.systemui.kairos.util.mapMaybeValues
+import com.android.systemui.kairos.util.zipWith
+
+// TODO: caching story? should each Scope have a cache of applied Stateful instances?
+/** A computation that can accumulate [Events] into [State]. */
+typealias Stateful<R> = StateScope.() -> R
+
+/**
+ * Returns a [Stateful] that, when [applied][StateScope.applyStateful], invokes [block] with the
+ * applier's [StateScope].
+ */
+@ExperimentalKairosApi
+@Suppress("NOTHING_TO_INLINE")
+inline fun <A> statefully(noinline block: StateScope.() -> A): Stateful<A> = block
+
+/**
+ * Operations that accumulate state within the Kairos network.
+ *
+ * State accumulation is an ongoing process that has a lifetime. Use `-Latest` combinators, such as
+ * [mapLatestStateful], to create smaller, nested lifecycles so that accumulation isn't running
+ * longer than needed.
+ */
+@ExperimentalKairosApi
+interface StateScope : TransactionScope {
+
+    /**
+     * Defers invoking [block] until after the current [StateScope] code-path completes, returning a
+     * [DeferredValue] that can be used to reference the result.
+     *
+     * Useful for recursive definitions.
+     *
+     * @see DeferredValue
+     */
+    fun <A> deferredStateScope(block: StateScope.() -> A): DeferredValue<A>
+
+    /**
+     * Returns a [State] that holds onto the most recently emitted value from this [Events], or
+     * [initialValue] if nothing has been emitted since it was constructed.
+     *
+     * Note that the value contained within the [State] is not updated until *after* all [Events]
+     * have been processed; this keeps the value of the [State] consistent during the entire Kairos
+     * transaction.
+     *
+     * @see holdState
+     */
+    fun <A> Events<A>.holdStateDeferred(initialValue: DeferredValue<A>): State<A>
+
+    /**
+     * Returns a [State] holding a [Map] that is updated incrementally whenever this emits a value.
+     *
+     * The value emitted is used as a "patch" for the tracked [Map]; for each key [K] in the emitted
+     * map, an associated value of [present][Maybe.present] will insert or replace the value in the
+     * tracked [Map], and an associated value of [absent][Maybe.absent] will remove the key from the
+     * tracked [Map].
+     *
+     * @sample com.android.systemui.kairos.KairosSamples.incrementals
+     * @see MapPatch
+     */
+    fun <K, V> Events<MapPatch<K, V>>.foldStateMapIncrementally(
+        initialValues: DeferredValue<Map<K, V>>
+    ): Incremental<K, V>
+
+    /**
+     * Returns an [Events] the emits the result of applying [Statefuls][Stateful] emitted from the
+     * original [Events].
+     *
+     * Unlike [applyLatestStateful], state accumulation is not stopped with each subsequent emission
+     * of the original [Events].
+     */
+    fun <A> Events<Stateful<A>>.applyStatefuls(): Events<A>
+
+    /**
+     * Returns an [Events] containing the results of applying each [Stateful] emitted from the
+     * original [Events], and a [DeferredValue] containing the result of applying [init]
+     * immediately.
+     *
+     * If the [Maybe] contained within the value for an associated key is [absent][Maybe.absent],
+     * then the previously-active [Stateful] will be stopped with no replacement.
+     *
+     * When each [Stateful] is applied, state accumulation from the previously-active [Stateful]
+     * with the same key is stopped.
+     *
+     * The optional [numKeys] argument is an optimization used to initialize the internal storage.
+     */
+    fun <K, A, B> Events<MapPatch<K, Stateful<A>>>.applyLatestStatefulForKey(
+        init: DeferredValue<Map<K, Stateful<B>>>,
+        numKeys: Int? = null,
+    ): Pair<Events<MapPatch<K, A>>, DeferredValue<Map<K, B>>>
+
+    // TODO: everything below this comment can be made into extensions once we have context params
+
+    /**
+     * Returns an [Events] that emits from a merged, incrementally-accumulated collection of
+     * [Events] emitted from this, following the patch rules outlined in
+     * [Map.applyPatch][com.android.systemui.kairos.util.applyPatch].
+     *
+     * ``` kotlin
+     *   fun <K, V> Events<MapPatch<K, Events<V>>>.mergeEventsIncrementally(
+     *     initialEvents: DeferredValue<Map<K, Events<V>>>,
+     *   ): Events<Map<K, V>> =
+     *     foldMapIncrementally(initialEvents).mergeEventsIncrementally(initialEvents)
+     * ```
+     *
+     * @see Incremental.mergeEventsIncrementally
+     * @see merge
+     */
+    fun <K, V> Events<MapPatch<K, Events<V>>>.mergeEventsIncrementally(
+        initialEvents: DeferredValue<Map<K, Events<V>>>
+    ): Events<Map<K, V>> = foldStateMapIncrementally(initialEvents).mergeEventsIncrementally()
+
+    /**
+     * Returns an [Events] that emits from a merged, incrementally-accumulated collection of
+     * [Events] emitted from this, following the patch rules outlined in
+     * [Map.applyPatch][com.android.systemui.kairos.util.applyPatch].
+     *
+     * ``` kotlin
+     *   fun <K, V> Events<MapPatch<K, Events<V>>>.mergeEventsIncrementallyPromptly(
+     *     initialEvents: DeferredValue<Map<K, Events<V>>>,
+     *   ): Events<Map<K, V>> =
+     *     foldMapIncrementally(initialEvents).mergeEventsIncrementallyPromptly(initialEvents)
+     * ```
+     *
+     * @see Incremental.mergeEventsIncrementallyPromptly
+     * @see merge
+     */
+    fun <K, V> Events<MapPatch<K, Events<V>>>.mergeEventsIncrementallyPromptly(
+        initialEvents: DeferredValue<Map<K, Events<V>>>
+    ): Events<Map<K, V>> =
+        foldStateMapIncrementally(initialEvents).mergeEventsIncrementallyPromptly()
+
+    /**
+     * Returns an [Events] that emits from a merged, incrementally-accumulated collection of
+     * [Events] emitted from this, following the patch rules outlined in
+     * [Map.applyPatch][com.android.systemui.kairos.util.applyPatch].
+     *
+     * ``` kotlin
+     *   fun <K, V> Events<MapPatch<K, Events<V>>>.mergeEventsIncrementally(
+     *     initialEvents: Map<K, Events<V>>,
+     *   ): Events<Map<K, V>> =
+     *     foldMapIncrementally(initialEvents).mergeEventsIncrementally(initialEvents)
+     * ```
+     *
+     * @see Incremental.mergeEventsIncrementally
+     * @see merge
+     */
+    fun <K, V> Events<MapPatch<K, Events<V>>>.mergeEventsIncrementally(
+        initialEvents: Map<K, Events<V>> = emptyMap()
+    ): Events<Map<K, V>> = mergeEventsIncrementally(deferredOf(initialEvents))
+
+    /**
+     * Returns an [Events] that emits from a merged, incrementally-accumulated collection of
+     * [Events] emitted from this, following the patch rules outlined in
+     * [Map.applyPatch][com.android.systemui.kairos.util.applyPatch].
+     *
+     * ``` kotlin
+     *   fun <K, V> Events<MapPatch<K, Events<V>>>.mergeEventsIncrementallyPromptly(
+     *     initialEvents: Map<K, Events<V>>,
+     *   ): Events<Map<K, V>> =
+     *     foldMapIncrementally(initialEvents).mergeEventsIncrementallyPromptly(initialEvents)
+     * ```
+     *
+     * @see Incremental.mergeEventsIncrementallyPromptly
+     * @see merge
+     */
+    fun <K, V> Events<MapPatch<K, Events<V>>>.mergeEventsIncrementallyPromptly(
+        initialEvents: Map<K, Events<V>> = emptyMap()
+    ): Events<Map<K, V>> = mergeEventsIncrementallyPromptly(deferredOf(initialEvents))
+
+    /** Applies the [Stateful] within this [StateScope]. */
+    fun <A> Stateful<A>.applyStateful(): A = this()
+
+    /**
+     * Applies the [Stateful] within this [StateScope], returning the result as a [DeferredValue].
+     */
+    fun <A> Stateful<A>.applyStatefulDeferred(): DeferredValue<A> = deferredStateScope {
+        applyStateful()
+    }
+
+    /**
+     * Returns a [State] that holds onto the most recently emitted value from this [Events], or
+     * [initialValue] if nothing has been emitted since it was constructed.
+     *
+     * Note that the value contained within the [State] is not updated until *after* all [Events]
+     * have been processed; this keeps the value of the [State] consistent during the entire Kairos
+     * transaction.
+     *
+     * @sample com.android.systemui.kairos.KairosSamples.holdState
+     * @see holdStateDeferred
+     */
+    fun <A> Events<A>.holdState(initialValue: A): State<A> =
+        holdStateDeferred(deferredOf(initialValue))
+
+    /**
+     * Returns an [Events] containing the results of applying [transform] to each value of the
+     * original [Events].
+     *
+     * [transform] can perform state accumulation via its [StateScope] receiver. Unlike
+     * [mapLatestStateful], accumulation is not stopped with each subsequent emission of the
+     * original [Events].
+     *
+     * ``` kotlin
+     *   fun <A, B> Events<A>.mapStateful(transform: StateScope.(A) -> B): Events<B> =
+     *       map { statefully { transform(it) } }.applyStatefuls()
+     * ```
+     */
+    fun <A, B> Events<A>.mapStateful(transform: StateScope.(A) -> B): Events<B> =
+        mapCheap { statefully { transform(it) } }.applyStatefuls()
+
+    /**
+     * Returns a [State] the holds the result of applying the [Stateful] held by the original
+     * [State].
+     *
+     * Unlike [applyLatestStateful], state accumulation is not stopped with each state change.
+     *
+     * ``` kotlin
+     *   fun <A> State<Stateful<A>>.applyStatefuls(): State<A> =
+     *       changes
+     *           .applyStatefuls()
+     *           .holdState(initialValue = sample().applyStateful())
+     * ```
+     */
+    fun <A> State<Stateful<A>>.applyStatefuls(): State<A> =
+        changes
+            .applyStatefuls()
+            .holdStateDeferred(
+                initialValue = deferredStateScope { sampleDeferred().value.applyStateful() }
+            )
+
+    /**
+     * Returns an [Events] that acts like the most recent [Events] to be emitted from the original
+     * [Events].
+     *
+     * ``` kotlin
+     *   fun <A> Events<Events<A>>.flatten() = holdState(emptyEvents).switchEvents()
+     * ```
+     *
+     * @see switchEvents
+     */
+    fun <A> Events<Events<A>>.flatten() = holdState(emptyEvents).switchEvents()
+
+    /**
+     * Returns an [Events] containing the results of applying [transform] to each value of the
+     * original [Events].
+     *
+     * [transform] can perform state accumulation via its [StateScope] receiver. With each
+     * invocation of [transform], state accumulation from previous invocation is stopped.
+     *
+     * ``` kotlin
+     *   fun <A, B> Events<A>.mapLatestStateful(transform: StateScope.(A) -> B): Events<B> =
+     *       map { statefully { transform(it) } }.applyLatestStateful()
+     * ```
+     */
+    fun <A, B> Events<A>.mapLatestStateful(transform: StateScope.(A) -> B): Events<B> =
+        mapCheap { statefully { transform(it) } }.applyLatestStateful()
+
+    /**
+     * Returns an [Events] that switches to a new [Events] produced by [transform] every time the
+     * original [Events] emits a value.
+     *
+     * [transform] can perform state accumulation via its [StateScope] receiver. With each
+     * invocation of [transform], state accumulation from previous invocation is stopped.
+     *
+     * ``` kotlin
+     *   fun <A, B> Events<A>.flatMapLatestStateful(
+     *       transform: StateScope.(A) -> Events<B>
+     *   ): Events<B> =
+     *       mapLatestStateful(transform).flatten()
+     * ```
+     */
+    fun <A, B> Events<A>.flatMapLatestStateful(transform: StateScope.(A) -> Events<B>): Events<B> =
+        mapLatestStateful(transform).flatten()
+
+    /**
+     * Returns an [Events] containing the results of applying each [Stateful] emitted from the
+     * original [Events].
+     *
+     * When each [Stateful] is applied, state accumulation from the previously-active [Stateful] is
+     * stopped.
+     *
+     * @sample com.android.systemui.kairos.KairosSamples.applyLatestStateful
+     */
+    fun <A> Events<Stateful<A>>.applyLatestStateful(): Events<A> = applyLatestStateful {}.first
+
+    /**
+     * Returns a [State] containing the value returned by applying the [Stateful] held by the
+     * original [State].
+     *
+     * When each [Stateful] is applied, state accumulation from the previously-active [Stateful] is
+     * stopped.
+     */
+    fun <A> State<Stateful<A>>.applyLatestStateful(): State<A> {
+        val (changes, init) = changes.applyLatestStateful { sample()() }
+        return changes.holdStateDeferred(init)
+    }
+
+    /**
+     * Returns an [Events] containing the results of applying each [Stateful] emitted from the
+     * original [Events], and a [DeferredValue] containing the result of applying [init]
+     * immediately.
+     *
+     * When each [Stateful] is applied, state accumulation from the previously-active [Stateful] is
+     * stopped.
+     */
+    fun <A, B> Events<Stateful<B>>.applyLatestStateful(
+        init: Stateful<A>
+    ): Pair<Events<B>, DeferredValue<A>> {
+        val (events, result) =
+            mapCheap { spec -> mapOf(Unit to Maybe.present(spec)) }
+                .applyLatestStatefulForKey(init = mapOf(Unit to init), numKeys = 1)
+        val outEvents: Events<B> =
+            events.mapMaybe {
+                checkNotNull(it[Unit]) { "applyLatest: expected result, but none present in: $it" }
+            }
+        val outInit: DeferredValue<A> = deferredTransactionScope {
+            val initResult: Map<Unit, A> = result.value
+            check(Unit in initResult) {
+                "applyLatest: expected initial result, but none present in: $initResult"
+            }
+            initResult.getValue(Unit)
+        }
+        return Pair(outEvents, outInit)
+    }
+
+    /**
+     * Returns an [Events] containing the results of applying each [Stateful] emitted from the
+     * original [Events], and a [DeferredValue] containing the result of applying [init]
+     * immediately.
+     *
+     * When each [Stateful] is applied, state accumulation from the previously-active [Stateful]
+     * with the same key is stopped.
+     *
+     * If the [Maybe] contained within the value for an associated key is [absent][Maybe.absent],
+     * then the previously-active [Stateful] will be stopped with no replacement.
+     *
+     * The optional [numKeys] argument is an optimization used to initialize the internal storage.
+     */
+    fun <K, A, B> Events<MapPatch<K, Stateful<A>>>.applyLatestStatefulForKey(
+        init: Map<K, Stateful<B>>,
+        numKeys: Int? = null,
+    ): Pair<Events<MapPatch<K, A>>, DeferredValue<Map<K, B>>> =
+        applyLatestStatefulForKey(deferredOf(init), numKeys)
+
+    /**
+     * Returns an [Incremental] containing the latest results of applying each [Stateful] emitted
+     * from the original [Incremental]'s [updates].
+     *
+     * When each [Stateful] is applied, state accumulation from the previously-active [Stateful]
+     * with the same key is stopped.
+     *
+     * If the [Maybe] contained within the value for an associated key is [absent][Maybe.absent],
+     * then the previously-active [Stateful] will be stopped with no replacement.
+     *
+     * The optional [numKeys] argument is an optimization used to initialize the internal storage.
+     */
+    fun <K, V> Incremental<K, Stateful<V>>.applyLatestStatefulForKey(
+        numKeys: Int? = null
+    ): Incremental<K, V> {
+        val (events, init) = updates.applyLatestStatefulForKey(sampleDeferred())
+        return events.foldStateMapIncrementally(init)
+    }
+
+    /**
+     * Returns a [State] containing the latest results of applying each [Stateful] emitted from the
+     * original [Events].
+     *
+     * When each [Stateful] is applied, state accumulation from the previously-active [Stateful]
+     * with the same key is stopped.
+     *
+     * If the [Maybe] contained within the value for an associated key is [absent][Maybe.absent],
+     * then the previously-active [Stateful] will be stopped with no replacement.
+     *
+     * The optional [numKeys] argument is an optimization used to initialize the internal storage.
+     */
+    fun <K, A> Events<MapPatch<K, Stateful<A>>>.holdLatestStatefulForKey(
+        init: DeferredValue<Map<K, Stateful<A>>>,
+        numKeys: Int? = null,
+    ): Incremental<K, A> {
+        val (changes, initialValues) = applyLatestStatefulForKey(init, numKeys)
+        return changes.foldStateMapIncrementally(initialValues)
+    }
+
+    /**
+     * Returns a [State] containing the latest results of applying each [Stateful] emitted from the
+     * original [Events].
+     *
+     * When each [Stateful] is applied, state accumulation from the previously-active [Stateful]
+     * with the same key is stopped.
+     *
+     * If the [Maybe] contained within the value for an associated key is [absent][Maybe.absent],
+     * then the previously-active [Stateful] will be stopped with no replacement.
+     *
+     * The optional [numKeys] argument is an optimization used to initialize the internal storage.
+     */
+    fun <K, A> Events<MapPatch<K, Stateful<A>>>.holdLatestStatefulForKey(
+        init: Map<K, Stateful<A>> = emptyMap(),
+        numKeys: Int? = null,
+    ): Incremental<K, A> = holdLatestStatefulForKey(deferredOf(init), numKeys)
+
+    /**
+     * Returns an [Events] containing the results of applying each [Stateful] emitted from the
+     * original [Events].
+     *
+     * When each [Stateful] is applied, state accumulation from the previously-active [Stateful]
+     * with the same key is stopped.
+     *
+     * If the [Maybe] contained within the value for an associated key is [absent][Maybe.absent],
+     * then the previously-active [Stateful] will be stopped with no replacement.
+     *
+     * The optional [numKeys] argument is an optimization used to initialize the internal storage.
+     *
+     * @sample com.android.systemui.kairos.KairosSamples.applyLatestStatefulForKey
+     */
+    fun <K, A> Events<MapPatch<K, Stateful<A>>>.applyLatestStatefulForKey(
+        numKeys: Int? = null
+    ): Events<MapPatch<K, A>> =
+        applyLatestStatefulForKey(init = emptyMap<K, Stateful<*>>(), numKeys = numKeys).first
+
+    /**
+     * Returns an [Events] containing the results of applying [transform] to each value of the
+     * original [Events], and a [DeferredValue] containing the result of applying [transform] to
+     * [initialValues] immediately.
+     *
+     * [transform] can perform state accumulation via its [StateScope] receiver. With each
+     * invocation of [transform], state accumulation from previous invocation is stopped.
+     *
+     * If the [Maybe] contained within the value for an associated key is [absent][Maybe.absent],
+     * then the previously-active [StateScope] will be stopped with no replacement.
+     *
+     * The optional [numKeys] argument is an optimization used to initialize the internal storage.
+     */
+    fun <K, A, B> Events<MapPatch<K, A>>.mapLatestStatefulForKey(
+        initialValues: DeferredValue<Map<K, A>>,
+        numKeys: Int? = null,
+        transform: StateScope.(A) -> B,
+    ): Pair<Events<MapPatch<K, B>>, DeferredValue<Map<K, B>>> =
+        map { patch -> patch.mapValues { (_, v) -> v.map { statefully { transform(it) } } } }
+            .applyLatestStatefulForKey(
+                deferredStateScope {
+                    initialValues.value.mapValues { (_, v) -> statefully { transform(v) } }
+                },
+                numKeys = numKeys,
+            )
+
+    /**
+     * Returns an [Events] containing the results of applying [transform] to each value of the
+     * original [Events], and a [DeferredValue] containing the result of applying [transform] to
+     * [initialValues] immediately.
+     *
+     * [transform] can perform state accumulation via its [StateScope] receiver. With each
+     * invocation of [transform], state accumulation from previous invocation is stopped.
+     *
+     * If the [Maybe] contained within the value for an associated key is [absent][Maybe.absent],
+     * then the previously-active [StateScope] will be stopped with no replacement.
+     *
+     * The optional [numKeys] argument is an optimization used to initialize the internal storage.
+     */
+    fun <K, A, B> Events<MapPatch<K, A>>.mapLatestStatefulForKey(
+        initialValues: Map<K, A>,
+        numKeys: Int? = null,
+        transform: StateScope.(A) -> B,
+    ): Pair<Events<MapPatch<K, B>>, DeferredValue<Map<K, B>>> =
+        mapLatestStatefulForKey(deferredOf(initialValues), numKeys, transform)
+
+    /**
+     * Returns an [Events] containing the results of applying [transform] to each value of the
+     * original [Events].
+     *
+     * [transform] can perform state accumulation via its [StateScope] receiver. With each
+     * invocation of [transform], state accumulation from previous invocation is stopped.
+     *
+     * If the [Maybe] contained within the value for an associated key is [absent][Maybe.absent],
+     * then the previously-active [StateScope] will be stopped with no replacement.
+     *
+     * The optional [numKeys] argument is an optimization used to initialize the internal storage.
+     *
+     * ``` kotlin
+     *   fun <K, A, B> Events<MapPatch<K, A>>.mapLatestStatefulForKey(
+     *       numKeys: Int? = null,
+     *       transform: StateScope.(A) -> B,
+     *   ): Pair<Events<MapPatch<K, B>>, DeferredValue<Map<K, B>>> =
+     *       map { patch -> patch.mapValues { (_, mv) -> mv.map { statefully { transform(it) } } } }
+     *           .applyLatestStatefulForKey(numKeys)
+     * ```
+     */
+    fun <K, A, B> Events<MapPatch<K, A>>.mapLatestStatefulForKey(
+        numKeys: Int? = null,
+        transform: StateScope.(A) -> B,
+    ): Events<MapPatch<K, B>> = mapLatestStatefulForKey(emptyMap(), numKeys, transform).first
+
+    /**
+     * Returns an [Events] that will only emit the next event of the original [Events], and then
+     * will act as [emptyEvents].
+     *
+     * If the original [Events] is emitting an event at this exact time, then it will be the only
+     * even emitted from the result [Events].
+     *
+     * ``` kotlin
+     *   fun <A> Events<A>.nextOnly(): Events<A> =
+     *       EventsLoop<A>().apply {
+     *           loopback = map { emptyEvents }.holdState(this@nextOnly).switchEvents()
+     *       }
+     * ```
+     */
+    fun <A> Events<A>.nextOnly(): Events<A> =
+        if (this === emptyEvents) {
+            this
+        } else {
+            EventsLoop<A>().apply {
+                loopback = mapCheap { emptyEvents }.holdState(this@nextOnly).switchEvents()
+            }
+        }
+
+    /**
+     * Returns an [Events] that skips the next emission of the original [Events].
+     *
+     * ``` kotlin
+     *   fun <A> Events<A>.skipNext(): Events<A> =
+     *       nextOnly().map { this@skipNext }.holdState(emptyEvents).switchEvents()
+     * ```
+     */
+    fun <A> Events<A>.skipNext(): Events<A> =
+        if (this === emptyEvents) {
+            this
+        } else {
+            nextOnly().mapCheap { this@skipNext }.holdState(emptyEvents).switchEvents()
+        }
+
+    /**
+     * Returns an [Events] that emits values from the original [Events] up until [stop] emits a
+     * value.
+     *
+     * If the original [Events] emits at the same time as [stop], then the returned [Events] will
+     * emit that value.
+     *
+     * ``` kotlin
+     *   fun <A> Events<A>.takeUntil(stop: Events<*>): Events<A> =
+     *       stop.map { emptyEvents }.nextOnly().holdState(this).switchEvents()
+     * ```
+     */
+    fun <A> Events<A>.takeUntil(stop: Events<*>): Events<A> =
+        if (stop === emptyEvents) {
+            this
+        } else {
+            stop.mapCheap { emptyEvents }.nextOnly().holdState(this).switchEvents()
+        }
+
+    /**
+     * Invokes [stateful] in a new [StateScope] that is a child of this one.
+     *
+     * This new scope is stopped when [stop] first emits a value, or when the parent scope is
+     * stopped. Stopping will end all state accumulation; any [States][State] returned from this
+     * scope will no longer update.
+     */
+    fun <A> childStateScope(stop: Events<*>, stateful: Stateful<A>): DeferredValue<A> {
+        val (_, init: DeferredValue<Map<Unit, A>>) =
+            stop
+                .nextOnly()
+                .map { mapOf(Unit to Maybe.absent<Stateful<A>>()) }
+                .applyLatestStatefulForKey(init = mapOf(Unit to stateful), numKeys = 1)
+        return deferredStateScope { init.value.getValue(Unit) }
+    }
+
+    /**
+     * Returns an [Events] that emits values from the original [Events] up to and including a value
+     * is emitted that satisfies [predicate].
+     *
+     * ``` kotlin
+     *   fun <A> Events<A>.takeUntil(predicate: TransactionScope.(A) -> Boolean): Events<A> =
+     *       takeUntil(filter(predicate))
+     * ```
+     */
+    fun <A> Events<A>.takeUntil(predicate: TransactionScope.(A) -> Boolean): Events<A> =
+        takeUntil(filter(predicate))
+
+    /**
+     * Returns a [State] that is incrementally updated when this [Events] emits a value, by applying
+     * [transform] to both the emitted value and the currently tracked state.
+     *
+     * Note that the value contained within the [State] is not updated until *after* all [Events]
+     * have been processed; this keeps the value of the [State] consistent during the entire Kairos
+     * transaction.
+     *
+     * ``` kotlin
+     *   fun <A, B> Events<A>.foldState(
+     *       initialValue: B,
+     *       transform: TransactionScope.(A, B) -> B,
+     *   ): State<B> {
+     *       lateinit var state: State<B>
+     *       return map { a -> transform(a, state.sample()) }
+     *           .holdState(initialValue)
+     *           .also { state = it }
+     *   }
+     * ```
+     */
+    fun <A, B> Events<A>.foldState(
+        initialValue: B,
+        transform: TransactionScope.(A, B) -> B,
+    ): State<B> {
+        lateinit var state: State<B>
+        return map { a -> transform(a, state.sample()) }.holdState(initialValue).also { state = it }
+    }
+
+    /**
+     * Returns a [State] that is incrementally updated when this [Events] emits a value, by applying
+     * [transform] to both the emitted value and the currently tracked state.
+     *
+     * Note that the value contained within the [State] is not updated until *after* all [Events]
+     * have been processed; this keeps the value of the [State] consistent during the entire Kairos
+     * transaction.
+     *
+     * ``` kotlin
+     *   fun <A, B> Events<A>.foldStateDeferred(
+     *       initialValue: DeferredValue<B>,
+     *       transform: TransactionScope.(A, B) -> B,
+     *   ): State<B> {
+     *       lateinit var state: State<B>
+     *       return map { a -> transform(a, state.sample()) }
+     *           .holdStateDeferred(initialValue)
+     *           .also { state = it }
+     *   }
+     * ```
+     */
+    fun <A, B> Events<A>.foldStateDeferred(
+        initialValue: DeferredValue<B>,
+        transform: TransactionScope.(A, B) -> B,
+    ): State<B> {
+        lateinit var state: State<B>
+        return map { a -> transform(a, state.sample()) }
+            .holdStateDeferred(initialValue)
+            .also { state = it }
+    }
+
+    /**
+     * Returns a [State] that holds onto the result of applying the most recently emitted [Stateful]
+     * this [Events], or [init] if nothing has been emitted since it was constructed.
+     *
+     * When each [Stateful] is applied, state accumulation from the previously-active [Stateful] is
+     * stopped.
+     *
+     * Note that the value contained within the [State] is not updated until *after* all [Events]
+     * have been processed; this keeps the value of the [State] consistent during the entire Kairos
+     * transaction.
+     *
+     * ``` kotlin
+     *   fun <A> Events<Stateful<A>>.holdLatestStateful(init: Stateful<A>): State<A> {
+     *       val (changes, initApplied) = applyLatestStateful(init)
+     *       return changes.holdStateDeferred(initApplied)
+     *   }
+     * ```
+     */
+    fun <A> Events<Stateful<A>>.holdLatestStateful(init: Stateful<A>): State<A> {
+        val (changes, initApplied) = applyLatestStateful(init)
+        return changes.holdStateDeferred(initApplied)
+    }
+
+    /**
+     * Returns an [Events] that emits the two most recent emissions from the original [Events].
+     * [initialValue] is used as the previous value for the first emission.
+     *
+     * Shorthand for `sample(hold(init)) { new, old -> Pair(old, new) }`
+     */
+    fun <S, T : S> Events<T>.pairwise(initialValue: S): Events<WithPrev<S, T>> {
+        val previous = holdState(initialValue)
+        return mapCheap { new -> WithPrev(previousValue = previous.sample(), newValue = new) }
+    }
+
+    /**
+     * Returns an [Events] that emits the two most recent emissions from the original [Events]. Note
+     * that the returned [Events] will not emit until the original [Events] has emitted twice.
+     */
+    fun <A> Events<A>.pairwise(): Events<WithPrev<A, A>> =
+        mapCheap { Maybe.present(it) }
+            .pairwise(Maybe.absent)
+            .mapMaybe { (prev, next) -> prev.zipWith(next, ::WithPrev) }
+
+    /**
+     * Returns a [State] that holds both the current and previous values of the original [State].
+     * [initialPreviousValue] is used as the first previous value.
+     *
+     * Shorthand for `sample(hold(init)) { new, old -> Pair(old, new) }`
+     */
+    fun <S, T : S> State<T>.pairwise(initialPreviousValue: S): State<WithPrev<S, T>> =
+        changes
+            .pairwise(initialPreviousValue)
+            .holdStateDeferred(
+                deferredTransactionScope { WithPrev(initialPreviousValue, sample()) }
+            )
+
+    /**
+     * Returns a [State] holding a [Map] that is updated incrementally whenever this emits a value.
+     *
+     * The value emitted is used as a "patch" for the tracked [Map]; for each key [K] in the emitted
+     * map, an associated value of [Maybe.present] will insert or replace the value in the tracked
+     * [Map], and an associated value of [absent][Maybe.absent] will remove the key from the tracked
+     * [Map].
+     */
+    fun <K, V> Events<MapPatch<K, V>>.foldStateMapIncrementally(
+        initialValues: Map<K, V> = emptyMap()
+    ): Incremental<K, V> = foldStateMapIncrementally(deferredOf(initialValues))
+
+    /**
+     * Returns an [Events] that wraps each emission of the original [Events] into an [IndexedValue],
+     * containing the emitted value and its index (starting from zero).
+     *
+     * ``` kotlin
+     *   fun <A> Events<A>.withIndex(): Events<IndexedValue<A>> {
+     *     val index = fold(0) { _, oldIdx -> oldIdx + 1 }
+     *     return sample(index) { a, idx -> IndexedValue(idx, a) }
+     *   }
+     * ```
+     */
+    fun <A> Events<A>.withIndex(): Events<IndexedValue<A>> {
+        val index = foldState(0) { _, old -> old + 1 }
+        return sample(index) { a, idx -> IndexedValue(idx, a) }
+    }
+
+    /**
+     * Returns an [Events] containing the results of applying [transform] to each value of the
+     * original [Events] and its index (starting from zero).
+     *
+     * ``` kotlin
+     *   fun <A> Events<A>.mapIndexed(transform: TransactionScope.(Int, A) -> B): Events<B> {
+     *       val index = foldState(0) { _, i -> i + 1 }
+     *       return sample(index) { a, idx -> transform(idx, a) }
+     *   }
+     * ```
+     */
+    fun <A, B> Events<A>.mapIndexed(transform: TransactionScope.(Int, A) -> B): Events<B> {
+        val index = foldState(0) { _, i -> i + 1 }
+        return sample(index) { a, idx -> transform(idx, a) }
+    }
+
+    /**
+     * Returns an [Events] where all subsequent repetitions of the same value are filtered out.
+     *
+     * ``` kotlin
+     *   fun <A> Events<A>.distinctUntilChanged(): Events<A> {
+     *       val state: State<Any?> = holdState(Any())
+     *       return filter { it != state.sample() }
+     *   }
+     * ```
+     */
+    fun <A> Events<A>.distinctUntilChanged(): Events<A> {
+        val state: State<Any?> = holdState(Any())
+        return filter { it != state.sample() }
+    }
+
+    /**
+     * Returns a new [Events] that emits at the same rate as the original [Events], but combines the
+     * emitted value with the most recent emission from [other] using [transform].
+     *
+     * Note that the returned [Events] will not emit anything until [other] has emitted at least one
+     * value.
+     *
+     * ``` kotlin
+     *   fun <A, B, C> Events<A>.sample(
+     *       other: Events<B>,
+     *       transform: TransactionScope.(A, B) -> C,
+     *   ): Events<C> {
+     *       val state = other.mapCheap { Maybe.present(it) }.holdState(Maybe.absent)
+     *       return sample(state) { a, b -> b.map { transform(a, it) } }.filterPresent()
+     *   }
+     * ```
+     */
+    fun <A, B, C> Events<A>.sample(
+        other: Events<B>,
+        transform: TransactionScope.(A, B) -> C,
+    ): Events<C> {
+        val state = other.mapCheap { Maybe.present(it) }.holdState(Maybe.absent)
+        return sample(state) { a, b -> b.map { transform(a, it) } }.filterPresent()
+    }
+
+    /**
+     * Returns a [State] that samples the [Transactional] held by the given [State] within the same
+     * transaction that the state changes.
+     *
+     * ``` kotlin
+     *   fun <A> State<Transactional<A>>.sampleTransactionals(): State<A> =
+     *       changes
+     *           .sampleTransactionals()
+     *           .holdStateDeferred(deferredTransactionScope { sample().sample() })
+     * ```
+     */
+    fun <A> State<Transactional<A>>.sampleTransactionals(): State<A> =
+        changes
+            .sampleTransactionals()
+            .holdStateDeferred(deferredTransactionScope { sample().sample() })
+
+    /**
+     * Returns a [State] that transforms the value held inside this [State] by applying it to the
+     * given function [transform].
+     *
+     * Note that this is less efficient than [State.map], which should be preferred if [transform]
+     * does not need access to [TransactionScope].
+     *
+     * ``` kotlin
+     *   fun <A, B> State<A>.mapTransactionally(transform: TransactionScope.(A) -> B): State<B> =
+     *       map { transactionally { transform(it) } }.sampleTransactionals()
+     * ```
+     */
+    fun <A, B> State<A>.mapTransactionally(transform: TransactionScope.(A) -> B): State<B> =
+        map { transactionally { transform(it) } }.sampleTransactionals()
+
+    /**
+     * Returns a [State] whose value is generated with [transform] by combining the current values
+     * of each given [State].
+     *
+     * Note that this is less efficient than [combine], which should be preferred if [transform]
+     * does not need access to [TransactionScope].
+     *
+     * ``` kotlin
+     *   fun <A, B, Z> combineTransactionally(
+     *       stateA: State<A>,
+     *       stateB: State<B>,
+     *       transform: TransactionScope.(A, B) -> Z,
+     *   ): State<Z> =
+     *       combine(stateA, stateB) { a, b -> transactionally { transform(a, b) } }
+     *           .sampleTransactionals()
+     * ```
+     *
+     * @see State.combineTransactionally
+     */
+    fun <A, B, Z> combineTransactionally(
+        stateA: State<A>,
+        stateB: State<B>,
+        transform: TransactionScope.(A, B) -> Z,
+    ): State<Z> =
+        combine(stateA, stateB) { a, b -> transactionally { transform(a, b) } }
+            .sampleTransactionals()
+
+    /**
+     * Returns a [State] whose value is generated with [transform] by combining the current values
+     * of each given [State].
+     *
+     * Note that this is less efficient than [combine], which should be preferred if [transform]
+     * does not need access to [TransactionScope].
+     *
+     * @see State.combineTransactionally
+     */
+    fun <A, B, C, Z> combineTransactionally(
+        stateA: State<A>,
+        stateB: State<B>,
+        stateC: State<C>,
+        transform: TransactionScope.(A, B, C) -> Z,
+    ): State<Z> =
+        combine(stateA, stateB, stateC) { a, b, c -> transactionally { transform(a, b, c) } }
+            .sampleTransactionals()
+
+    /**
+     * Returns a [State] whose value is generated with [transform] by combining the current values
+     * of each given [State].
+     *
+     * Note that this is less efficient than [combine], which should be preferred if [transform]
+     * does not need access to [TransactionScope].
+     *
+     * @see State.combineTransactionally
+     */
+    fun <A, B, C, D, Z> combineTransactionally(
+        stateA: State<A>,
+        stateB: State<B>,
+        stateC: State<C>,
+        stateD: State<D>,
+        transform: TransactionScope.(A, B, C, D) -> Z,
+    ): State<Z> =
+        combine(stateA, stateB, stateC, stateD) { a, b, c, d ->
+                transactionally { transform(a, b, c, d) }
+            }
+            .sampleTransactionals()
+
+    /**
+     * Returns a [State] by applying [transform] to the value held by the original [State].
+     *
+     * Note that this is less efficient than [flatMap], which should be preferred if [transform]
+     * does not need access to [TransactionScope].
+     *
+     * ``` kotlin
+     *   fun <A, B> State<A>.flatMapTransactionally(
+     *       transform: TransactionScope.(A) -> State<B>
+     *   ): State<B> = map { transactionally { transform(it) } }.sampleTransactionals().flatten()
+     * ```
+     */
+    fun <A, B> State<A>.flatMapTransactionally(
+        transform: TransactionScope.(A) -> State<B>
+    ): State<B> = map { transactionally { transform(it) } }.sampleTransactionals().flatten()
+
+    /**
+     * Returns a [State] whose value is generated with [transform] by combining the current values
+     * of each given [State].
+     *
+     * Note that this is less efficient than [combine], which should be preferred if [transform]
+     * does not need access to [TransactionScope].
+     *
+     * @see State.combineTransactionally
+     */
+    fun <A, Z> combineTransactionally(
+        vararg states: State<A>,
+        transform: TransactionScope.(List<A>) -> Z,
+    ): State<Z> = combine(*states).mapTransactionally(transform)
+
+    /**
+     * Returns a [State] whose value is generated with [transform] by combining the current values
+     * of each given [State].
+     *
+     * Note that this is less efficient than [combine], which should be preferred if [transform]
+     * does not need access to [TransactionScope].
+     *
+     * @see State.combineTransactionally
+     */
+    fun <A, Z> Iterable<State<A>>.combineTransactionally(
+        transform: TransactionScope.(List<A>) -> Z
+    ): State<Z> = combine().mapTransactionally(transform)
+
+    /**
+     * Returns a [State] by combining the values held inside the given [State]s by applying them to
+     * the given function [transform].
+     *
+     * Note that this is less efficient than [combine], which should be preferred if [transform]
+     * does not need access to [TransactionScope].
+     */
+    @Suppress("INAPPLICABLE_JVM_NAME")
+    @JvmName(name = "combineStateTransactionally")
+    fun <A, B, C> State<A>.combineTransactionally(
+        other: State<B>,
+        transform: TransactionScope.(A, B) -> C,
+    ): State<C> = combineTransactionally(this, other, transform)
+
+    /**
+     * Returns an [Incremental] that reflects the state of the original [Incremental], but also adds
+     * / removes entries based on the state of the original's values.
+     *
+     * ``` kotlin
+     *   fun <K, V> Incremental<K, State<Maybe<V>>>.applyStateIncrementally(): Incremental<K, V> =
+     *       mapValues { (_, v) -> v.changes }
+     *           .mergeEventsIncrementallyPromptly()
+     *           .foldStateMapIncrementally(
+     *               deferredStateScope { sample().mapMaybeValues { (_, s) -> s.sample() } }
+     *           )
+     * ```
+     */
+    fun <K, V> Incremental<K, State<Maybe<V>>>.applyStateIncrementally(): Incremental<K, V> =
+        mapValues { (_, v) -> v.changes }
+            .mergeEventsIncrementallyPromptly()
+            .foldStateMapIncrementally(
+                deferredStateScope { sample().mapMaybeValues { (_, s) -> s.sample() } }
+            )
+
+    /**
+     * Returns an [Incremental] that reflects the state of the original [Incremental], but also adds
+     * / removes entries based on the [State] returned from applying [transform] to the original's
+     * entries.
+     *
+     * ``` kotlin
+     *   fun <K, V, U> Incremental<K, V>.mapIncrementalState(
+     *       transform: KairosScope.(Map.Entry<K, V>) -> State<Maybe<U>>
+     *   ): Incremental<K, U> = mapValues { transform(it) }.applyStateIncrementally()
+     * ```
+     */
+    fun <K, V, U> Incremental<K, V>.mapIncrementalState(
+        transform: KairosScope.(Map.Entry<K, V>) -> State<Maybe<U>>
+    ): Incremental<K, U> = mapValues { transform(it) }.applyStateIncrementally()
+
+    /**
+     * Returns an [Incremental] that reflects the state of the original [Incremental], but also adds
+     * / removes entries based on the [State] returned from applying [transform] to the original's
+     * entries, such that entries are added when that state is `true`, and removed when `false`.
+     *
+     * ``` kotlin
+     *   fun <K, V> Incremental<K, V>.filterIncrementally(
+     *       transform: KairosScope.(Map.Entry<K, V>) -> State<Boolean>
+     *   ): Incremental<K, V> = mapIncrementalState { entry ->
+     *       transform(entry).map { if (it) Maybe.present(entry.value) else Maybe.absent }
+     *   }
+     * ```
+     */
+    fun <K, V> Incremental<K, V>.filterIncrementally(
+        transform: KairosScope.(Map.Entry<K, V>) -> State<Boolean>
+    ): Incremental<K, V> = mapIncrementalState { entry ->
+        transform(entry).map { if (it) Maybe.present(entry.value) else Maybe.absent }
+    }
+
+    /**
+     * Returns an [Incremental] that samples the [Transactionals][Transactional] held by the
+     * original within the same transaction that the incremental [updates].
+     *
+     * ``` kotlin
+     *   fun <K, V> Incremental<K, Transactional<V>>.sampleTransactionals(): Incremental<K, V> =
+     *       updates
+     *           .map { patch -> patch.mapValues { (k, mv) -> mv.map { it.sample() } } }
+     *           .foldStateMapIncrementally(
+     *               deferredStateScope { sample().mapValues { (k, v) -> v.sample() } }
+     *           )
+     * ```
+     */
+    fun <K, V> Incremental<K, Transactional<V>>.sampleTransactionals(): Incremental<K, V> =
+        updates
+            .map { patch -> patch.mapValues { (k, mv) -> mv.map { it.sample() } } }
+            .foldStateMapIncrementally(
+                deferredStateScope { sample().mapValues { (k, v) -> v.sample() } }
+            )
+
+    /**
+     * Returns an [Incremental] that tracks the entries of the original incremental, but values
+     * replaced with those obtained by applying [transform] to each original entry.
+     *
+     * Note that this is less efficient than [mapValues], which should be preferred if [transform]
+     * does not need access to [TransactionScope].
+     *
+     * ``` kotlin
+     *   fun <K, V, U> Incremental<K, V>.mapValuesTransactionally(
+     *       transform: TransactionScope.(Map.Entry<K, V>) -> U
+     *   ): Incremental<K, U> =
+     *       mapValues { transactionally { transform(it) } }.sampleTransactionals()
+     * ```
+     */
+    fun <K, V, U> Incremental<K, V>.mapValuesTransactionally(
+        transform: TransactionScope.(Map.Entry<K, V>) -> U
+    ): Incremental<K, U> = mapValues { transactionally { transform(it) } }.sampleTransactionals()
+}
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Switch.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Switch.kt
new file mode 100644
index 0000000..63e27d0
--- /dev/null
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Switch.kt
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.kairos
+
+import com.android.systemui.kairos.internal.IncrementalImpl
+import com.android.systemui.kairos.internal.constInit
+import com.android.systemui.kairos.internal.init
+import com.android.systemui.kairos.internal.mapImpl
+import com.android.systemui.kairos.internal.switchDeferredImplSingle
+import com.android.systemui.kairos.internal.switchPromptImplSingle
+import com.android.systemui.kairos.util.mapPatchFromFullDiff
+
+/**
+ * Returns an [Events] that switches to the [Events] contained within this [State] whenever it
+ * changes.
+ *
+ * This switch does take effect until the *next* transaction after [State] changes. For a switch
+ * that takes effect immediately, see [switchEventsPromptly].
+ *
+ * @sample com.android.systemui.kairos.KairosSamples.switchEvents
+ */
+@ExperimentalKairosApi
+fun <A> State<Events<A>>.switchEvents(): Events<A> {
+    val patches =
+        mapImpl({ init.connect(this).changes }) { newEvents, _ -> newEvents.init.connect(this) }
+    return EventsInit(
+        constInit(
+            name = null,
+            switchDeferredImplSingle(
+                getStorage = {
+                    init.connect(this).getCurrentWithEpoch(this).first.init.connect(this)
+                },
+                getPatches = { patches },
+            ),
+        )
+    )
+}
+
+/**
+ * Returns an [Events] that switches to the [Events] contained within this [State] whenever it
+ * changes.
+ *
+ * This switch takes effect immediately within the same transaction that [State] changes. If the
+ * newly-switched-in [Events] is emitting a value within this transaction, then that value will be
+ * emitted from this switch. If not, but the previously-switched-in [Events] *is* emitting, then
+ * that value will be emitted from this switch instead. Otherwise, there will be no emission.
+ *
+ * In general, you should prefer [switchEvents] over this method. It is both safer and more
+ * performant.
+ *
+ * @sample com.android.systemui.kairos.KairosSamples.switchEventsPromptly
+ */
+// TODO: parameter to handle coincidental emission from both old and new
+@ExperimentalKairosApi
+fun <A> State<Events<A>>.switchEventsPromptly(): Events<A> {
+    val patches =
+        mapImpl({ init.connect(this).changes }) { newEvents, _ -> newEvents.init.connect(this) }
+    return EventsInit(
+        constInit(
+            name = null,
+            switchPromptImplSingle(
+                getStorage = {
+                    init.connect(this).getCurrentWithEpoch(this).first.init.connect(this)
+                },
+                getPatches = { patches },
+            ),
+        )
+    )
+}
+
+/** Returns an [Incremental] that behaves like current value of this [State]. */
+fun <K, V> State<Incremental<K, V>>.switchIncremental(): Incremental<K, V> {
+    val stateChangePatches =
+        transitions.mapNotNull { (old, new) ->
+            mapPatchFromFullDiff(old.sample(), new.sample()).takeIf { it.isNotEmpty() }
+        }
+    val innerChanges =
+        map { inner ->
+                merge(stateChangePatches, inner.updates) { switchPatch, upcomingPatch ->
+                    switchPatch + upcomingPatch
+                }
+            }
+            .switchEventsPromptly()
+    val flattened = flatten()
+    return IncrementalInit(
+        init("switchIncremental") {
+            val upstream = flattened.init.connect(this)
+            IncrementalImpl(
+                "switchIncremental",
+                "switchIncremental",
+                upstream.changes,
+                innerChanges.init.connect(this),
+                upstream.store,
+            )
+        }
+    )
+}
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/TFlow.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/TFlow.kt
deleted file mode 100644
index a175e2e..0000000
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/TFlow.kt
+++ /dev/null
@@ -1,560 +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.kairos
-
-import com.android.systemui.kairos.internal.DemuxImpl
-import com.android.systemui.kairos.internal.Init
-import com.android.systemui.kairos.internal.InitScope
-import com.android.systemui.kairos.internal.InputNode
-import com.android.systemui.kairos.internal.Network
-import com.android.systemui.kairos.internal.NoScope
-import com.android.systemui.kairos.internal.TFlowImpl
-import com.android.systemui.kairos.internal.activated
-import com.android.systemui.kairos.internal.cached
-import com.android.systemui.kairos.internal.constInit
-import com.android.systemui.kairos.internal.filterNode
-import com.android.systemui.kairos.internal.init
-import com.android.systemui.kairos.internal.map
-import com.android.systemui.kairos.internal.mapImpl
-import com.android.systemui.kairos.internal.mapMaybeNode
-import com.android.systemui.kairos.internal.mergeNodes
-import com.android.systemui.kairos.internal.mergeNodesLeft
-import com.android.systemui.kairos.internal.neverImpl
-import com.android.systemui.kairos.internal.switchDeferredImplSingle
-import com.android.systemui.kairos.internal.switchPromptImpl
-import com.android.systemui.kairos.internal.util.hashString
-import com.android.systemui.kairos.util.Either
-import com.android.systemui.kairos.util.Left
-import com.android.systemui.kairos.util.Maybe
-import com.android.systemui.kairos.util.Right
-import com.android.systemui.kairos.util.just
-import com.android.systemui.kairos.util.map
-import com.android.systemui.kairos.util.toMaybe
-import java.util.concurrent.atomic.AtomicReference
-import kotlin.reflect.KProperty
-import kotlinx.coroutines.CompletableDeferred
-import kotlinx.coroutines.CoroutineStart
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.async
-import kotlinx.coroutines.coroutineScope
-
-/** A series of values of type [A] available at discrete points in time. */
-@ExperimentalFrpApi
-sealed class TFlow<out A> {
-    companion object {
-        /** A [TFlow] with no values. */
-        val empty: TFlow<Nothing> = EmptyFlow
-    }
-}
-
-/** A [TFlow] with no values. */
-@ExperimentalFrpApi val emptyTFlow: TFlow<Nothing> = TFlow.empty
-
-/**
- * A forward-reference to a [TFlow]. Useful for recursive definitions.
- *
- * This reference can be used like a standard [TFlow], but will hold up evaluation of the FRP
- * network until the [loopback] reference is set.
- */
-@ExperimentalFrpApi
-class TFlowLoop<A> : TFlow<A>() {
-    private val deferred = CompletableDeferred<TFlow<A>>()
-
-    internal val init: Init<TFlowImpl<A>> =
-        init(name = null) { deferred.await().init.connect(evalScope = this) }
-
-    /** The [TFlow] this reference is referring to. */
-    @ExperimentalFrpApi
-    var loopback: TFlow<A>? = null
-        set(value) {
-            value?.let {
-                check(deferred.complete(value)) { "TFlowLoop.loopback has already been set." }
-                field = value
-            }
-        }
-
-    operator fun getValue(thisRef: Any?, property: KProperty<*>): TFlow<A> = this
-
-    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: TFlow<A>) {
-        loopback = value
-    }
-
-    override fun toString(): String = "${this::class.simpleName}@$hashString"
-}
-
-/** TODO */
-@ExperimentalFrpApi fun <A> Lazy<TFlow<A>>.defer(): TFlow<A> = deferInline { value }
-
-/** TODO */
-@ExperimentalFrpApi
-fun <A> FrpDeferredValue<TFlow<A>>.defer(): TFlow<A> = deferInline { unwrapped.await() }
-
-/** TODO */
-@ExperimentalFrpApi
-fun <A> deferTFlow(block: suspend FrpScope.() -> TFlow<A>): TFlow<A> = deferInline {
-    NoScope.runInFrpScope(block)
-}
-
-/** Returns a [TFlow] that emits the new value of this [TState] when it changes. */
-@ExperimentalFrpApi
-val <A> TState<A>.stateChanges: TFlow<A>
-    get() = TFlowInit(init(name = null) { init.connect(evalScope = this).changes })
-
-/**
- * Returns a [TFlow] that contains only the [just] results of applying [transform] to each value of
- * the original [TFlow].
- *
- * @see mapNotNull
- */
-@ExperimentalFrpApi
-fun <A, B> TFlow<A>.mapMaybe(transform: suspend FrpTransactionScope.(A) -> Maybe<B>): TFlow<B> {
-    val pulse =
-        mapMaybeNode({ init.connect(evalScope = this) }) { runInTransactionScope { transform(it) } }
-    return TFlowInit(constInit(name = null, pulse))
-}
-
-/**
- * Returns a [TFlow] that contains only the non-null results of applying [transform] to each value
- * of the original [TFlow].
- *
- * @see mapMaybe
- */
-@ExperimentalFrpApi
-fun <A, B> TFlow<A>.mapNotNull(transform: suspend FrpTransactionScope.(A) -> B?): TFlow<B> =
-    mapMaybe {
-        transform(it).toMaybe()
-    }
-
-/** Returns a [TFlow] containing only values of the original [TFlow] that are not null. */
-@ExperimentalFrpApi fun <A> TFlow<A?>.filterNotNull(): TFlow<A> = mapNotNull { it }
-
-/** Shorthand for `mapNotNull { it as? A }`. */
-@ExperimentalFrpApi
-inline fun <reified A> TFlow<*>.filterIsInstance(): TFlow<A> = mapNotNull { it as? A }
-
-/** Shorthand for `mapMaybe { it }`. */
-@ExperimentalFrpApi fun <A> TFlow<Maybe<A>>.filterJust(): TFlow<A> = mapMaybe { it }
-
-/**
- * Returns a [TFlow] containing the results of applying [transform] to each value of the original
- * [TFlow].
- */
-@ExperimentalFrpApi
-fun <A, B> TFlow<A>.map(transform: suspend FrpTransactionScope.(A) -> B): TFlow<B> {
-    val mapped: TFlowImpl<B> =
-        mapImpl({ init.connect(evalScope = this) }) { a -> runInTransactionScope { transform(a) } }
-    return TFlowInit(constInit(name = null, mapped.cached()))
-}
-
-/**
- * Like [map], but the emission is not cached during the transaction. Use only if [transform] is
- * fast and pure.
- *
- * @see map
- */
-@ExperimentalFrpApi
-fun <A, B> TFlow<A>.mapCheap(transform: suspend FrpTransactionScope.(A) -> B): TFlow<B> =
-    TFlowInit(
-        constInit(
-            name = null,
-            mapImpl({ init.connect(evalScope = this) }) { a ->
-                runInTransactionScope { transform(a) }
-            },
-        )
-    )
-
-/**
- * Returns a [TFlow] that invokes [action] before each value of the original [TFlow] is emitted.
- * Useful for logging and debugging.
- *
- * ```
- *   pulse.onEach { foo(it) } == pulse.map { foo(it); it }
- * ```
- *
- * Note that the side effects performed in [onEach] are only performed while the resulting [TFlow]
- * is connected to an output of the FRP network. If your goal is to reliably perform side effects in
- * response to a [TFlow], use the output combinators available in [FrpBuildScope], such as
- * [FrpBuildScope.toSharedFlow] or [FrpBuildScope.observe].
- */
-@ExperimentalFrpApi
-fun <A> TFlow<A>.onEach(action: suspend FrpTransactionScope.(A) -> Unit): TFlow<A> = map {
-    action(it)
-    it
-}
-
-/**
- * Returns a [TFlow] containing only values of the original [TFlow] that satisfy the given
- * [predicate].
- */
-@ExperimentalFrpApi
-fun <A> TFlow<A>.filter(predicate: suspend FrpTransactionScope.(A) -> Boolean): TFlow<A> {
-    val pulse =
-        filterNode({ init.connect(evalScope = this) }) { runInTransactionScope { predicate(it) } }
-    return TFlowInit(constInit(name = null, pulse.cached()))
-}
-
-/**
- * Splits a [TFlow] of pairs into a pair of [TFlows][TFlow], where each returned [TFlow] emits half
- * of the original.
- *
- * Shorthand for:
- * ```kotlin
- * val lefts = map { it.first }
- * val rights = map { it.second }
- * return Pair(lefts, rights)
- * ```
- */
-@ExperimentalFrpApi
-fun <A, B> TFlow<Pair<A, B>>.unzip(): Pair<TFlow<A>, TFlow<B>> {
-    val lefts = map { it.first }
-    val rights = map { it.second }
-    return lefts to rights
-}
-
-/**
- * Merges the given [TFlows][TFlow] into a single [TFlow] that emits events from both.
- *
- * Because [TFlow]s can only emit one value per transaction, the provided [transformCoincidence]
- * function is used to combine coincident emissions to produce the result value to be emitted by the
- * merged [TFlow].
- */
-@ExperimentalFrpApi
-fun <A> TFlow<A>.mergeWith(
-    other: TFlow<A>,
-    transformCoincidence: suspend FrpTransactionScope.(A, A) -> A = { a, _ -> a },
-): TFlow<A> {
-    val node =
-        mergeNodes(
-            getPulse = { init.connect(evalScope = this) },
-            getOther = { other.init.connect(evalScope = this) },
-        ) { a, b ->
-            runInTransactionScope { transformCoincidence(a, b) }
-        }
-    return TFlowInit(constInit(name = null, node))
-}
-
-/**
- * Merges the given [TFlows][TFlow] into a single [TFlow] that emits events from all. All coincident
- * emissions are collected into the emitted [List], preserving the input ordering.
- *
- * @see mergeWith
- * @see mergeLeft
- */
-@ExperimentalFrpApi
-fun <A> merge(vararg flows: TFlow<A>): TFlow<List<A>> = flows.asIterable().merge()
-
-/**
- * Merges the given [TFlows][TFlow] into a single [TFlow] that emits events from all. In the case of
- * coincident emissions, the emission from the left-most [TFlow] is emitted.
- *
- * @see merge
- */
-@ExperimentalFrpApi
-fun <A> mergeLeft(vararg flows: TFlow<A>): TFlow<A> = flows.asIterable().mergeLeft()
-
-/**
- * Merges the given [TFlows][TFlow] into a single [TFlow] that emits events from all.
- *
- * Because [TFlow]s can only emit one value per transaction, the provided [transformCoincidence]
- * function is used to combine coincident emissions to produce the result value to be emitted by the
- * merged [TFlow].
- */
-// TODO: can be optimized to avoid creating the intermediate list
-fun <A> merge(vararg flows: TFlow<A>, transformCoincidence: (A, A) -> A): TFlow<A> =
-    merge(*flows).map { l -> l.reduce(transformCoincidence) }
-
-/**
- * Merges the given [TFlows][TFlow] into a single [TFlow] that emits events from all. All coincident
- * emissions are collected into the emitted [List], preserving the input ordering.
- *
- * @see mergeWith
- * @see mergeLeft
- */
-@ExperimentalFrpApi
-fun <A> Iterable<TFlow<A>>.merge(): TFlow<List<A>> =
-    TFlowInit(constInit(name = null, mergeNodes { map { it.init.connect(evalScope = this) } }))
-
-/**
- * Merges the given [TFlows][TFlow] into a single [TFlow] that emits events from all. In the case of
- * coincident emissions, the emission from the left-most [TFlow] is emitted.
- *
- * @see merge
- */
-@ExperimentalFrpApi
-fun <A> Iterable<TFlow<A>>.mergeLeft(): TFlow<A> =
-    TFlowInit(constInit(name = null, mergeNodesLeft { map { it.init.connect(evalScope = this) } }))
-
-/**
- * Creates a new [TFlow] that emits events from all given [TFlow]s. All simultaneous emissions are
- * collected into the emitted [List], preserving the input ordering.
- *
- * @see mergeWith
- */
-@ExperimentalFrpApi fun <A> Sequence<TFlow<A>>.merge(): TFlow<List<A>> = asIterable().merge()
-
-/**
- * Creates a new [TFlow] that emits events from all given [TFlow]s. All simultaneous emissions are
- * collected into the emitted [Map], and are given the same key of the associated [TFlow] in the
- * input [Map].
- *
- * @see mergeWith
- */
-@ExperimentalFrpApi
-fun <K, A> Map<K, TFlow<A>>.merge(): TFlow<Map<K, A>> =
-    asSequence().map { (k, flowA) -> flowA.map { a -> k to a } }.toList().merge().map { it.toMap() }
-
-/**
- * Returns a [GroupedTFlow] that can be used to efficiently split a single [TFlow] into multiple
- * downstream [TFlow]s.
- *
- * The input [TFlow] emits [Map] instances that specify which downstream [TFlow] the associated
- * value will be emitted from. These downstream [TFlow]s can be obtained via
- * [GroupedTFlow.eventsForKey].
- *
- * An example:
- * ```
- *   val sFoo: TFlow<Map<String, Foo>> = ...
- *   val fooById: GroupedTFlow<String, Foo> = sFoo.groupByKey()
- *   val fooBar: TFlow<Foo> = fooById["bar"]
- * ```
- *
- * This is semantically equivalent to `val fooBar = sFoo.mapNotNull { map -> map["bar"] }` but is
- * significantly more efficient; specifically, using [mapNotNull] in this way incurs a `O(n)`
- * performance hit, where `n` is the number of different [mapNotNull] operations used to filter on a
- * specific key's presence in the emitted [Map]. [groupByKey] internally uses a [HashMap] to lookup
- * the appropriate downstream [TFlow], and so operates in `O(1)`.
- *
- * Note that the result [GroupedTFlow] should be cached and re-used to gain the performance benefit.
- *
- * @see selector
- */
-@ExperimentalFrpApi
-fun <K, A> TFlow<Map<K, A>>.groupByKey(numKeys: Int? = null): GroupedTFlow<K, A> =
-    GroupedTFlow(DemuxImpl({ init.connect(this) }, numKeys))
-
-/**
- * Shorthand for `map { mapOf(extractKey(it) to it) }.groupByKey()`
- *
- * @see groupByKey
- */
-@ExperimentalFrpApi
-fun <K, A> TFlow<A>.groupBy(
-    numKeys: Int? = null,
-    extractKey: suspend FrpTransactionScope.(A) -> K,
-): GroupedTFlow<K, A> = map { mapOf(extractKey(it) to it) }.groupByKey(numKeys)
-
-/**
- * Returns two new [TFlow]s that contain elements from this [TFlow] that satisfy or don't satisfy
- * [predicate].
- *
- * Using this is equivalent to `upstream.filter(predicate) to upstream.filter { !predicate(it) }`
- * but is more efficient; specifically, [partition] will only invoke [predicate] once per element.
- */
-@ExperimentalFrpApi
-fun <A> TFlow<A>.partition(
-    predicate: suspend FrpTransactionScope.(A) -> Boolean
-): Pair<TFlow<A>, TFlow<A>> {
-    val grouped: GroupedTFlow<Boolean, A> = groupBy(numKeys = 2, extractKey = predicate)
-    return Pair(grouped.eventsForKey(true), grouped.eventsForKey(false))
-}
-
-/**
- * Returns two new [TFlow]s that contain elements from this [TFlow]; [Pair.first] will contain
- * [Left] values, and [Pair.second] will contain [Right] values.
- *
- * Using this is equivalent to using [filterIsInstance] in conjunction with [map] twice, once for
- * [Left]s and once for [Right]s, but is slightly more efficient; specifically, the
- * [filterIsInstance] check is only performed once per element.
- */
-@ExperimentalFrpApi
-fun <A, B> TFlow<Either<A, B>>.partitionEither(): Pair<TFlow<A>, TFlow<B>> {
-    val (left, right) = partition { it is Left }
-    return Pair(left.mapCheap { (it as Left).value }, right.mapCheap { (it as Right).value })
-}
-
-/**
- * A mapping from keys of type [K] to [TFlow]s emitting values of type [A].
- *
- * @see groupByKey
- */
-@ExperimentalFrpApi
-class GroupedTFlow<in K, out A> internal constructor(internal val impl: DemuxImpl<K, A>) {
-    /**
-     * Returns a [TFlow] that emits values of type [A] that correspond to the given [key].
-     *
-     * @see groupByKey
-     */
-    @ExperimentalFrpApi
-    fun eventsForKey(key: K): TFlow<A> = TFlowInit(constInit(name = null, impl.eventsForKey(key)))
-
-    /**
-     * Returns a [TFlow] that emits values of type [A] that correspond to the given [key].
-     *
-     * @see groupByKey
-     */
-    @ExperimentalFrpApi operator fun get(key: K): TFlow<A> = eventsForKey(key)
-}
-
-/**
- * Returns a [TFlow] that switches to the [TFlow] contained within this [TState] whenever it
- * changes.
- *
- * This switch does take effect until the *next* transaction after [TState] changes. For a switch
- * that takes effect immediately, see [switchPromptly].
- */
-@ExperimentalFrpApi
-fun <A> TState<TFlow<A>>.switch(): TFlow<A> {
-    return TFlowInit(
-        constInit(
-            name = null,
-            switchDeferredImplSingle(
-                getStorage = {
-                    init.connect(this).getCurrentWithEpoch(this).first.init.connect(this)
-                },
-                getPatches = {
-                    mapImpl({ init.connect(this).changes }) { newFlow ->
-                        newFlow.init.connect(this)
-                    }
-                },
-            ),
-        )
-    )
-}
-
-/**
- * Returns a [TFlow] that switches to the [TFlow] contained within this [TState] whenever it
- * changes.
- *
- * This switch takes effect immediately within the same transaction that [TState] changes. In
- * general, you should prefer [switch] over this method. It is both safer and more performant.
- */
-// TODO: parameter to handle coincidental emission from both old and new
-@ExperimentalFrpApi
-fun <A> TState<TFlow<A>>.switchPromptly(): TFlow<A> {
-    val switchNode =
-        switchPromptImpl(
-            getStorage = {
-                mapOf(Unit to init.connect(this).getCurrentWithEpoch(this).first.init.connect(this))
-            },
-            getPatches = {
-                val patches = init.connect(this).changes
-                mapImpl({ patches }) { newFlow -> mapOf(Unit to just(newFlow.init.connect(this))) }
-            },
-        )
-    return TFlowInit(constInit(name = null, mapImpl({ switchNode }) { it.getValue(Unit) }))
-}
-
-/**
- * A mutable [TFlow] that provides the ability to [emit] values to the flow, handling backpressure
- * by coalescing all emissions into batches.
- *
- * @see FrpNetwork.coalescingMutableTFlow
- */
-@ExperimentalFrpApi
-class CoalescingMutableTFlow<In, Out>
-internal constructor(
-    internal val name: String?,
-    internal val coalesce: (old: Out, new: In) -> Out,
-    internal val network: Network,
-    private val getInitialValue: () -> Out,
-    internal val impl: InputNode<Out> = InputNode(),
-) : TFlow<Out>() {
-    internal val storage = AtomicReference(false to getInitialValue())
-
-    override fun toString(): String = "${this::class.simpleName}@$hashString"
-
-    /**
-     * Inserts [value] into the current batch, enqueueing it for emission from this [TFlow] if not
-     * already pending.
-     *
-     * Backpressure occurs when [emit] is called while the FRP network is currently in a
-     * transaction; if called multiple times, then emissions will be coalesced into a single batch
-     * that is then processed when the network is ready.
-     */
-    @ExperimentalFrpApi
-    fun emit(value: In) {
-        val (scheduled, _) = storage.getAndUpdate { (_, old) -> true to coalesce(old, value) }
-        if (!scheduled) {
-            @Suppress("DeferredResultUnused")
-            network.transaction("CoalescingMutableTFlow${name?.let { "($name)" }.orEmpty()}.emit") {
-                impl.visit(this, storage.getAndSet(false to getInitialValue()).second)
-            }
-        }
-    }
-}
-
-/**
- * A mutable [TFlow] that provides the ability to [emit] values to the flow, handling backpressure
- * by suspending the emitter.
- *
- * @see FrpNetwork.coalescingMutableTFlow
- */
-@ExperimentalFrpApi
-class MutableTFlow<T>
-internal constructor(internal val network: Network, internal val impl: InputNode<T> = InputNode()) :
-    TFlow<T>() {
-    internal val name: String? = null
-
-    private val storage = AtomicReference<Job?>(null)
-
-    override fun toString(): String = "${this::class.simpleName}@$hashString"
-
-    /**
-     * Emits a [value] to this [TFlow], suspending the caller until the FRP transaction containing
-     * the emission has completed.
-     */
-    @ExperimentalFrpApi
-    suspend fun emit(value: T) {
-        coroutineScope {
-            var jobOrNull: Job? = null
-            val newEmit =
-                async(start = CoroutineStart.LAZY) {
-                    jobOrNull?.join()
-                    network
-                        .transaction("MutableTFlow($name).emit") { impl.visit(this, value) }
-                        .await()
-                }
-            jobOrNull = storage.getAndSet(newEmit)
-            newEmit.await()
-        }
-    }
-
-    //    internal suspend fun emitInCurrentTransaction(value: T, evalScope: EvalScope) {
-    //        if (storage.getAndSet(just(value)) is None) {
-    //            impl.visit(evalScope)
-    //        }
-    //    }
-}
-
-private data object EmptyFlow : TFlow<Nothing>()
-
-internal class TFlowInit<out A>(val init: Init<TFlowImpl<A>>) : TFlow<A>() {
-    override fun toString(): String = "${this::class.simpleName}@$hashString"
-}
-
-internal val <A> TFlow<A>.init: Init<TFlowImpl<A>>
-    get() =
-        when (this) {
-            is EmptyFlow -> constInit("EmptyFlow", neverImpl)
-            is TFlowInit -> init
-            is TFlowLoop -> init
-            is CoalescingMutableTFlow<*, A> -> constInit(name, impl.activated())
-            is MutableTFlow -> constInit(name, impl.activated())
-        }
-
-private inline fun <A> deferInline(crossinline block: suspend InitScope.() -> TFlow<A>): TFlow<A> =
-    TFlowInit(init(name = null) { block().init.connect(evalScope = this) })
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/TState.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/TState.kt
deleted file mode 100644
index 80e7474..0000000
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/TState.kt
+++ /dev/null
@@ -1,544 +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.kairos
-
-import com.android.systemui.kairos.internal.DerivedMapCheap
-import com.android.systemui.kairos.internal.Init
-import com.android.systemui.kairos.internal.InitScope
-import com.android.systemui.kairos.internal.Network
-import com.android.systemui.kairos.internal.NoScope
-import com.android.systemui.kairos.internal.Schedulable
-import com.android.systemui.kairos.internal.TFlowImpl
-import com.android.systemui.kairos.internal.TStateImpl
-import com.android.systemui.kairos.internal.TStateSource
-import com.android.systemui.kairos.internal.activated
-import com.android.systemui.kairos.internal.cached
-import com.android.systemui.kairos.internal.constInit
-import com.android.systemui.kairos.internal.constS
-import com.android.systemui.kairos.internal.filterNode
-import com.android.systemui.kairos.internal.flatMap
-import com.android.systemui.kairos.internal.init
-import com.android.systemui.kairos.internal.map
-import com.android.systemui.kairos.internal.mapCheap
-import com.android.systemui.kairos.internal.mapImpl
-import com.android.systemui.kairos.internal.util.hashString
-import com.android.systemui.kairos.internal.zipStates
-import kotlin.reflect.KProperty
-import kotlinx.coroutines.CompletableDeferred
-import kotlinx.coroutines.Deferred
-import kotlinx.coroutines.async
-import kotlinx.coroutines.coroutineScope
-
-/**
- * A time-varying value with discrete changes. Essentially, a combination of a [Transactional] that
- * holds a value, and a [TFlow] that emits when the value changes.
- */
-@ExperimentalFrpApi sealed class TState<out A>
-
-/** A [TState] that never changes. */
-@ExperimentalFrpApi
-fun <A> tStateOf(value: A): TState<A> {
-    val operatorName = "tStateOf"
-    val name = "$operatorName($value)"
-    return TStateInit(constInit(name, constS(name, operatorName, value)))
-}
-
-/** TODO */
-@ExperimentalFrpApi fun <A> Lazy<TState<A>>.defer(): TState<A> = deferInline { value }
-
-/** TODO */
-@ExperimentalFrpApi
-fun <A> FrpDeferredValue<TState<A>>.defer(): TState<A> = deferInline { unwrapped.await() }
-
-/** TODO */
-@ExperimentalFrpApi
-fun <A> deferTState(block: suspend FrpScope.() -> TState<A>): TState<A> = deferInline {
-    NoScope.runInFrpScope(block)
-}
-
-/**
- * Returns a [TState] containing the results of applying [transform] to the value held by the
- * original [TState].
- */
-@ExperimentalFrpApi
-fun <A, B> TState<A>.map(transform: suspend FrpScope.(A) -> B): TState<B> {
-    val operatorName = "map"
-    val name = operatorName
-    return TStateInit(
-        init(name) {
-            init.connect(evalScope = this).map(name, operatorName) {
-                NoScope.runInFrpScope { transform(it) }
-            }
-        }
-    )
-}
-
-/**
- * Returns a [TState] that transforms the value held inside this [TState] by applying it to the
- * [transform].
- *
- * Note that unlike [map], the result is not cached. This means that not only should [transform] be
- * fast and pure, it should be *monomorphic* (1-to-1). Failure to do this means that [stateChanges]
- * for the returned [TState] will operate unexpectedly, emitting at rates that do not reflect an
- * observable change to the returned [TState].
- */
-@ExperimentalFrpApi
-fun <A, B> TState<A>.mapCheapUnsafe(transform: suspend FrpScope.(A) -> B): TState<B> {
-    val operatorName = "map"
-    val name = operatorName
-    return TStateInit(
-        init(name) {
-            init.connect(evalScope = this).mapCheap(name, operatorName) {
-                NoScope.runInFrpScope { transform(it) }
-            }
-        }
-    )
-}
-
-/**
- * Returns a [TState] by combining the values held inside the given [TState]s by applying them to
- * the given function [transform].
- */
-@ExperimentalFrpApi
-fun <A, B, C> TState<A>.combineWith(
-    other: TState<B>,
-    transform: suspend FrpScope.(A, B) -> C,
-): TState<C> = combine(this, other, transform)
-
-/**
- * Splits a [TState] of pairs into a pair of [TFlows][TState], where each returned [TState] holds
- * half of the original.
- *
- * Shorthand for:
- * ```kotlin
- * val lefts = map { it.first }
- * val rights = map { it.second }
- * return Pair(lefts, rights)
- * ```
- */
-@ExperimentalFrpApi
-fun <A, B> TState<Pair<A, B>>.unzip(): Pair<TState<A>, TState<B>> {
-    val left = map { it.first }
-    val right = map { it.second }
-    return left to right
-}
-
-/**
- * Returns a [TState] by combining the values held inside the given [TStates][TState] into a [List].
- *
- * @see TState.combineWith
- */
-@ExperimentalFrpApi
-fun <A> Iterable<TState<A>>.combine(): TState<List<A>> {
-    val operatorName = "combine"
-    val name = operatorName
-    return TStateInit(
-        init(name) {
-            zipStates(name, operatorName, states = map { it.init.connect(evalScope = this) })
-        }
-    )
-}
-
-/**
- * Returns a [TState] by combining the values held inside the given [TStates][TState] into a [Map].
- *
- * @see TState.combineWith
- */
-@ExperimentalFrpApi
-fun <K : Any, A> Map<K, TState<A>>.combine(): TState<Map<K, A>> {
-    val operatorName = "combine"
-    val name = operatorName
-    return TStateInit(
-        init(name) {
-            zipStates(
-                name,
-                operatorName,
-                states = mapValues { it.value.init.connect(evalScope = this) },
-            )
-        }
-    )
-}
-
-/**
- * Returns a [TState] whose value is generated with [transform] by combining the current values of
- * each given [TState].
- *
- * @see TState.combineWith
- */
-@ExperimentalFrpApi
-fun <A, B> Iterable<TState<A>>.combine(transform: suspend FrpScope.(List<A>) -> B): TState<B> =
-    combine().map(transform)
-
-/**
- * Returns a [TState] by combining the values held inside the given [TState]s into a [List].
- *
- * @see TState.combineWith
- */
-@ExperimentalFrpApi
-fun <A> combine(vararg states: TState<A>): TState<List<A>> = states.asIterable().combine()
-
-/**
- * Returns a [TState] whose value is generated with [transform] by combining the current values of
- * each given [TState].
- *
- * @see TState.combineWith
- */
-@ExperimentalFrpApi
-fun <A, B> combine(
-    vararg states: TState<A>,
-    transform: suspend FrpScope.(List<A>) -> B,
-): TState<B> = states.asIterable().combine(transform)
-
-/**
- * Returns a [TState] whose value is generated with [transform] by combining the current values of
- * each given [TState].
- *
- * @see TState.combineWith
- */
-@ExperimentalFrpApi
-fun <A, B, Z> combine(
-    stateA: TState<A>,
-    stateB: TState<B>,
-    transform: suspend FrpScope.(A, B) -> Z,
-): TState<Z> {
-    val operatorName = "combine"
-    val name = operatorName
-    return TStateInit(
-        init(name) {
-            coroutineScope {
-                val dl1: Deferred<TStateImpl<A>> = async {
-                    stateA.init.connect(evalScope = this@init)
-                }
-                val dl2: Deferred<TStateImpl<B>> = async {
-                    stateB.init.connect(evalScope = this@init)
-                }
-                zipStates(name, operatorName, dl1.await(), dl2.await()) { a, b ->
-                    NoScope.runInFrpScope { transform(a, b) }
-                }
-            }
-        }
-    )
-}
-
-/**
- * Returns a [TState] whose value is generated with [transform] by combining the current values of
- * each given [TState].
- *
- * @see TState.combineWith
- */
-@ExperimentalFrpApi
-fun <A, B, C, Z> combine(
-    stateA: TState<A>,
-    stateB: TState<B>,
-    stateC: TState<C>,
-    transform: suspend FrpScope.(A, B, C) -> Z,
-): TState<Z> {
-    val operatorName = "combine"
-    val name = operatorName
-    return TStateInit(
-        init(name) {
-            coroutineScope {
-                val dl1: Deferred<TStateImpl<A>> = async {
-                    stateA.init.connect(evalScope = this@init)
-                }
-                val dl2: Deferred<TStateImpl<B>> = async {
-                    stateB.init.connect(evalScope = this@init)
-                }
-                val dl3: Deferred<TStateImpl<C>> = async {
-                    stateC.init.connect(evalScope = this@init)
-                }
-                zipStates(name, operatorName, dl1.await(), dl2.await(), dl3.await()) { a, b, c ->
-                    NoScope.runInFrpScope { transform(a, b, c) }
-                }
-            }
-        }
-    )
-}
-
-/**
- * Returns a [TState] whose value is generated with [transform] by combining the current values of
- * each given [TState].
- *
- * @see TState.combineWith
- */
-@ExperimentalFrpApi
-fun <A, B, C, D, Z> combine(
-    stateA: TState<A>,
-    stateB: TState<B>,
-    stateC: TState<C>,
-    stateD: TState<D>,
-    transform: suspend FrpScope.(A, B, C, D) -> Z,
-): TState<Z> {
-    val operatorName = "combine"
-    val name = operatorName
-    return TStateInit(
-        init(name) {
-            coroutineScope {
-                val dl1: Deferred<TStateImpl<A>> = async {
-                    stateA.init.connect(evalScope = this@init)
-                }
-                val dl2: Deferred<TStateImpl<B>> = async {
-                    stateB.init.connect(evalScope = this@init)
-                }
-                val dl3: Deferred<TStateImpl<C>> = async {
-                    stateC.init.connect(evalScope = this@init)
-                }
-                val dl4: Deferred<TStateImpl<D>> = async {
-                    stateD.init.connect(evalScope = this@init)
-                }
-                zipStates(name, operatorName, dl1.await(), dl2.await(), dl3.await(), dl4.await()) {
-                    a,
-                    b,
-                    c,
-                    d ->
-                    NoScope.runInFrpScope { transform(a, b, c, d) }
-                }
-            }
-        }
-    )
-}
-
-/**
- * Returns a [TState] whose value is generated with [transform] by combining the current values of
- * each given [TState].
- *
- * @see TState.combineWith
- */
-@ExperimentalFrpApi
-fun <A, B, C, D, E, Z> combine(
-    stateA: TState<A>,
-    stateB: TState<B>,
-    stateC: TState<C>,
-    stateD: TState<D>,
-    stateE: TState<E>,
-    transform: suspend FrpScope.(A, B, C, D, E) -> Z,
-): TState<Z> {
-    val operatorName = "combine"
-    val name = operatorName
-    return TStateInit(
-        init(name) {
-            coroutineScope {
-                val dl1: Deferred<TStateImpl<A>> = async {
-                    stateA.init.connect(evalScope = this@init)
-                }
-                val dl2: Deferred<TStateImpl<B>> = async {
-                    stateB.init.connect(evalScope = this@init)
-                }
-                val dl3: Deferred<TStateImpl<C>> = async {
-                    stateC.init.connect(evalScope = this@init)
-                }
-                val dl4: Deferred<TStateImpl<D>> = async {
-                    stateD.init.connect(evalScope = this@init)
-                }
-                val dl5: Deferred<TStateImpl<E>> = async {
-                    stateE.init.connect(evalScope = this@init)
-                }
-                zipStates(
-                    name,
-                    operatorName,
-                    dl1.await(),
-                    dl2.await(),
-                    dl3.await(),
-                    dl4.await(),
-                    dl5.await(),
-                ) { a, b, c, d, e ->
-                    NoScope.runInFrpScope { transform(a, b, c, d, e) }
-                }
-            }
-        }
-    )
-}
-
-/** Returns a [TState] by applying [transform] to the value held by the original [TState]. */
-@ExperimentalFrpApi
-fun <A, B> TState<A>.flatMap(transform: suspend FrpScope.(A) -> TState<B>): TState<B> {
-    val operatorName = "flatMap"
-    val name = operatorName
-    return TStateInit(
-        init(name) {
-            init.connect(this).flatMap(name, operatorName) { a ->
-                NoScope.runInFrpScope { transform(a) }.init.connect(this)
-            }
-        }
-    )
-}
-
-/** Shorthand for `flatMap { it }` */
-@ExperimentalFrpApi fun <A> TState<TState<A>>.flatten() = flatMap { it }
-
-/**
- * Returns a [TStateSelector] that can be used to efficiently check if the input [TState] is
- * currently holding a specific value.
- *
- * An example:
- * ```
- *   val lInt: TState<Int> = ...
- *   val intSelector: TStateSelector<Int> = lInt.selector()
- *   // Tracks if lInt is holding 1
- *   val isOne: TState<Boolean> = intSelector.whenSelected(1)
- * ```
- *
- * This is semantically equivalent to `val isOne = lInt.map { i -> i == 1 }`, but is significantly
- * more efficient; specifically, using [TState.map] in this way incurs a `O(n)` performance hit,
- * where `n` is the number of different [TState.map] operations used to track a specific value.
- * [selector] internally uses a [HashMap] to lookup the appropriate downstream [TState] to update,
- * and so operates in `O(1)`.
- *
- * Note that the result [TStateSelector] should be cached and re-used to gain the performance
- * benefit.
- *
- * @see groupByKey
- */
-@ExperimentalFrpApi
-fun <A> TState<A>.selector(numDistinctValues: Int? = null): TStateSelector<A> =
-    TStateSelector(
-        this,
-        stateChanges
-            .map { new -> mapOf(new to true, sampleDeferred().get() to false) }
-            .groupByKey(numDistinctValues),
-    )
-
-/**
- * Tracks the currently selected value of type [A] from an upstream [TState].
- *
- * @see selector
- */
-@ExperimentalFrpApi
-class TStateSelector<in A>
-internal constructor(
-    private val upstream: TState<A>,
-    private val groupedChanges: GroupedTFlow<A, Boolean>,
-) {
-    /**
-     * Returns a [TState] that tracks whether the upstream [TState] is currently holding the given
-     * [value].
-     *
-     * @see selector
-     */
-    @ExperimentalFrpApi
-    fun whenSelected(value: A): TState<Boolean> {
-        val operatorName = "TStateSelector#whenSelected"
-        val name = "$operatorName[$value]"
-        return TStateInit(
-            init(name) {
-                DerivedMapCheap(
-                    name,
-                    operatorName,
-                    upstream = upstream.init.connect(evalScope = this),
-                    changes = groupedChanges.impl.eventsForKey(value),
-                ) {
-                    it == value
-                }
-            }
-        )
-    }
-
-    @ExperimentalFrpApi operator fun get(value: A): TState<Boolean> = whenSelected(value)
-}
-
-/** TODO */
-@ExperimentalFrpApi
-class MutableTState<T>
-internal constructor(internal val network: Network, initialValue: Deferred<T>) : TState<T>() {
-
-    private val input: CoalescingMutableTFlow<Deferred<T>, Deferred<T>?> =
-        CoalescingMutableTFlow(
-            name = null,
-            coalesce = { _, new -> new },
-            network = network,
-            getInitialValue = { null },
-        )
-
-    internal val tState = run {
-        val changes = input.impl
-        val name = null
-        val operatorName = "MutableTState"
-        lateinit var state: TStateSource<T>
-        val calm: TFlowImpl<T> =
-            filterNode({ mapImpl(upstream = { changes.activated() }) { it!!.await() } }) { new ->
-                    new != state.getCurrentWithEpoch(evalScope = this).first
-                }
-                .cached()
-        state = TStateSource(name, operatorName, initialValue, calm)
-        @Suppress("DeferredResultUnused")
-        network.transaction("MutableTState.init") {
-            calm.activate(evalScope = this, downstream = Schedulable.S(state))?.let {
-                (connection, needsEval) ->
-                state.upstreamConnection = connection
-                if (needsEval) {
-                    schedule(state)
-                }
-            }
-        }
-        TStateInit(constInit(name, state))
-    }
-
-    /** TODO */
-    @ExperimentalFrpApi fun setValue(value: T) = input.emit(CompletableDeferred(value))
-
-    @ExperimentalFrpApi
-    fun setValueDeferred(value: FrpDeferredValue<T>) = input.emit(value.unwrapped)
-}
-
-/** A forward-reference to a [TState], allowing for recursive definitions. */
-@ExperimentalFrpApi
-class TStateLoop<A> : TState<A>() {
-
-    private val name: String? = null
-
-    private val deferred = CompletableDeferred<TState<A>>()
-
-    internal val init: Init<TStateImpl<A>> =
-        init(name) { deferred.await().init.connect(evalScope = this) }
-
-    /** The [TState] this [TStateLoop] will forward to. */
-    @ExperimentalFrpApi
-    var loopback: TState<A>? = null
-        set(value) {
-            value?.let {
-                check(deferred.complete(value)) { "TStateLoop.loopback has already been set." }
-                field = value
-            }
-        }
-
-    @ExperimentalFrpApi
-    operator fun getValue(thisRef: Any?, property: KProperty<*>): TState<A> = this
-
-    @ExperimentalFrpApi
-    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: TState<A>) {
-        loopback = value
-    }
-
-    override fun toString(): String = "${this::class.simpleName}@$hashString"
-}
-
-internal class TStateInit<A> internal constructor(internal val init: Init<TStateImpl<A>>) :
-    TState<A>() {
-    override fun toString(): String = "${this::class.simpleName}@$hashString"
-}
-
-internal val <A> TState<A>.init: Init<TStateImpl<A>>
-    get() =
-        when (this) {
-            is TStateInit -> init
-            is TStateLoop -> init
-            is MutableTState -> tState.init
-        }
-
-private inline fun <A> deferInline(
-    crossinline block: suspend InitScope.() -> TState<A>
-): TState<A> = TStateInit(init(name = null) { block().init.connect(evalScope = this) })
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/ToColdFlow.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/ToColdFlow.kt
new file mode 100644
index 0000000..3d2768b
--- /dev/null
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/ToColdFlow.kt
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.kairos
+
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.channelFlow
+import kotlinx.coroutines.flow.conflate
+
+/**
+ * Returns a cold [Flow] that, when collected, emits from this [Events]. [network] is needed to
+ * transactionally connect to / disconnect from the [Events] when collection starts/stops.
+ */
+@ExperimentalKairosApi
+fun <A> Events<A>.toColdConflatedFlow(network: KairosNetwork): Flow<A> =
+    channelFlow { network.activateSpec { observe { trySend(it) } } }.conflate()
+
+/**
+ * Returns a cold [Flow] that, when collected, emits from this [State]. [network] is needed to
+ * transactionally connect to / disconnect from the [State] when collection starts/stops.
+ */
+@ExperimentalKairosApi
+fun <A> State<A>.toColdConflatedFlow(network: KairosNetwork): Flow<A> =
+    channelFlow { network.activateSpec { observe { trySend(it) } } }.conflate()
+
+/**
+ * Returns a cold [Flow] that, when collected, applies this [BuildSpec] in a new transaction in this
+ * [network], and then emits from the returned [Events].
+ *
+ * When collection is cancelled, so is the [BuildSpec]. This means all ongoing work is cleaned up.
+ */
+@ExperimentalKairosApi
+@JvmName("eventsSpecToColdConflatedFlow")
+fun <A> BuildSpec<Events<A>>.toColdConflatedFlow(network: KairosNetwork): Flow<A> =
+    channelFlow { network.activateSpec { applySpec().observe { trySend(it) } } }.conflate()
+
+/**
+ * Returns a cold [Flow] that, when collected, applies this [BuildSpec] in a new transaction in this
+ * [network], and then emits from the returned [State].
+ *
+ * When collection is cancelled, so is the [BuildSpec]. This means all ongoing work is cleaned up.
+ */
+@ExperimentalKairosApi
+@JvmName("stateSpecToColdConflatedFlow")
+fun <A> BuildSpec<State<A>>.toColdConflatedFlow(network: KairosNetwork): Flow<A> =
+    channelFlow { network.activateSpec { applySpec().observe { trySend(it) } } }.conflate()
+
+/**
+ * Returns a cold [Flow] that, when collected, applies this [Transactional] in a new transaction in
+ * this [network], and then emits from the returned [Events].
+ */
+@ExperimentalKairosApi
+@JvmName("transactionalFlowToColdConflatedFlow")
+fun <A> Transactional<Events<A>>.toColdConflatedFlow(network: KairosNetwork): Flow<A> =
+    channelFlow { network.activateSpec { sample().observe { trySend(it) } } }.conflate()
+
+/**
+ * Returns a cold [Flow] that, when collected, applies this [Transactional] in a new transaction in
+ * this [network], and then emits from the returned [State].
+ */
+@ExperimentalKairosApi
+@JvmName("transactionalStateToColdConflatedFlow")
+fun <A> Transactional<State<A>>.toColdConflatedFlow(network: KairosNetwork): Flow<A> =
+    channelFlow { network.activateSpec { sample().observe { trySend(it) } } }.conflate()
+
+/**
+ * Returns a cold [Flow] that, when collected, applies this [Stateful] in a new transaction in this
+ * [network], and then emits from the returned [Events].
+ *
+ * When collection is cancelled, so is the [Stateful]. This means all ongoing work is cleaned up.
+ */
+@ExperimentalKairosApi
+@JvmName("statefulFlowToColdConflatedFlow")
+fun <A> Stateful<Events<A>>.toColdConflatedFlow(network: KairosNetwork): Flow<A> =
+    channelFlow { network.activateSpec { applyStateful().observe { trySend(it) } } }.conflate()
+
+/**
+ * Returns a cold [Flow] that, when collected, applies this [Transactional] in a new transaction in
+ * this [network], and then emits from the returned [State].
+ *
+ * When collection is cancelled, so is the [Stateful]. This means all ongoing work is cleaned up.
+ */
+@ExperimentalKairosApi
+@JvmName("statefulStateToColdConflatedFlow")
+fun <A> Stateful<State<A>>.toColdConflatedFlow(network: KairosNetwork): Flow<A> =
+    channelFlow { network.activateSpec { applyStateful().observe { trySend(it) } } }.conflate()
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/TransactionScope.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/TransactionScope.kt
new file mode 100644
index 0000000..a5ac909
--- /dev/null
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/TransactionScope.kt
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.kairos
+
+import com.android.systemui.kairos.util.Maybe
+import com.android.systemui.kairos.util.These
+
+/**
+ * Kairos operations that are available while a transaction is active.
+ *
+ * These operations do not accumulate state, which makes [TransactionScope] weaker than
+ * [StateScope], but allows it to be used in more places.
+ */
+@ExperimentalKairosApi
+interface TransactionScope : KairosScope {
+
+    /**
+     * Returns the current value of this [Transactional] as a [DeferredValue].
+     *
+     * Compared to [sample], you may want to use this instead if you do not need to inspect the
+     * sampled value, but instead want to pass it to another Kairos API that accepts a
+     * [DeferredValue]. In this case, [sampleDeferred] is both safer and more performant.
+     *
+     * @see sample
+     */
+    fun <A> Transactional<A>.sampleDeferred(): DeferredValue<A>
+
+    /**
+     * Returns the current value of this [State] as a [DeferredValue].
+     *
+     * Compared to [sample], you may want to use this instead if you do not need to inspect the
+     * sampled value, but instead want to pass it to another Kairos API that accepts a
+     * [DeferredValue]. In this case, [sampleDeferred] is both safer and more performant.
+     *
+     * @see sample
+     */
+    fun <A> State<A>.sampleDeferred(): DeferredValue<A>
+
+    /**
+     * Defers invoking [block] until after the current [TransactionScope] code-path completes,
+     * returning a [DeferredValue] that can be used to reference the result.
+     *
+     * Useful for recursive definitions.
+     *
+     * @see DeferredValue
+     */
+    fun <A> deferredTransactionScope(block: TransactionScope.() -> A): DeferredValue<A>
+
+    /** An [Events] that emits once, within the current transaction, and then never again. */
+    val now: Events<Unit>
+
+    /**
+     * Returns the current value held by this [State]. Guaranteed to be consistent within the same
+     * transaction.
+     *
+     * @see sampleDeferred
+     */
+    fun <A> State<A>.sample(): A = sampleDeferred().value
+
+    /**
+     * Returns the current value held by this [Transactional]. Guaranteed to be consistent within
+     * the same transaction.
+     *
+     * @see sampleDeferred
+     */
+    fun <A> Transactional<A>.sample(): A = sampleDeferred().value
+}
+
+/**
+ * Returns an [Events] that emits the value sampled from the [Transactional] produced by each
+ * emission of the original [Events], within the same transaction of the original emission.
+ */
+@ExperimentalKairosApi
+fun <A> Events<Transactional<A>>.sampleTransactionals(): Events<A> = map { it.sample() }
+
+/** @see TransactionScope.sample */
+@ExperimentalKairosApi
+fun <A, B, C> Events<A>.sample(
+    state: State<B>,
+    transform: TransactionScope.(A, B) -> C,
+): Events<C> = map { transform(it, state.sample()) }
+
+/** @see TransactionScope.sample */
+@ExperimentalKairosApi
+fun <A, B, C> Events<A>.sample(
+    sampleable: Transactional<B>,
+    transform: TransactionScope.(A, B) -> C,
+): Events<C> = map { transform(it, sampleable.sample()) }
+
+/**
+ * Like [sample], but if [state] is changing at the time it is sampled ([changes] is emitting), then
+ * the new value is passed to [transform].
+ *
+ * Note that [sample] is both more performant and safer to use with recursive definitions. You will
+ * generally want to use it rather than this.
+ *
+ * @see sample
+ */
+@ExperimentalKairosApi
+fun <A, B, C> Events<A>.samplePromptly(
+    state: State<B>,
+    transform: TransactionScope.(A, B) -> C,
+): Events<C> =
+    sample(state) { a, b -> These.first(a to b) }
+        .mergeWith(state.changes.map { These.second(it) }) { thiz, that ->
+            These.both((thiz as These.First).value, (that as These.Second).value)
+        }
+        .mapMaybe { these ->
+            when (these) {
+                // both present, transform the upstream value and the new value
+                is These.Both -> Maybe.present(transform(these.first.first, these.second))
+                // no upstream present, so don't perform the sample
+                is These.Second -> Maybe.absent()
+                // just the upstream, so transform the upstream and the old value
+                is These.First -> Maybe.present(transform(these.value.first, these.value.second))
+            }
+        }
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Transactional.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Transactional.kt
index 6b1c8c8..cf98821 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Transactional.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/Transactional.kt
@@ -16,57 +16,94 @@
 
 package com.android.systemui.kairos
 
+import com.android.systemui.kairos.internal.CompletableLazy
 import com.android.systemui.kairos.internal.InitScope
 import com.android.systemui.kairos.internal.NoScope
 import com.android.systemui.kairos.internal.TransactionalImpl
 import com.android.systemui.kairos.internal.init
 import com.android.systemui.kairos.internal.transactionalImpl
 import com.android.systemui.kairos.internal.util.hashString
-import kotlinx.coroutines.CompletableDeferred
 
 /**
  * A time-varying value. A [Transactional] encapsulates the idea of some continuous state; each time
  * it is "sampled", a new result may be produced.
  *
- * Because FRP operates over an "idealized" model of Time that can be passed around as a data type,
- * [Transactional]s are guaranteed to produce the same result if queried multiple times at the same
- * (conceptual) time, in order to preserve _referential transparency_.
+ * Because Kairos operates over an "idealized" model of Time that can be passed around as a data
+ * type, [Transactionals][Transactional] are guaranteed to produce the same result if queried
+ * multiple times at the same (conceptual) time, in order to preserve _referential transparency_.
  */
-@ExperimentalFrpApi
-class Transactional<out A> internal constructor(internal val impl: TState<TransactionalImpl<A>>) {
+@ExperimentalKairosApi
+class Transactional<out A> internal constructor(internal val impl: State<TransactionalImpl<A>>) {
     override fun toString(): String = "${this::class.simpleName}@$hashString"
 }
 
 /** A constant [Transactional] that produces [value] whenever it is sampled. */
-@ExperimentalFrpApi
+@ExperimentalKairosApi
 fun <A> transactionalOf(value: A): Transactional<A> =
-    Transactional(tStateOf(TransactionalImpl.Const(CompletableDeferred(value))))
+    Transactional(stateOf(TransactionalImpl.Const(CompletableLazy(value))))
 
-/** TODO */
-@ExperimentalFrpApi
-fun <A> FrpDeferredValue<Transactional<A>>.defer(): Transactional<A> = deferInline {
-    unwrapped.await()
-}
+/**
+ * Returns a [Transactional] that acts as a deferred-reference to the [Transactional] produced by
+ * this [DeferredValue].
+ *
+ * When the returned [Transactional] is accessed by the Kairos network, the [DeferredValue] will be
+ * queried and used.
+ *
+ * Useful for recursive definitions.
+ *
+ * ``` kotlin
+ *   fun <A> DeferredValue<Transactional<A>>.defer() = deferredTransactional { get() }
+ * ```
+ */
+@ExperimentalKairosApi
+fun <A> DeferredValue<Transactional<A>>.defer(): Transactional<A> = deferInline { unwrapped.value }
 
-/** TODO */
-@ExperimentalFrpApi fun <A> Lazy<Transactional<A>>.defer(): Transactional<A> = deferInline { value }
+/**
+ * Returns a [Transactional] that acts as a deferred-reference to the [Transactional] produced by
+ * this [Lazy].
+ *
+ * When the returned [Transactional] is accessed by the Kairos network, the [Lazy]'s
+ * [value][Lazy.value] will be queried and used.
+ *
+ * Useful for recursive definitions.
+ *
+ * ``` kotlin
+ *   fun <A> Lazy<Transactional<A>>.defer() = deferredTransactional { value }
+ * ```
+ */
+@ExperimentalKairosApi
+fun <A> Lazy<Transactional<A>>.defer(): Transactional<A> = deferInline { value }
 
-/** TODO */
-@ExperimentalFrpApi
-fun <A> deferTransactional(block: suspend FrpScope.() -> Transactional<A>): Transactional<A> =
+/**
+ * Returns a [Transactional] that acts as a deferred-reference to the [Transactional] produced by
+ * [block].
+ *
+ * When the returned [Transactional] is accessed by the Kairos network, [block] will be invoked and
+ * the returned [Transactional] will be used.
+ *
+ * Useful for recursive definitions.
+ */
+@ExperimentalKairosApi
+fun <A> deferredTransactional(block: KairosScope.() -> Transactional<A>): Transactional<A> =
     deferInline {
-        NoScope.runInFrpScope(block)
+        NoScope.block()
     }
 
 private inline fun <A> deferInline(
-    crossinline block: suspend InitScope.() -> Transactional<A>
+    crossinline block: InitScope.() -> Transactional<A>
 ): Transactional<A> =
-    Transactional(TStateInit(init(name = null) { block().impl.init.connect(evalScope = this) }))
+    Transactional(StateInit(init(name = null) { block().impl.init.connect(evalScope = this) }))
 
 /**
  * Returns a [Transactional]. The passed [block] will be evaluated on demand at most once per
  * transaction; any subsequent sampling within the same transaction will receive a cached value.
+ *
+ * @sample com.android.systemui.kairos.KairosSamples.sampleTransactional
  */
-@ExperimentalFrpApi
-fun <A> transactionally(block: suspend FrpTransactionScope.() -> A): Transactional<A> =
-    Transactional(tStateOf(transactionalImpl { runInTransactionScope(block) }))
+@ExperimentalKairosApi
+fun <A> transactionally(block: TransactionScope.() -> A): Transactional<A> =
+    Transactional(stateOf(transactionalImpl { block() }))
+
+/** Returns a [Transactional] that, when queried, samples this [State]. */
+fun <A> State<A>.asTransactional(): Transactional<A> =
+    Transactional(map { TransactionalImpl.Const(CompletableLazy(it)) })
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/debug/Debug.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/debug/Debug.kt
deleted file mode 100644
index 0674a2e..0000000
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/debug/Debug.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.kairos.debug
-
-import com.android.systemui.kairos.MutableTState
-import com.android.systemui.kairos.TState
-import com.android.systemui.kairos.TStateInit
-import com.android.systemui.kairos.TStateLoop
-import com.android.systemui.kairos.internal.DerivedFlatten
-import com.android.systemui.kairos.internal.DerivedMap
-import com.android.systemui.kairos.internal.DerivedMapCheap
-import com.android.systemui.kairos.internal.DerivedZipped
-import com.android.systemui.kairos.internal.Init
-import com.android.systemui.kairos.internal.TStateDerived
-import com.android.systemui.kairos.internal.TStateImpl
-import com.android.systemui.kairos.internal.TStateSource
-import com.android.systemui.kairos.util.Just
-import com.android.systemui.kairos.util.Maybe
-import com.android.systemui.kairos.util.None
-import com.android.systemui.kairos.util.flatMap
-import com.android.systemui.kairos.util.map
-import com.android.systemui.kairos.util.none
-import com.android.systemui.kairos.util.orElseGet
-
-// object IdGen {
-//    private val counter = AtomicLong()
-//    fun getId() = counter.getAndIncrement()
-// }
-
-typealias StateGraph = Graph<ActivationInfo>
-
-sealed class StateInfo(
-    val name: String,
-    val value: Maybe<Any?>,
-    val operator: String,
-    val epoch: Long?,
-)
-
-class Source(name: String, value: Maybe<Any?>, operator: String, epoch: Long) :
-    StateInfo(name, value, operator, epoch)
-
-class Derived(
-    name: String,
-    val type: DerivedStateType,
-    value: Maybe<Any?>,
-    operator: String,
-    epoch: Long?,
-) : StateInfo(name, value, operator, epoch)
-
-sealed interface DerivedStateType
-
-data object Flatten : DerivedStateType
-
-data class Mapped(val cheap: Boolean) : DerivedStateType
-
-data object Combine : DerivedStateType
-
-sealed class InitInfo(val name: String)
-
-class Uninitialized(name: String) : InitInfo(name)
-
-class Initialized(val state: StateInfo) : InitInfo(state.name)
-
-sealed interface ActivationInfo
-
-class Inactive(val name: String) : ActivationInfo
-
-class Active(val nodeInfo: StateInfo) : ActivationInfo
-
-class Dead(val name: String) : ActivationInfo
-
-data class Edge(val upstream: Any, val downstream: Any, val tag: Any? = null)
-
-data class Graph<T>(val nodes: Map<Any, T>, val edges: List<Edge>)
-
-internal fun TState<*>.dump(infoMap: MutableMap<Any, InitInfo>, edges: MutableList<Edge>) {
-    val init: Init<TStateImpl<Any?>> =
-        when (this) {
-            is TStateInit -> init
-            is TStateLoop -> init
-            is MutableTState -> tState.init
-        }
-    when (val stateMaybe = init.getUnsafe()) {
-        None -> {
-            infoMap[this] = Uninitialized(init.name ?: init.toString())
-        }
-        is Just -> {
-            stateMaybe.value.dump(infoMap, edges)
-        }
-    }
-}
-
-internal fun TStateImpl<*>.dump(infoById: MutableMap<Any, InitInfo>, edges: MutableList<Edge>) {
-    val state = this
-    if (state in infoById) return
-    val stateInfo =
-        when (state) {
-            is TStateDerived -> {
-                val type =
-                    when (state) {
-                        is DerivedFlatten -> {
-                            state.upstream.dump(infoById, edges)
-                            edges.add(
-                                Edge(upstream = state.upstream, downstream = state, tag = "outer")
-                            )
-                            state.upstream
-                                .getUnsafe()
-                                .orElseGet { null }
-                                ?.let {
-                                    edges.add(
-                                        Edge(upstream = it, downstream = state, tag = "inner")
-                                    )
-                                    it.dump(infoById, edges)
-                                }
-                            Flatten
-                        }
-                        is DerivedMap<*, *> -> {
-                            state.upstream.dump(infoById, edges)
-                            edges.add(Edge(upstream = state.upstream, downstream = state))
-                            Mapped(cheap = false)
-                        }
-                        is DerivedZipped<*, *> -> {
-                            state.upstream.forEach { (key, upstream) ->
-                                edges.add(
-                                    Edge(upstream = upstream, downstream = state, tag = "key=$key")
-                                )
-                                upstream.dump(infoById, edges)
-                            }
-                            Combine
-                        }
-                    }
-                Derived(
-                    state.name ?: state.operatorName,
-                    type,
-                    state.getCachedUnsafe(),
-                    state.operatorName,
-                    state.invalidatedEpoch,
-                )
-            }
-            is TStateSource ->
-                Source(
-                    state.name ?: state.operatorName,
-                    state.getStorageUnsafe(),
-                    state.operatorName,
-                    state.writeEpoch,
-                )
-            is DerivedMapCheap<*, *> -> {
-                state.upstream.dump(infoById, edges)
-                edges.add(Edge(upstream = state.upstream, downstream = state))
-                val type = Mapped(cheap = true)
-                Derived(
-                    state.name ?: state.operatorName,
-                    type,
-                    state.getUnsafe(),
-                    state.operatorName,
-                    null,
-                )
-            }
-        }
-    infoById[state] = Initialized(stateInfo)
-}
-
-private fun <A> TStateImpl<A>.getUnsafe(): Maybe<A> =
-    when (this) {
-        is TStateDerived -> getCachedUnsafe()
-        is TStateSource -> getStorageUnsafe()
-        is DerivedMapCheap<*, *> -> none
-    }
-
-private fun <A> TStateImpl<A>.getUnsafeWithEpoch(): Maybe<Pair<A, Long>> =
-    when (this) {
-        is TStateDerived -> getCachedUnsafe().map { it to invalidatedEpoch }
-        is TStateSource -> getStorageUnsafe().map { it to writeEpoch }
-        is DerivedMapCheap<*, *> -> none
-    }
-
-/**
- * Returns the current value held in this [TState], or [none] if the [TState] has not been
- * initialized.
- *
- * The returned [Long] is the *epoch* at which the internal cache was last updated. This can be used
- * to identify values which are out-of-date.
- */
-fun <A> TState<A>.sampleUnsafe(): Maybe<Pair<A, Long>> =
-    when (this) {
-        is MutableTState -> tState.init.getUnsafe().flatMap { it.getUnsafeWithEpoch() }
-        is TStateInit -> init.getUnsafe().flatMap { it.getUnsafeWithEpoch() }
-        is TStateLoop -> this.init.getUnsafe().flatMap { it.getUnsafeWithEpoch() }
-    }
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/BuildScopeImpl.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/BuildScopeImpl.kt
index 7e63849..2f4c396 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/BuildScopeImpl.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/BuildScopeImpl.kt
@@ -16,155 +16,116 @@
 
 package com.android.systemui.kairos.internal
 
-import com.android.systemui.kairos.CoalescingMutableTFlow
-import com.android.systemui.kairos.FrpBuildScope
-import com.android.systemui.kairos.FrpCoalescingProducerScope
-import com.android.systemui.kairos.FrpDeferredValue
-import com.android.systemui.kairos.FrpEffectScope
-import com.android.systemui.kairos.FrpNetwork
-import com.android.systemui.kairos.FrpProducerScope
-import com.android.systemui.kairos.FrpSpec
-import com.android.systemui.kairos.FrpStateScope
-import com.android.systemui.kairos.FrpTransactionScope
-import com.android.systemui.kairos.GroupedTFlow
-import com.android.systemui.kairos.LocalFrpNetwork
-import com.android.systemui.kairos.MutableTFlow
-import com.android.systemui.kairos.TFlow
-import com.android.systemui.kairos.TFlowInit
+import com.android.systemui.kairos.BuildScope
+import com.android.systemui.kairos.BuildSpec
+import com.android.systemui.kairos.CoalescingEventProducerScope
+import com.android.systemui.kairos.CoalescingMutableEvents
+import com.android.systemui.kairos.DeferredValue
+import com.android.systemui.kairos.EffectScope
+import com.android.systemui.kairos.EventProducerScope
+import com.android.systemui.kairos.Events
+import com.android.systemui.kairos.EventsInit
+import com.android.systemui.kairos.GroupedEvents
+import com.android.systemui.kairos.KairosCoroutineScope
+import com.android.systemui.kairos.KairosNetwork
+import com.android.systemui.kairos.LocalNetwork
+import com.android.systemui.kairos.MutableEvents
+import com.android.systemui.kairos.TransactionScope
 import com.android.systemui.kairos.groupByKey
 import com.android.systemui.kairos.init
 import com.android.systemui.kairos.internal.util.childScope
-import com.android.systemui.kairos.internal.util.mapValuesParallel
+import com.android.systemui.kairos.internal.util.invokeOnCancel
+import com.android.systemui.kairos.internal.util.launchImmediate
 import com.android.systemui.kairos.launchEffect
 import com.android.systemui.kairos.mergeLeft
-import com.android.systemui.kairos.util.Just
 import com.android.systemui.kairos.util.Maybe
-import com.android.systemui.kairos.util.None
-import com.android.systemui.kairos.util.just
+import com.android.systemui.kairos.util.Maybe.Absent
+import com.android.systemui.kairos.util.Maybe.Present
 import com.android.systemui.kairos.util.map
 import java.util.concurrent.atomic.AtomicReference
-import kotlin.coroutines.Continuation
 import kotlin.coroutines.CoroutineContext
-import kotlin.coroutines.EmptyCoroutineContext
-import kotlin.coroutines.startCoroutine
-import kotlinx.coroutines.CompletableDeferred
 import kotlinx.coroutines.CompletableJob
 import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.CoroutineStart
 import kotlinx.coroutines.Deferred
+import kotlinx.coroutines.DisposableHandle
 import kotlinx.coroutines.Job
+import kotlinx.coroutines.async
 import kotlinx.coroutines.cancel
-import kotlinx.coroutines.completeWith
 import kotlinx.coroutines.job
 
 internal class BuildScopeImpl(val stateScope: StateScopeImpl, val coroutineScope: CoroutineScope) :
-    BuildScope, StateScope by stateScope {
+    InternalBuildScope, InternalStateScope by stateScope {
 
     private val job: Job
         get() = coroutineScope.coroutineContext.job
 
-    override val frpScope: FrpBuildScope = FrpBuildScopeImpl()
-
-    override suspend fun <R> runInBuildScope(block: suspend FrpBuildScope.() -> R): R {
-        val complete = CompletableDeferred<R>(parent = coroutineContext.job)
-        block.startCoroutine(
-            frpScope,
-            object : Continuation<R> {
-                override val context: CoroutineContext
-                    get() = EmptyCoroutineContext
-
-                override fun resumeWith(result: Result<R>) {
-                    complete.completeWith(result)
-                }
-            },
-        )
-        return complete.await()
+    override val kairosNetwork: KairosNetwork by lazy {
+        LocalNetwork(network, coroutineScope, endSignal)
     }
 
-    private fun <A, T : TFlow<A>, S> buildTFlow(
-        constructFlow: (InputNode<A>) -> Pair<T, S>,
-        builder: suspend S.() -> Unit,
-    ): TFlow<A> {
-        var job: Job? = null
-        val stopEmitter = newStopEmitter("buildTFlow")
-        // Create a child scope that will be kept alive beyond the end of this transaction.
-        val childScope = coroutineScope.childScope()
-        lateinit var emitter: Pair<T, S>
-        val inputNode =
-            InputNode<A>(
-                activate = {
-                    check(job == null) { "already activated" }
-                    job =
-                        reenterBuildScope(this@BuildScopeImpl, childScope).runInBuildScope {
-                            launchEffect {
-                                builder(emitter.second)
-                                stopEmitter.emit(Unit)
-                            }
-                        }
-                },
-                deactivate = {
-                    checkNotNull(job) { "already deactivated" }.cancel()
-                    job = null
-                },
-            )
-        emitter = constructFlow(inputNode)
-        return with(frpScope) { emitter.first.takeUntil(mergeLeft(stopEmitter, endSignal)) }
-    }
-
-    private fun <T> tFlowInternal(builder: suspend FrpProducerScope<T>.() -> Unit): TFlow<T> =
-        buildTFlow(
-            constructFlow = { inputNode ->
-                val flow = MutableTFlow(network, inputNode)
-                flow to
-                    object : FrpProducerScope<T> {
+    override fun <T> events(builder: suspend EventProducerScope<T>.() -> Unit): Events<T> =
+        buildEvents(
+            constructEvents = { inputNode ->
+                val events = MutableEvents(network, inputNode)
+                events to
+                    object : EventProducerScope<T> {
                         override suspend fun emit(value: T) {
-                            flow.emit(value)
+                            events.emit(value)
                         }
                     }
             },
             builder = builder,
         )
 
-    private fun <In, Out> coalescingTFlowInternal(
+    override fun <In, Out> coalescingEvents(
         getInitialValue: () -> Out,
         coalesce: (old: Out, new: In) -> Out,
-        builder: suspend FrpCoalescingProducerScope<In>.() -> Unit,
-    ): TFlow<Out> =
-        buildTFlow(
-            constructFlow = { inputNode ->
-                val flow =
-                    CoalescingMutableTFlow(null, coalesce, network, getInitialValue, inputNode)
-                flow to
-                    object : FrpCoalescingProducerScope<In> {
+        builder: suspend CoalescingEventProducerScope<In>.() -> Unit,
+    ): Events<Out> =
+        buildEvents(
+            constructEvents = { inputNode ->
+                val events =
+                    CoalescingMutableEvents(
+                        null,
+                        coalesce = { old, new: In -> coalesce(old.value, new) },
+                        network,
+                        getInitialValue,
+                        inputNode,
+                    )
+                events to
+                    object : CoalescingEventProducerScope<In> {
                         override fun emit(value: In) {
-                            flow.emit(value)
+                            events.emit(value)
                         }
                     }
             },
             builder = builder,
         )
 
-    private fun <A> asyncScopeInternal(block: FrpSpec<A>): Pair<FrpDeferredValue<A>, Job> {
+    override fun <A> asyncScope(block: BuildSpec<A>): Pair<DeferredValue<A>, Job> {
         val childScope = mutableChildBuildScope()
-        return FrpDeferredValue(deferAsync { childScope.runInBuildScope(block) }) to childScope.job
+        return DeferredValue(deferAsync { block(childScope) }) to childScope.job
     }
 
-    private fun <R> deferredInternal(block: suspend FrpBuildScope.() -> R): FrpDeferredValue<R> =
-        FrpDeferredValue(deferAsync { runInBuildScope(block) })
+    override fun <R> deferredBuildScope(block: BuildScope.() -> R): DeferredValue<R> =
+        DeferredValue(deferAsync { block() })
 
-    private fun deferredActionInternal(block: suspend FrpBuildScope.() -> Unit) {
-        deferAction { runInBuildScope(block) }
+    override fun deferredBuildScopeAction(block: BuildScope.() -> Unit) {
+        deferAction { block() }
     }
 
-    private fun <A> TFlow<A>.observeEffectInternal(
-        context: CoroutineContext,
-        block: suspend FrpEffectScope.(A) -> Unit,
-    ): Job {
+    override fun <A> Events<A>.observe(
+        coroutineContext: CoroutineContext,
+        block: EffectScope.(A) -> Unit,
+    ): DisposableHandle {
         val subRef = AtomicReference<Maybe<Output<A>>>(null)
         val childScope = coroutineScope.childScope()
-        // When our scope is cancelled, deactivate this observer.
-        childScope.coroutineContext.job.invokeOnCompletion {
-            subRef.getAndSet(None)?.let { output ->
-                if (output is Just) {
+        lateinit var cancelHandle: DisposableHandle
+        val handle = DisposableHandle {
+            cancelHandle.dispose()
+            subRef.getAndSet(Absent)?.let { output ->
+                if (output is Present) {
                     @Suppress("DeferredResultUnused")
                     network.transaction("observeEffect cancelled") {
                         scheduleDeactivation(output.value)
@@ -172,112 +133,156 @@
                 }
             }
         }
-        // Defer so that we don't suspend the caller
-        deferAction {
-            val outputNode =
-                Output<A>(
-                    context = context,
-                    onDeath = { subRef.getAndSet(None)?.let { childScope.cancel() } },
-                    onEmit = { output ->
-                        if (subRef.get() is Just) {
-                            // Not cancelled, safe to emit
-                            val coroutine: suspend FrpEffectScope.() -> Unit = { block(output) }
-                            val complete = CompletableDeferred<Unit>(parent = coroutineContext.job)
-                            coroutine.startCoroutine(
-                                object : FrpEffectScope, FrpTransactionScope by frpScope {
-                                    override val frpCoroutineScope: CoroutineScope = childScope
-                                    override val frpNetwork: FrpNetwork =
-                                        LocalFrpNetwork(network, childScope, endSignal)
-                                },
-                                completion =
-                                    object : Continuation<Unit> {
-                                        override val context: CoroutineContext
-                                            get() = EmptyCoroutineContext
+        // When our scope is cancelled, deactivate this observer.
+        cancelHandle = childScope.coroutineContext.job.invokeOnCompletion { handle.dispose() }
+        val localNetwork = LocalNetwork(network, childScope, endSignal)
+        val outputNode =
+            Output<A>(
+                context = coroutineContext,
+                onDeath = { subRef.set(Absent) },
+                onEmit = { output ->
+                    if (subRef.get() is Present) {
+                        // Not cancelled, safe to emit
+                        val scope =
+                            object : EffectScope, TransactionScope by this {
+                                override fun <R> async(
+                                    context: CoroutineContext,
+                                    start: CoroutineStart,
+                                    block: suspend KairosCoroutineScope.() -> R,
+                                ): Deferred<R> =
+                                    childScope.async(context, start) {
+                                        object : KairosCoroutineScope, CoroutineScope by this {
+                                                override val kairosNetwork: KairosNetwork
+                                                    get() = localNetwork
+                                            }
+                                            .block()
+                                    }
 
-                                        override fun resumeWith(result: Result<Unit>) {
-                                            complete.completeWith(result)
-                                        }
-                                    },
-                            )
-                            complete.await()
-                        }
-                    },
-                )
-            with(frpScope) { this@observeEffectInternal.takeUntil(endSignal) }
+                                override val kairosNetwork: KairosNetwork
+                                    get() = localNetwork
+                            }
+                        scope.block(output)
+                    }
+                },
+            )
+        // Defer, in case any EventsLoops / StateLoops still need to be set
+        deferAction {
+            // Check for immediate cancellation
+            if (subRef.get() != null) return@deferAction
+            this@observe.takeUntil(endSignal)
                 .init
                 .connect(evalScope = stateScope.evalScope)
                 .activate(evalScope = stateScope.evalScope, outputNode.schedulable)
                 ?.let { (conn, needsEval) ->
                     outputNode.upstream = conn
-                    if (!subRef.compareAndSet(null, just(outputNode))) {
+                    if (!subRef.compareAndSet(null, Maybe.present(outputNode))) {
                         // Job's already been cancelled, schedule deactivation
                         scheduleDeactivation(outputNode)
                     } else if (needsEval) {
-                        outputNode.schedule(evalScope = stateScope.evalScope)
+                        outputNode.schedule(0, evalScope = stateScope.evalScope)
                     }
                 } ?: run { childScope.cancel() }
         }
-        return childScope.coroutineContext.job
+        return handle
     }
 
-    private fun <A, B> TFlow<A>.mapBuildInternal(
-        transform: suspend FrpBuildScope.(A) -> B
-    ): TFlow<B> {
+    override fun <A, B> Events<A>.mapBuild(transform: BuildScope.(A) -> B): Events<B> {
         val childScope = coroutineScope.childScope()
-        return TFlowInit(
+        return EventsInit(
             constInit(
                 "mapBuild",
-                mapImpl({ init.connect(evalScope = this) }) { spec ->
+                mapImpl({ init.connect(evalScope = this) }) { spec, _ ->
                         reenterBuildScope(outerScope = this@BuildScopeImpl, childScope)
-                            .runInBuildScope { transform(spec) }
+                            .transform(spec)
                     }
                     .cached(),
             )
         )
     }
 
-    private fun <K, A, B> TFlow<Map<K, Maybe<FrpSpec<A>>>>.applyLatestForKeyInternal(
-        init: FrpDeferredValue<Map<K, FrpSpec<B>>>,
+    override fun <K, A, B> Events<Map<K, Maybe<BuildSpec<A>>>>.applyLatestSpecForKey(
+        initialSpecs: DeferredValue<Map<K, BuildSpec<B>>>,
         numKeys: Int?,
-    ): Pair<TFlow<Map<K, Maybe<A>>>, FrpDeferredValue<Map<K, B>>> {
-        val eventsByKey: GroupedTFlow<K, Maybe<FrpSpec<A>>> = groupByKey(numKeys)
-        val initOut: Deferred<Map<K, B>> = deferAsync {
-            init.unwrapped.await().mapValuesParallel { (k, spec) ->
-                val newEnd = with(frpScope) { eventsByKey[k].skipNext() }
+    ): Pair<Events<Map<K, Maybe<A>>>, DeferredValue<Map<K, B>>> {
+        val eventsByKey: GroupedEvents<K, Maybe<BuildSpec<A>>> = groupByKey(numKeys)
+        val initOut: Lazy<Map<K, B>> = deferAsync {
+            initialSpecs.unwrapped.value.mapValues { (k, spec) ->
+                val newEnd = eventsByKey[k]
                 val newScope = childBuildScope(newEnd)
-                newScope.runInBuildScope(spec)
+                newScope.spec()
             }
         }
         val childScope = coroutineScope.childScope()
-        val changesNode: TFlowImpl<Map<K, Maybe<A>>> =
-            mapImpl(upstream = { this@applyLatestForKeyInternal.init.connect(evalScope = this) }) {
-                upstreamMap ->
+        val changesNode: EventsImpl<Map<K, Maybe<A>>> =
+            mapImpl(upstream = { this@applyLatestSpecForKey.init.connect(evalScope = this) }) {
+                upstreamMap,
+                _ ->
                 reenterBuildScope(this@BuildScopeImpl, childScope).run {
-                    upstreamMap.mapValuesParallel { (k: K, ma: Maybe<FrpSpec<A>>) ->
+                    upstreamMap.mapValues { (k: K, ma: Maybe<BuildSpec<A>>) ->
                         ma.map { spec ->
-                            val newEnd = with(frpScope) { eventsByKey[k].skipNext() }
+                            val newEnd = eventsByKey[k].skipNext()
                             val newScope = childBuildScope(newEnd)
-                            newScope.runInBuildScope(spec)
+                            newScope.spec()
                         }
                     }
                 }
             }
-        val changes: TFlow<Map<K, Maybe<A>>> =
-            TFlowInit(constInit("applyLatestForKey", changesNode.cached()))
+        val changes: Events<Map<K, Maybe<A>>> =
+            EventsInit(constInit("applyLatestForKey", changesNode.cached()))
         // Ensure effects are observed; otherwise init will stay alive longer than expected
-        changes.observeEffectInternal(EmptyCoroutineContext) {}
-        return changes to FrpDeferredValue(initOut)
+        changes.observe()
+        return changes to DeferredValue(initOut)
     }
 
-    private fun newStopEmitter(name: String): CoalescingMutableTFlow<Unit, Unit> =
-        CoalescingMutableTFlow(
+    private fun <A, T : Events<A>, S> buildEvents(
+        name: String? = null,
+        constructEvents: (InputNode<A>) -> Pair<T, S>,
+        builder: suspend S.() -> Unit,
+    ): Events<A> {
+        var job: Job? = null
+        val stopEmitter = newStopEmitter("buildEvents[$name]")
+        // Create a child scope that will be kept alive beyond the end of this transaction.
+        val childScope = coroutineScope.childScope()
+        lateinit var emitter: Pair<T, S>
+        val inputNode =
+            InputNode<A>(
+                activate = {
+                    // It's possible that activation occurs after all effects have been run, due
+                    // to a MuxDeferred switch-in. For this reason, we need to activate in a new
+                    // transaction.
+                    check(job == null) { "[$name] already activated" }
+                    job =
+                        childScope.launchImmediate {
+                            network
+                                .transaction("buildEvents") {
+                                    reenterBuildScope(this@BuildScopeImpl, childScope)
+                                        .launchEffect {
+                                            builder(emitter.second)
+                                            stopEmitter.emit(Unit)
+                                        }
+                                }
+                                .await()
+                                .join()
+                        }
+                },
+                deactivate = {
+                    checkNotNull(job) { "[$name] already deactivated" }.cancel()
+                    job = null
+                },
+            )
+        emitter = constructEvents(inputNode)
+        return emitter.first.takeUntil(mergeLeft(stopEmitter, endSignal))
+    }
+
+    private fun newStopEmitter(name: String): CoalescingMutableEvents<Unit, Unit> =
+        CoalescingMutableEvents(
             name = name,
             coalesce = { _, _: Unit -> },
             network = network,
             getInitialValue = {},
         )
 
-    private suspend fun childBuildScope(newEnd: TFlow<Any>): BuildScopeImpl {
+    private fun childBuildScope(newEnd: Events<Any>): BuildScopeImpl {
         val newCoroutineScope: CoroutineScope = coroutineScope.childScope()
         return BuildScopeImpl(
                 stateScope = stateScope.childStateScope(newEnd),
@@ -292,65 +297,23 @@
                         (newCoroutineScope.coroutineContext.job as CompletableJob).complete()
                     }
                 )
-                runInBuildScope { endSignal.nextOnly().observe { newCoroutineScope.cancel() } }
+                endSignalOnce.observe { newCoroutineScope.cancel() }
             }
     }
 
     private fun mutableChildBuildScope(): BuildScopeImpl {
-        val stopEmitter = newStopEmitter("mutableChildBuildScope")
         val childScope = coroutineScope.childScope()
-        childScope.coroutineContext.job.invokeOnCompletion { stopEmitter.emit(Unit) }
-        // Ensure that once this transaction is done, the new child scope enters the completing
-        // state (kept alive so long as there are child jobs).
-        // TODO: need to keep the scope alive if it's used to accumulate state.
-        //  Otherwise, stopEmitter will emit early, due to the call to complete().
-        //        scheduleOutput(
-        //            OneShot {
-        //                // TODO: don't like this cast
-        //                (childScope.coroutineContext.job as CompletableJob).complete()
-        //            }
-        //        )
+        val stopEmitter = lazy {
+            newStopEmitter("mutableChildBuildScope").apply {
+                childScope.invokeOnCancel { emit(Unit) }
+            }
+        }
         return BuildScopeImpl(
-            stateScope = StateScopeImpl(evalScope = stateScope.evalScope, endSignal = stopEmitter),
+            stateScope =
+                StateScopeImpl(evalScope = stateScope.evalScope, endSignalLazy = stopEmitter),
             coroutineScope = childScope,
         )
     }
-
-    private inner class FrpBuildScopeImpl : FrpBuildScope, FrpStateScope by stateScope.frpScope {
-
-        override fun <T> tFlow(builder: suspend FrpProducerScope<T>.() -> Unit): TFlow<T> =
-            tFlowInternal(builder)
-
-        override fun <In, Out> coalescingTFlow(
-            getInitialValue: () -> Out,
-            coalesce: (old: Out, new: In) -> Out,
-            builder: suspend FrpCoalescingProducerScope<In>.() -> Unit,
-        ): TFlow<Out> = coalescingTFlowInternal(getInitialValue, coalesce, builder)
-
-        override fun <A> asyncScope(block: FrpSpec<A>): Pair<FrpDeferredValue<A>, Job> =
-            asyncScopeInternal(block)
-
-        override fun <R> deferredBuildScope(
-            block: suspend FrpBuildScope.() -> R
-        ): FrpDeferredValue<R> = deferredInternal(block)
-
-        override fun deferredBuildScopeAction(block: suspend FrpBuildScope.() -> Unit) =
-            deferredActionInternal(block)
-
-        override fun <A> TFlow<A>.observe(
-            coroutineContext: CoroutineContext,
-            block: suspend FrpEffectScope.(A) -> Unit,
-        ): Job = observeEffectInternal(coroutineContext, block)
-
-        override fun <A, B> TFlow<A>.mapBuild(transform: suspend FrpBuildScope.(A) -> B): TFlow<B> =
-            mapBuildInternal(transform)
-
-        override fun <K, A, B> TFlow<Map<K, Maybe<FrpSpec<A>>>>.applyLatestSpecForKey(
-            initialSpecs: FrpDeferredValue<Map<K, FrpSpec<B>>>,
-            numKeys: Int?,
-        ): Pair<TFlow<Map<K, Maybe<A>>>, FrpDeferredValue<Map<K, B>>> =
-            applyLatestForKeyInternal(initialSpecs, numKeys)
-    }
 }
 
 private fun EvalScope.reenterBuildScope(
@@ -358,6 +321,7 @@
     coroutineScope: CoroutineScope,
 ) =
     BuildScopeImpl(
-        stateScope = StateScopeImpl(evalScope = this, endSignal = outerScope.endSignal),
+        stateScope =
+            StateScopeImpl(evalScope = this, endSignalLazy = outerScope.stateScope.endSignalLazy),
         coroutineScope,
     )
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/DeferScope.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/DeferScope.kt
index f65307c..d2c154f 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/DeferScope.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/DeferScope.kt
@@ -16,33 +16,51 @@
 
 package com.android.systemui.kairos.internal
 
-import com.android.systemui.kairos.internal.util.asyncImmediate
-import com.android.systemui.kairos.internal.util.launchImmediate
-import kotlinx.coroutines.CoroutineName
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.CoroutineStart
-import kotlinx.coroutines.Deferred
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.coroutineScope
-import kotlinx.coroutines.isActive
+internal interface DeferScope {
+    fun deferAction(block: () -> Unit)
 
-internal typealias DeferScope = CoroutineScope
-
-internal inline fun DeferScope.deferAction(
-    start: CoroutineStart = CoroutineStart.UNDISPATCHED,
-    crossinline block: suspend () -> Unit,
-): Job {
-    check(isActive) { "Cannot perform deferral, scope already closed." }
-    return launchImmediate(start, CoroutineName("deferAction")) { block() }
+    fun <R> deferAsync(block: () -> R): Lazy<R>
 }
 
-internal inline fun <R> DeferScope.deferAsync(
-    start: CoroutineStart = CoroutineStart.UNDISPATCHED,
-    crossinline block: suspend () -> R,
-): Deferred<R> {
-    check(isActive) { "Cannot perform deferral, scope already closed." }
-    return asyncImmediate(start, CoroutineName("deferAsync")) { block() }
+internal inline fun <A> deferScope(block: DeferScope.() -> A): A {
+    val scope =
+        object : DeferScope {
+            val deferrals = ArrayDeque<() -> Unit>() // TODO: store lazies instead?
+
+            fun drainDeferrals() {
+                while (deferrals.isNotEmpty()) {
+                    deferrals.removeFirst().invoke()
+                }
+            }
+
+            override fun deferAction(block: () -> Unit) {
+                deferrals.add(block)
+            }
+
+            override fun <R> deferAsync(block: () -> R): Lazy<R> =
+                lazy(block).also { deferrals.add { it.value } }
+        }
+    return scope.block().also { scope.drainDeferrals() }
 }
 
-internal suspend inline fun <A> deferScope(noinline block: suspend DeferScope.() -> A): A =
-    coroutineScope(block)
+internal object NoValue
+
+internal class CompletableLazy<T>(
+    private var _value: Any? = NoValue,
+    private val name: String? = null,
+) : Lazy<T> {
+
+    fun setValue(value: T) {
+        check(_value === NoValue) { "CompletableLazy value already set" }
+        _value = value
+    }
+
+    override val value: T
+        get() {
+            check(_value !== NoValue) { "CompletableLazy($name) accessed before initialized" }
+            @Suppress("UNCHECKED_CAST")
+            return _value as T
+        }
+
+    override fun isInitialized(): Boolean = _value !== NoValue
+}
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Demux.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Demux.kt
index e7b9952..4cf2458 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Demux.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Demux.kt
@@ -14,270 +14,242 @@
  * limitations under the License.
  */
 
-@file:Suppress("NOTHING_TO_INLINE")
-
 package com.android.systemui.kairos.internal
 
+import com.android.systemui.kairos.internal.store.ConcurrentHashMapK
+import com.android.systemui.kairos.internal.store.MapHolder
+import com.android.systemui.kairos.internal.store.MapK
+import com.android.systemui.kairos.internal.store.MutableMapK
 import com.android.systemui.kairos.internal.util.hashString
-import com.android.systemui.kairos.util.Just
-import com.android.systemui.kairos.util.Maybe
-import com.android.systemui.kairos.util.flatMap
-import com.android.systemui.kairos.util.getMaybe
-import java.util.concurrent.ConcurrentHashMap
-import kotlinx.coroutines.CompletableDeferred
-import kotlinx.coroutines.async
-import kotlinx.coroutines.coroutineScope
-import kotlinx.coroutines.launch
+import com.android.systemui.kairos.internal.util.logDuration
 import kotlinx.coroutines.sync.Mutex
-import kotlinx.coroutines.sync.withLock
 
-internal class DemuxNode<K, A>(
-    private val branchNodeByKey: ConcurrentHashMap<K, DemuxBranchNode<K, A>>,
+internal class DemuxNode<W, K, A>(
+    private val branchNodeByKey: MutableMapK<W, K, DemuxNode<W, K, A>.BranchNode>,
     val lifecycle: DemuxLifecycle<K, A>,
-    private val spec: DemuxActivator<K, A>,
+    private val spec: DemuxActivator<W, K, A>,
 ) : SchedulableNode {
 
     val schedulable = Schedulable.N(this)
 
-    inline val mutex
-        get() = lifecycle.mutex
+    lateinit var upstreamConnection: NodeConnection<MapK<W, K, A>>
 
-    lateinit var upstreamConnection: NodeConnection<Map<K, A>>
+    @Volatile private var epoch: Long = Long.MIN_VALUE
 
-    fun getAndMaybeAddDownstream(key: K): DemuxBranchNode<K, A> =
-        branchNodeByKey.getOrPut(key) { DemuxBranchNode(key, this) }
+    fun hasCurrentValueLocked(logIndent: Int, evalScope: EvalScope, key: K): Boolean =
+        evalScope.epoch == epoch &&
+            upstreamConnection.getPushEvent(logIndent, evalScope).contains(key)
 
-    override suspend fun schedule(evalScope: EvalScope) {
-        val upstreamResult = upstreamConnection.getPushEvent(evalScope)
-        if (upstreamResult is Just) {
-            coroutineScope {
-                val outerScope = this
-                mutex.withLock {
-                    coroutineScope {
-                        for ((key, _) in upstreamResult.value) {
-                            launch {
-                                branchNodeByKey[key]?.let { branch ->
-                                    outerScope.launch { branch.schedule(evalScope) }
-                                }
-                            }
-                        }
-                    }
+    fun hasCurrentValue(logIndent: Int, evalScope: EvalScope, key: K): Boolean =
+        hasCurrentValueLocked(logIndent, evalScope, key)
+
+    fun getAndMaybeAddDownstream(key: K): BranchNode =
+        branchNodeByKey.getOrPut(key) { BranchNode(key) }
+
+    override fun schedule(logIndent: Int, evalScope: EvalScope) =
+        logDuration(logIndent, "DemuxNode.schedule") {
+            val upstreamResult =
+                logDuration("upstream.getPushEvent") {
+                    upstreamConnection.getPushEvent(currentLogIndent, evalScope)
                 }
+            updateEpoch(evalScope)
+            for ((key, _) in upstreamResult) {
+                if (!branchNodeByKey.contains(key)) continue
+                val branch = branchNodeByKey.getValue(key)
+                branch.schedule(currentLogIndent, evalScope)
             }
         }
+
+    override fun adjustDirectUpstream(scheduler: Scheduler, oldDepth: Int, newDepth: Int) {
+        for ((_, branchNode) in branchNodeByKey) {
+            branchNode.downstreamSet.adjustDirectUpstream(scheduler, oldDepth, newDepth)
+        }
     }
 
-    override suspend fun adjustDirectUpstream(scheduler: Scheduler, oldDepth: Int, newDepth: Int) {
-        coroutineScope {
-            mutex.withLock {
-                for ((_, branchNode) in branchNodeByKey) {
-                    branchNode.downstreamSet.adjustDirectUpstream(
-                        coroutineScope = this,
-                        scheduler,
-                        oldDepth,
-                        newDepth,
-                    )
-                }
-            }
-        }
-    }
-
-    override suspend fun moveIndirectUpstreamToDirect(
+    override fun moveIndirectUpstreamToDirect(
         scheduler: Scheduler,
         oldIndirectDepth: Int,
-        oldIndirectSet: Set<MuxDeferredNode<*, *>>,
+        oldIndirectSet: Set<MuxDeferredNode<*, *, *>>,
         newDirectDepth: Int,
     ) {
-        coroutineScope {
-            mutex.withLock {
-                for ((_, branchNode) in branchNodeByKey) {
-                    branchNode.downstreamSet.moveIndirectUpstreamToDirect(
-                        coroutineScope = this,
-                        scheduler,
-                        oldIndirectDepth,
-                        oldIndirectSet,
-                        newDirectDepth,
-                    )
-                }
-            }
+        for ((_, branchNode) in branchNodeByKey) {
+            branchNode.downstreamSet.moveIndirectUpstreamToDirect(
+                scheduler,
+                oldIndirectDepth,
+                oldIndirectSet,
+                newDirectDepth,
+            )
         }
     }
 
-    override suspend fun adjustIndirectUpstream(
+    override fun adjustIndirectUpstream(
         scheduler: Scheduler,
         oldDepth: Int,
         newDepth: Int,
-        removals: Set<MuxDeferredNode<*, *>>,
-        additions: Set<MuxDeferredNode<*, *>>,
+        removals: Set<MuxDeferredNode<*, *, *>>,
+        additions: Set<MuxDeferredNode<*, *, *>>,
     ) {
-        coroutineScope {
-            mutex.withLock {
-                for ((_, branchNode) in branchNodeByKey) {
-                    branchNode.downstreamSet.adjustIndirectUpstream(
-                        coroutineScope = this,
-                        scheduler,
-                        oldDepth,
-                        newDepth,
-                        removals,
-                        additions,
-                    )
-                }
-            }
+        for ((_, branchNode) in branchNodeByKey) {
+            branchNode.downstreamSet.adjustIndirectUpstream(
+                scheduler,
+                oldDepth,
+                newDepth,
+                removals,
+                additions,
+            )
         }
     }
 
-    override suspend fun moveDirectUpstreamToIndirect(
+    override fun moveDirectUpstreamToIndirect(
         scheduler: Scheduler,
         oldDirectDepth: Int,
         newIndirectDepth: Int,
-        newIndirectSet: Set<MuxDeferredNode<*, *>>,
+        newIndirectSet: Set<MuxDeferredNode<*, *, *>>,
     ) {
-        coroutineScope {
-            mutex.withLock {
-                for ((_, branchNode) in branchNodeByKey) {
-                    branchNode.downstreamSet.moveDirectUpstreamToIndirect(
-                        coroutineScope = this,
-                        scheduler,
-                        oldDirectDepth,
-                        newIndirectDepth,
-                        newIndirectSet,
-                    )
-                }
-            }
+        for ((_, branchNode) in branchNodeByKey) {
+            branchNode.downstreamSet.moveDirectUpstreamToIndirect(
+                scheduler,
+                oldDirectDepth,
+                newIndirectDepth,
+                newIndirectSet,
+            )
         }
     }
 
-    override suspend fun removeIndirectUpstream(
+    override fun removeIndirectUpstream(
         scheduler: Scheduler,
         depth: Int,
-        indirectSet: Set<MuxDeferredNode<*, *>>,
+        indirectSet: Set<MuxDeferredNode<*, *, *>>,
     ) {
-        coroutineScope {
-            mutex.withLock {
-                lifecycle.lifecycleState = DemuxLifecycleState.Dead
-                for ((_, branchNode) in branchNodeByKey) {
-                    branchNode.downstreamSet.removeIndirectUpstream(
-                        coroutineScope = this,
-                        scheduler,
-                        depth,
-                        indirectSet,
-                    )
-                }
-            }
+        lifecycle.lifecycleState = DemuxLifecycleState.Dead
+        for ((_, branchNode) in branchNodeByKey) {
+            branchNode.downstreamSet.removeIndirectUpstream(scheduler, depth, indirectSet)
         }
     }
 
-    override suspend fun removeDirectUpstream(scheduler: Scheduler, depth: Int) {
-        coroutineScope {
-            mutex.withLock {
-                lifecycle.lifecycleState = DemuxLifecycleState.Dead
-                for ((_, branchNode) in branchNodeByKey) {
-                    branchNode.downstreamSet.removeDirectUpstream(
-                        coroutineScope = this,
-                        scheduler,
-                        depth,
-                    )
-                }
-            }
+    override fun removeDirectUpstream(scheduler: Scheduler, depth: Int) {
+        lifecycle.lifecycleState = DemuxLifecycleState.Dead
+        for ((_, branchNode) in branchNodeByKey) {
+            branchNode.downstreamSet.removeDirectUpstream(scheduler, depth)
         }
     }
 
-    suspend fun removeDownstreamAndDeactivateIfNeeded(key: K) {
-        val deactivate =
-            mutex.withLock {
-                branchNodeByKey.remove(key)
-                branchNodeByKey.isEmpty()
-            }
+    fun removeDownstreamAndDeactivateIfNeeded(key: K) {
+        branchNodeByKey.remove(key)
+        val deactivate = branchNodeByKey.isEmpty()
         if (deactivate) {
             // No need for mutex here; no more concurrent changes to can occur during this phase
             lifecycle.lifecycleState = DemuxLifecycleState.Inactive(spec)
             upstreamConnection.removeDownstreamAndDeactivateIfNeeded(downstream = schedulable)
         }
     }
-}
 
-internal class DemuxBranchNode<K, A>(val key: K, private val demuxNode: DemuxNode<K, A>) :
-    PushNode<A> {
-
-    private val mutex = Mutex()
-
-    val downstreamSet = DownstreamSet()
-
-    override val depthTracker: DepthTracker
-        get() = demuxNode.upstreamConnection.depthTracker
-
-    override suspend fun hasCurrentValue(transactionStore: TransactionStore): Boolean =
-        demuxNode.upstreamConnection.hasCurrentValue(transactionStore)
-
-    override suspend fun getPushEvent(evalScope: EvalScope): Maybe<A> =
-        demuxNode.upstreamConnection.getPushEvent(evalScope).flatMap { it.getMaybe(key) }
-
-    override suspend fun addDownstream(downstream: Schedulable) {
-        mutex.withLock { downstreamSet.add(downstream) }
+    fun updateEpoch(evalScope: EvalScope) {
+        epoch = evalScope.epoch
     }
 
-    override suspend fun removeDownstream(downstream: Schedulable) {
-        mutex.withLock { downstreamSet.remove(downstream) }
-    }
+    fun getPushEvent(logIndent: Int, evalScope: EvalScope, key: K): A =
+        logDuration(logIndent, "Demux.getPushEvent($key)") {
+            upstreamConnection.getPushEvent(currentLogIndent, evalScope).getValue(key)
+        }
 
-    override suspend fun removeDownstreamAndDeactivateIfNeeded(downstream: Schedulable) {
-        val canDeactivate =
-            mutex.withLock {
-                downstreamSet.remove(downstream)
-                downstreamSet.isEmpty()
+    inner class BranchNode(val key: K) : PushNode<A> {
+
+        val downstreamSet = DownstreamSet()
+
+        override val depthTracker: DepthTracker
+            get() = upstreamConnection.depthTracker
+
+        override fun hasCurrentValue(logIndent: Int, evalScope: EvalScope): Boolean =
+            hasCurrentValue(logIndent, evalScope, key)
+
+        override fun getPushEvent(logIndent: Int, evalScope: EvalScope): A =
+            getPushEvent(logIndent, evalScope, key)
+
+        override fun addDownstream(downstream: Schedulable) {
+            downstreamSet.add(downstream)
+        }
+
+        override fun removeDownstream(downstream: Schedulable) {
+            downstreamSet.remove(downstream)
+        }
+
+        override fun removeDownstreamAndDeactivateIfNeeded(downstream: Schedulable) {
+            downstreamSet.remove(downstream)
+            val canDeactivate = downstreamSet.isEmpty()
+            if (canDeactivate) {
+                removeDownstreamAndDeactivateIfNeeded(key)
             }
-        if (canDeactivate) {
-            demuxNode.removeDownstreamAndDeactivateIfNeeded(key)
         }
-    }
 
-    override suspend fun deactivateIfNeeded() {
-        if (mutex.withLock { downstreamSet.isEmpty() }) {
-            demuxNode.removeDownstreamAndDeactivateIfNeeded(key)
+        override fun deactivateIfNeeded() {
+            if (downstreamSet.isEmpty()) {
+                removeDownstreamAndDeactivateIfNeeded(key)
+            }
         }
-    }
 
-    override suspend fun scheduleDeactivationIfNeeded(evalScope: EvalScope) {
-        if (mutex.withLock { downstreamSet.isEmpty() }) {
-            evalScope.scheduleDeactivation(this)
+        override fun scheduleDeactivationIfNeeded(evalScope: EvalScope) {
+            if (downstreamSet.isEmpty()) {
+                evalScope.scheduleDeactivation(this)
+            }
         }
-    }
 
-    suspend fun schedule(evalScope: EvalScope) {
-        if (!coroutineScope { mutex.withLock { scheduleAll(downstreamSet, evalScope) } }) {
-            evalScope.scheduleDeactivation(this)
+        fun schedule(logIndent: Int, evalScope: EvalScope) {
+            logDuration(logIndent, "DemuxBranchNode($key).schedule") {
+                if (!scheduleAll(currentLogIndent, downstreamSet, evalScope)) {
+                    evalScope.scheduleDeactivation(this@BranchNode)
+                }
+            }
         }
     }
 }
 
-internal fun <K, A> DemuxImpl(
-    upstream: suspend EvalScope.() -> TFlowImpl<Map<K, A>>,
+internal fun <W, K, A> DemuxImpl(
+    upstream: EventsImpl<MapK<W, K, A>>,
     numKeys: Int?,
+    storeFactory: MutableMapK.Factory<W, K>,
 ): DemuxImpl<K, A> =
     DemuxImpl(
         DemuxLifecycle(
-            object : DemuxActivator<K, A> {
-                override suspend fun activate(
-                    evalScope: EvalScope,
-                    lifecycle: DemuxLifecycle<K, A>,
-                ): Pair<DemuxNode<K, A>, Boolean>? {
-                    val dmux = DemuxNode(ConcurrentHashMap(numKeys ?: 16), lifecycle, this)
-                    return upstream
-                        .invoke(evalScope)
-                        .activate(evalScope, downstream = dmux.schedulable)
-                        ?.let { (conn, needsEval) ->
-                            dmux.apply { upstreamConnection = conn } to needsEval
-                        }
-                }
-            }
+            DemuxLifecycleState.Inactive(DemuxActivator(numKeys, upstream, storeFactory))
         )
     )
 
+internal fun <K, A> demuxMap(
+    upstream: EvalScope.() -> EventsImpl<Map<K, A>>,
+    numKeys: Int?,
+): DemuxImpl<K, A> =
+    DemuxImpl(mapImpl(upstream) { it, _ -> MapHolder(it) }, numKeys, ConcurrentHashMapK.Factory())
+
+internal class DemuxActivator<W, K, A>(
+    private val numKeys: Int?,
+    private val upstream: EventsImpl<MapK<W, K, A>>,
+    private val storeFactory: MutableMapK.Factory<W, K>,
+) {
+    fun activate(
+        evalScope: EvalScope,
+        lifecycle: DemuxLifecycle<K, A>,
+    ): Pair<DemuxNode<W, K, A>, Set<K>>? {
+        val demux = DemuxNode(storeFactory.create(numKeys), lifecycle, this)
+        return upstream.activate(evalScope, demux.schedulable)?.let { (conn, needsEval) ->
+            Pair(
+                demux.apply { upstreamConnection = conn },
+                if (needsEval) {
+                    demux.updateEpoch(evalScope)
+                    conn.getPushEvent(0, evalScope).keys
+                } else {
+                    emptySet()
+                },
+            )
+        }
+    }
+}
+
 internal class DemuxImpl<in K, out A>(private val dmux: DemuxLifecycle<K, A>) {
-    fun eventsForKey(key: K): TFlowImpl<A> = TFlowCheap { downstream ->
+    fun eventsForKey(key: K): EventsImpl<A> = EventsImplCheap { downstream ->
         dmux.activate(evalScope = this, key)?.let { (branchNode, needsEval) ->
             branchNode.addDownstream(downstream)
-            val branchNeedsEval = needsEval && branchNode.getPushEvent(evalScope = this) is Just
+            val branchNeedsEval = needsEval && branchNode.hasCurrentValue(0, evalScope = this)
             ActivationResult(
                 connection = NodeConnection(branchNode, branchNode),
                 needsEval = branchNeedsEval,
@@ -289,61 +261,45 @@
 internal class DemuxLifecycle<K, A>(@Volatile var lifecycleState: DemuxLifecycleState<K, A>) {
     val mutex = Mutex()
 
-    override fun toString(): String = "TFlowDmuxState[$hashString][$lifecycleState][$mutex]"
+    override fun toString(): String = "EventsDmuxState[$hashString][$lifecycleState][$mutex]"
 
-    suspend fun activate(evalScope: EvalScope, key: K): Pair<DemuxBranchNode<K, A>, Boolean>? =
-        coroutineScope {
-            mutex
-                .withLock {
-                    when (val state = lifecycleState) {
-                        is DemuxLifecycleState.Dead -> null
-                        is DemuxLifecycleState.Active ->
-                            state.node.getAndMaybeAddDownstream(key) to
-                                async {
-                                    state.node.upstreamConnection.hasCurrentValue(
-                                        evalScope.transactionStore
-                                    )
-                                }
-                        is DemuxLifecycleState.Inactive -> {
-                            state.spec
-                                .activate(evalScope, this@DemuxLifecycle)
-                                .also { result ->
-                                    lifecycleState =
-                                        if (result == null) {
-                                            DemuxLifecycleState.Dead
-                                        } else {
-                                            DemuxLifecycleState.Active(result.first)
-                                        }
-                                }
-                                ?.let { (node, needsEval) ->
-                                    node.getAndMaybeAddDownstream(key) to
-                                        CompletableDeferred(needsEval)
-                                }
-                        }
+    fun activate(evalScope: EvalScope, key: K): Pair<DemuxNode<*, K, A>.BranchNode, Boolean>? =
+        when (val state = lifecycleState) {
+            is DemuxLifecycleState.Dead -> {
+                null
+            }
+
+            is DemuxLifecycleState.Active -> {
+                state.node.getAndMaybeAddDownstream(key) to
+                    state.node.hasCurrentValueLocked(0, evalScope, key)
+            }
+
+            is DemuxLifecycleState.Inactive -> {
+                state.spec
+                    .activate(evalScope, this@DemuxLifecycle)
+                    .also { result ->
+                        lifecycleState =
+                            if (result == null) {
+                                DemuxLifecycleState.Dead
+                            } else {
+                                DemuxLifecycleState.Active(result.first)
+                            }
                     }
-                }
-                ?.let { (branch, result) -> branch to result.await() }
+                    ?.let { (node, needsEval) ->
+                        node.getAndMaybeAddDownstream(key) to (key in needsEval)
+                    }
+            }
         }
 }
 
 internal sealed interface DemuxLifecycleState<out K, out A> {
-    class Inactive<K, A>(val spec: DemuxActivator<K, A>) : DemuxLifecycleState<K, A> {
+    class Inactive<K, A>(val spec: DemuxActivator<*, K, A>) : DemuxLifecycleState<K, A> {
         override fun toString(): String = "Inactive"
     }
 
-    class Active<K, A>(val node: DemuxNode<K, A>) : DemuxLifecycleState<K, A> {
+    class Active<K, A>(val node: DemuxNode<*, K, A>) : DemuxLifecycleState<K, A> {
         override fun toString(): String = "Active(node=$node)"
     }
 
     data object Dead : DemuxLifecycleState<Nothing, Nothing>
 }
-
-internal interface DemuxActivator<K, A> {
-    suspend fun activate(
-        evalScope: EvalScope,
-        lifecycle: DemuxLifecycle<K, A>,
-    ): Pair<DemuxNode<K, A>, Boolean>?
-}
-
-internal inline fun <K, A> DemuxLifecycle(onSubscribe: DemuxActivator<K, A>) =
-    DemuxLifecycle(DemuxLifecycleState.Inactive(onSubscribe))
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/EvalScopeImpl.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/EvalScopeImpl.kt
index 815473f..80a2948 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/EvalScopeImpl.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/EvalScopeImpl.kt
@@ -16,57 +16,54 @@
 
 package com.android.systemui.kairos.internal
 
-import com.android.systemui.kairos.FrpDeferredValue
-import com.android.systemui.kairos.FrpTransactionScope
-import com.android.systemui.kairos.TFlow
-import com.android.systemui.kairos.TFlowInit
-import com.android.systemui.kairos.TFlowLoop
-import com.android.systemui.kairos.TState
-import com.android.systemui.kairos.TStateInit
+import com.android.systemui.kairos.DeferredValue
+import com.android.systemui.kairos.Events
+import com.android.systemui.kairos.EventsInit
+import com.android.systemui.kairos.EventsLoop
+import com.android.systemui.kairos.State
+import com.android.systemui.kairos.StateInit
+import com.android.systemui.kairos.TransactionScope
 import com.android.systemui.kairos.Transactional
-import com.android.systemui.kairos.emptyTFlow
+import com.android.systemui.kairos.emptyEvents
 import com.android.systemui.kairos.init
 import com.android.systemui.kairos.mapCheap
-import com.android.systemui.kairos.switch
-import kotlin.coroutines.Continuation
-import kotlin.coroutines.CoroutineContext
-import kotlin.coroutines.EmptyCoroutineContext
-import kotlin.coroutines.startCoroutine
-import kotlinx.coroutines.CompletableDeferred
-import kotlinx.coroutines.completeWith
-import kotlinx.coroutines.job
+import com.android.systemui.kairos.switchEvents
 
 internal class EvalScopeImpl(networkScope: NetworkScope, deferScope: DeferScope) :
-    EvalScope, NetworkScope by networkScope, DeferScope by deferScope {
+    EvalScope, NetworkScope by networkScope, DeferScope by deferScope, TransactionScope {
 
-    private suspend fun <A> Transactional<A>.sample(): A =
-        impl.sample().sample(this@EvalScopeImpl).await()
+    override fun <A> Transactional<A>.sampleDeferred(): DeferredValue<A> =
+        DeferredValue(deferAsync { impl.sample().sample(this@EvalScopeImpl).value })
 
-    private suspend fun <A> TState<A>.sample(): A =
-        init.connect(evalScope = this@EvalScopeImpl).getCurrentWithEpoch(this@EvalScopeImpl).first
+    override fun <A> State<A>.sampleDeferred(): DeferredValue<A> =
+        DeferredValue(
+            deferAsync {
+                init
+                    .connect(evalScope = this@EvalScopeImpl)
+                    .getCurrentWithEpoch(this@EvalScopeImpl)
+                    .first
+            }
+        )
 
-    private val <A> Transactional<A>.deferredValue: FrpDeferredValue<A>
-        get() = FrpDeferredValue(deferAsync { sample() })
+    override fun <R> deferredTransactionScope(block: TransactionScope.() -> R): DeferredValue<R> =
+        DeferredValue(deferAsync { block() })
 
-    private val <A> TState<A>.deferredValue: FrpDeferredValue<A>
-        get() = FrpDeferredValue(deferAsync { sample() })
-
-    private val nowInternal: TFlow<Unit> by lazy {
-        var result by TFlowLoop<Unit>()
+    override val now: Events<Unit> by lazy {
+        var result by EventsLoop<Unit>()
         result =
-            TStateInit(
+            StateInit(
                     constInit(
                         "now",
-                        mkState(
+                        activatedStateSource(
                             "now",
                             "now",
                             this,
-                            { result.mapCheap { emptyTFlow }.init.connect(evalScope = this) },
-                            CompletableDeferred(
-                                TFlowInit(
+                            { result.mapCheap { emptyEvents }.init.connect(evalScope = this) },
+                            CompletableLazy(
+                                EventsInit(
                                     constInit(
                                         "now",
-                                        TFlowCheap {
+                                        EventsImplCheap {
                                             ActivationResult(
                                                 connection = NodeConnection(AlwaysNode, AlwaysNode),
                                                 needsEval = true,
@@ -78,42 +75,7 @@
                         ),
                     )
                 )
-                .switch()
+                .switchEvents()
         result
     }
-
-    private fun <R> deferredInternal(
-        block: suspend FrpTransactionScope.() -> R
-    ): FrpDeferredValue<R> = FrpDeferredValue(deferAsync { runInTransactionScope(block) })
-
-    override suspend fun <R> runInTransactionScope(block: suspend FrpTransactionScope.() -> R): R {
-        val complete = CompletableDeferred<R>(parent = coroutineContext.job)
-        block.startCoroutine(
-            frpScope,
-            object : Continuation<R> {
-                override val context: CoroutineContext
-                    get() = EmptyCoroutineContext
-
-                override fun resumeWith(result: Result<R>) {
-                    complete.completeWith(result)
-                }
-            },
-        )
-        return complete.await()
-    }
-
-    override val frpScope: FrpTransactionScope = FrpTransactionScopeImpl()
-
-    inner class FrpTransactionScopeImpl : FrpTransactionScope {
-        override fun <A> Transactional<A>.sampleDeferred(): FrpDeferredValue<A> = deferredValue
-
-        override fun <A> TState<A>.sampleDeferred(): FrpDeferredValue<A> = deferredValue
-
-        override fun <R> deferredTransactionScope(
-            block: suspend FrpTransactionScope.() -> R
-        ): FrpDeferredValue<R> = deferredInternal(block)
-
-        override val now: TFlow<Unit>
-            get() = nowInternal
-    }
 }
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/EventsImpl.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/EventsImpl.kt
new file mode 100644
index 0000000..59c5e72
--- /dev/null
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/EventsImpl.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.kairos.internal
+
+/* Initialized Events */
+internal fun interface EventsImpl<out A> {
+    fun activate(evalScope: EvalScope, downstream: Schedulable): ActivationResult<A>?
+}
+
+internal data class ActivationResult<out A>(
+    val connection: NodeConnection<A>,
+    val needsEval: Boolean,
+)
+
+internal inline fun <A> EventsImplCheap(crossinline cheap: CheapNodeSubscribe<A>) =
+    EventsImpl { scope, ds ->
+        scope.cheap(ds)
+    }
+
+internal typealias CheapNodeSubscribe<A> =
+    EvalScope.(downstream: Schedulable) -> ActivationResult<A>?
+
+internal data class NodeConnection<out A>(
+    val directUpstream: PullNode<A>,
+    val schedulerUpstream: PushNode<*>,
+)
+
+internal fun <A> NodeConnection<A>.hasCurrentValue(logIndent: Int, evalScope: EvalScope): Boolean =
+    schedulerUpstream.hasCurrentValue(logIndent, evalScope)
+
+internal fun <A> NodeConnection<A>.removeDownstreamAndDeactivateIfNeeded(downstream: Schedulable) =
+    schedulerUpstream.removeDownstreamAndDeactivateIfNeeded(downstream)
+
+internal fun <A> NodeConnection<A>.scheduleDeactivationIfNeeded(evalScope: EvalScope) =
+    schedulerUpstream.scheduleDeactivationIfNeeded(evalScope)
+
+internal fun <A> NodeConnection<A>.removeDownstream(downstream: Schedulable) =
+    schedulerUpstream.removeDownstream(downstream)
+
+internal fun <A> NodeConnection<A>.getPushEvent(logIndent: Int, evalScope: EvalScope): A =
+    directUpstream.getPushEvent(logIndent, evalScope)
+
+internal val <A> NodeConnection<A>.depthTracker: DepthTracker
+    get() = schedulerUpstream.depthTracker
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/FilterNode.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/FilterNode.kt
index bc06a36..f86e761 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/FilterNode.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/FilterNode.kt
@@ -16,32 +16,32 @@
 
 package com.android.systemui.kairos.internal
 
-import com.android.systemui.kairos.util.Just
+import com.android.systemui.kairos.internal.store.Single
+import com.android.systemui.kairos.internal.store.SingletonMapK
 import com.android.systemui.kairos.util.Maybe
-import com.android.systemui.kairos.util.just
-import com.android.systemui.kairos.util.none
+import com.android.systemui.kairos.util.Maybe.Present
 
-internal inline fun <A, B> mapMaybeNode(
-    crossinline getPulse: suspend EvalScope.() -> TFlowImpl<A>,
-    crossinline f: suspend EvalScope.(A) -> Maybe<B>,
-): TFlowImpl<B> {
-    return DemuxImpl(
-            {
-                mapImpl(getPulse) {
-                    val maybeResult = f(it)
-                    if (maybeResult is Just) {
-                        mapOf(Unit to maybeResult.value)
-                    } else {
-                        emptyMap()
-                    }
+internal inline fun <A> filterPresentImpl(
+    crossinline getPulse: EvalScope.() -> EventsImpl<Maybe<A>>
+): EventsImpl<A> =
+    DemuxImpl(
+            mapImpl(getPulse) { maybeResult, _ ->
+                if (maybeResult is Present) {
+                    Single(maybeResult.value)
+                } else {
+                    Single<A>()
                 }
             },
             numKeys = 1,
+            storeFactory = SingletonMapK.Factory(),
         )
         .eventsForKey(Unit)
-}
 
-internal inline fun <A> filterNode(
-    crossinline getPulse: suspend EvalScope.() -> TFlowImpl<A>,
-    crossinline f: suspend EvalScope.(A) -> Boolean,
-): TFlowImpl<A> = mapMaybeNode(getPulse) { if (f(it)) just(it) else none }
+internal inline fun <A> filterImpl(
+    crossinline getPulse: EvalScope.() -> EventsImpl<A>,
+    crossinline f: EvalScope.(A) -> Boolean,
+): EventsImpl<A> {
+    val mapped =
+        mapImpl(getPulse) { it, _ -> if (f(it)) Maybe.present(it) else Maybe.absent }.cached()
+    return filterPresentImpl { mapped }
+}
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Graph.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Graph.kt
index 04ce5b6..b956e44 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Graph.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Graph.kt
@@ -18,8 +18,6 @@
 
 import com.android.systemui.kairos.internal.util.Bag
 import java.util.TreeMap
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.launch
 
 /**
  * Tracks all upstream connections for Mux nodes.
@@ -72,15 +70,15 @@
     @Volatile var snapshotIndirectDepth: Int = 0
     @Volatile var snapshotDirectDepth: Int = 0
 
-    private val _snapshotIndirectRoots = HashSet<MuxDeferredNode<*, *>>()
+    private val _snapshotIndirectRoots = HashSet<MuxDeferredNode<*, *, *>>()
     val snapshotIndirectRoots
         get() = _snapshotIndirectRoots.toSet()
 
-    private val indirectAdditions = HashSet<MuxDeferredNode<*, *>>()
-    private val indirectRemovals = HashSet<MuxDeferredNode<*, *>>()
+    private val indirectAdditions = HashSet<MuxDeferredNode<*, *, *>>()
+    private val indirectRemovals = HashSet<MuxDeferredNode<*, *, *>>()
     private val dirty_directUpstreamDepths = TreeMap<Int, Int>()
     private val dirty_indirectUpstreamDepths = TreeMap<Int, Int>()
-    private val dirty_indirectUpstreamRoots = Bag<MuxDeferredNode<*, *>>()
+    private val dirty_indirectUpstreamRoots = Bag<MuxDeferredNode<*, *, *>>()
     @Volatile var dirty_directDepth = 0
     @Volatile private var dirty_indirectDepth = 0
     @Volatile private var dirty_depthIsDirect = true
@@ -161,9 +159,9 @@
     }
 
     fun updateIndirectRoots(
-        additions: Set<MuxDeferredNode<*, *>>? = null,
-        removals: Set<MuxDeferredNode<*, *>>? = null,
-        butNot: MuxDeferredNode<*, *>? = null,
+        additions: Set<MuxDeferredNode<*, *, *>>? = null,
+        removals: Set<MuxDeferredNode<*, *, *>>? = null,
+        butNot: MuxDeferredNode<*, *, *>? = null,
     ): Boolean {
         val addsChanged =
             additions
@@ -192,14 +190,13 @@
         return remainder
     }
 
-    suspend fun propagateChanges(scheduler: Scheduler, muxNode: MuxNode<*, *, *>) {
+    fun propagateChanges(scheduler: Scheduler, muxNode: MuxNode<*, *, *>) {
         if (isDirty()) {
             schedule(scheduler, muxNode)
         }
     }
 
     fun applyChanges(
-        coroutineScope: CoroutineScope,
         scheduler: Scheduler,
         downstreamSet: DownstreamSet,
         muxNode: MuxNode<*, *, *>,
@@ -208,21 +205,19 @@
             dirty_depthIsDirect -> {
                 if (snapshotIsDirect) {
                     downstreamSet.adjustDirectUpstream(
-                        coroutineScope,
                         scheduler,
                         oldDepth = snapshotDirectDepth,
                         newDepth = dirty_directDepth,
                     )
                 } else {
                     downstreamSet.moveIndirectUpstreamToDirect(
-                        coroutineScope,
                         scheduler,
                         oldIndirectDepth = snapshotIndirectDepth,
                         oldIndirectSet =
                             buildSet {
                                 addAll(snapshotIndirectRoots)
                                 if (snapshotIsIndirectRoot) {
-                                    add(muxNode as MuxDeferredNode<*, *>)
+                                    add(muxNode as MuxDeferredNode<*, *, *>)
                                 }
                             },
                         newDirectDepth = dirty_directDepth,
@@ -233,7 +228,6 @@
             dirty_hasIndirectUpstream() || dirty_isIndirectRoot -> {
                 if (snapshotIsDirect) {
                     downstreamSet.moveDirectUpstreamToIndirect(
-                        coroutineScope,
                         scheduler,
                         oldDirectDepth = snapshotDirectDepth,
                         newIndirectDepth = dirty_indirectDepth,
@@ -241,13 +235,12 @@
                             buildSet {
                                 addAll(dirty_indirectUpstreamRoots)
                                 if (dirty_isIndirectRoot) {
-                                    add(muxNode as MuxDeferredNode<*, *>)
+                                    add(muxNode as MuxDeferredNode<*, *, *>)
                                 }
                             },
                     )
                 } else {
                     downstreamSet.adjustIndirectUpstream(
-                        coroutineScope,
                         scheduler,
                         oldDepth = snapshotIndirectDepth,
                         newDepth = dirty_indirectDepth,
@@ -255,14 +248,14 @@
                             buildSet {
                                 addAll(indirectRemovals)
                                 if (snapshotIsIndirectRoot && !dirty_isIndirectRoot) {
-                                    add(muxNode as MuxDeferredNode<*, *>)
+                                    add(muxNode as MuxDeferredNode<*, *, *>)
                                 }
                             },
                         additions =
                             buildSet {
                                 addAll(indirectAdditions)
                                 if (!snapshotIsIndirectRoot && dirty_isIndirectRoot) {
-                                    add(muxNode as MuxDeferredNode<*, *>)
+                                    add(muxNode as MuxDeferredNode<*, *, *>)
                                 }
                             },
                     )
@@ -274,21 +267,16 @@
                 muxNode.lifecycle.lifecycleState = MuxLifecycleState.Dead
 
                 if (snapshotIsDirect) {
-                    downstreamSet.removeDirectUpstream(
-                        coroutineScope,
-                        scheduler,
-                        depth = snapshotDirectDepth,
-                    )
+                    downstreamSet.removeDirectUpstream(scheduler, depth = snapshotDirectDepth)
                 } else {
                     downstreamSet.removeIndirectUpstream(
-                        coroutineScope,
                         scheduler,
                         depth = snapshotIndirectDepth,
                         indirectSet =
                             buildSet {
                                 addAll(snapshotIndirectRoots)
                                 if (snapshotIsIndirectRoot) {
-                                    add(muxNode as MuxDeferredNode<*, *>)
+                                    add(muxNode as MuxDeferredNode<*, *, *>)
                                 }
                             },
                     )
@@ -352,8 +340,8 @@
 internal class DownstreamSet {
 
     val outputs = HashSet<Output<*>>()
-    val stateWriters = mutableListOf<TStateSource<*>>()
-    val muxMovers = HashSet<MuxDeferredNode<*, *>>()
+    val stateWriters = mutableListOf<StateSource<*>>()
+    val muxMovers = HashSet<MuxDeferredNode<*, *, *>>()
     val nodes = HashSet<SchedulableNode>()
 
     fun add(schedulable: Schedulable) {
@@ -374,125 +362,92 @@
         }
     }
 
-    fun adjustDirectUpstream(
-        coroutineScope: CoroutineScope,
-        scheduler: Scheduler,
-        oldDepth: Int,
-        newDepth: Int,
-    ) =
-        coroutineScope.run {
-            for (node in nodes) {
-                launch { node.adjustDirectUpstream(scheduler, oldDepth, newDepth) }
-            }
+    fun adjustDirectUpstream(scheduler: Scheduler, oldDepth: Int, newDepth: Int) {
+        for (node in nodes) {
+            node.adjustDirectUpstream(scheduler, oldDepth, newDepth)
         }
+    }
 
     fun moveIndirectUpstreamToDirect(
-        coroutineScope: CoroutineScope,
         scheduler: Scheduler,
         oldIndirectDepth: Int,
-        oldIndirectSet: Set<MuxDeferredNode<*, *>>,
+        oldIndirectSet: Set<MuxDeferredNode<*, *, *>>,
         newDirectDepth: Int,
-    ) =
-        coroutineScope.run {
-            for (node in nodes) {
-                launch {
-                    node.moveIndirectUpstreamToDirect(
-                        scheduler,
-                        oldIndirectDepth,
-                        oldIndirectSet,
-                        newDirectDepth,
-                    )
-                }
-            }
-            for (mover in muxMovers) {
-                launch {
-                    mover.moveIndirectPatchNodeToDirect(scheduler, oldIndirectDepth, oldIndirectSet)
-                }
-            }
+    ) {
+        for (node in nodes) {
+            node.moveIndirectUpstreamToDirect(
+                scheduler,
+                oldIndirectDepth,
+                oldIndirectSet,
+                newDirectDepth,
+            )
         }
+        for (mover in muxMovers) {
+            mover.moveIndirectPatchNodeToDirect(scheduler, oldIndirectDepth, oldIndirectSet)
+        }
+    }
 
     fun adjustIndirectUpstream(
-        coroutineScope: CoroutineScope,
         scheduler: Scheduler,
         oldDepth: Int,
         newDepth: Int,
-        removals: Set<MuxDeferredNode<*, *>>,
-        additions: Set<MuxDeferredNode<*, *>>,
-    ) =
-        coroutineScope.run {
-            for (node in nodes) {
-                launch {
-                    node.adjustIndirectUpstream(scheduler, oldDepth, newDepth, removals, additions)
-                }
-            }
-            for (mover in muxMovers) {
-                launch {
-                    mover.adjustIndirectPatchNode(
-                        scheduler,
-                        oldDepth,
-                        newDepth,
-                        removals,
-                        additions,
-                    )
-                }
-            }
+        removals: Set<MuxDeferredNode<*, *, *>>,
+        additions: Set<MuxDeferredNode<*, *, *>>,
+    ) {
+        for (node in nodes) {
+            node.adjustIndirectUpstream(scheduler, oldDepth, newDepth, removals, additions)
         }
+        for (mover in muxMovers) {
+            mover.adjustIndirectPatchNode(scheduler, oldDepth, newDepth, removals, additions)
+        }
+    }
 
     fun moveDirectUpstreamToIndirect(
-        coroutineScope: CoroutineScope,
         scheduler: Scheduler,
         oldDirectDepth: Int,
         newIndirectDepth: Int,
-        newIndirectSet: Set<MuxDeferredNode<*, *>>,
-    ) =
-        coroutineScope.run {
-            for (node in nodes) {
-                launch {
-                    node.moveDirectUpstreamToIndirect(
-                        scheduler,
-                        oldDirectDepth,
-                        newIndirectDepth,
-                        newIndirectSet,
-                    )
-                }
-            }
-            for (mover in muxMovers) {
-                launch {
-                    mover.moveDirectPatchNodeToIndirect(scheduler, newIndirectDepth, newIndirectSet)
-                }
-            }
+        newIndirectSet: Set<MuxDeferredNode<*, *, *>>,
+    ) {
+        for (node in nodes) {
+            node.moveDirectUpstreamToIndirect(
+                scheduler,
+                oldDirectDepth,
+                newIndirectDepth,
+                newIndirectSet,
+            )
         }
+        for (mover in muxMovers) {
+            mover.moveDirectPatchNodeToIndirect(scheduler, newIndirectDepth, newIndirectSet)
+        }
+    }
 
     fun removeIndirectUpstream(
-        coroutineScope: CoroutineScope,
         scheduler: Scheduler,
         depth: Int,
-        indirectSet: Set<MuxDeferredNode<*, *>>,
-    ) =
-        coroutineScope.run {
-            for (node in nodes) {
-                launch { node.removeIndirectUpstream(scheduler, depth, indirectSet) }
-            }
-            for (mover in muxMovers) {
-                launch { mover.removeIndirectPatchNode(scheduler, depth, indirectSet) }
-            }
-            for (output in outputs) {
-                launch { output.kill() }
-            }
+        indirectSet: Set<MuxDeferredNode<*, *, *>>,
+    ) {
+        for (node in nodes) {
+            node.removeIndirectUpstream(scheduler, depth, indirectSet)
         }
+        for (mover in muxMovers) {
+            mover.removeIndirectPatchNode(scheduler, depth, indirectSet)
+        }
+        for (output in outputs) {
+            output.kill()
+        }
+    }
 
-    fun removeDirectUpstream(coroutineScope: CoroutineScope, scheduler: Scheduler, depth: Int) =
-        coroutineScope.run {
-            for (node in nodes) {
-                launch { node.removeDirectUpstream(scheduler, depth) }
-            }
-            for (mover in muxMovers) {
-                launch { mover.removeDirectPatchNode(scheduler) }
-            }
-            for (output in outputs) {
-                launch { output.kill() }
-            }
+    fun removeDirectUpstream(scheduler: Scheduler, depth: Int) {
+        for (node in nodes) {
+            node.removeDirectUpstream(scheduler, depth)
         }
+        for (mover in muxMovers) {
+            mover.removeDirectPatchNode(scheduler)
+        }
+        for (output in outputs) {
+            output.kill()
+        }
+    }
 
     fun clear() {
         outputs.clear()
@@ -504,9 +459,9 @@
 
 // TODO: remove this indirection
 internal sealed interface Schedulable {
-    data class S constructor(val state: TStateSource<*>) : Schedulable
+    data class S constructor(val state: StateSource<*>) : Schedulable
 
-    data class M constructor(val muxMover: MuxDeferredNode<*, *>) : Schedulable
+    data class M constructor(val muxMover: MuxDeferredNode<*, *, *>) : Schedulable
 
     data class N constructor(val node: SchedulableNode) : Schedulable
 
@@ -518,13 +473,14 @@
 
 @Suppress("NOTHING_TO_INLINE") internal inline fun DownstreamSet.isNotEmpty() = !isEmpty()
 
-internal fun CoroutineScope.scheduleAll(
+internal fun scheduleAll(
+    logIndent: Int,
     downstreamSet: DownstreamSet,
     evalScope: EvalScope,
 ): Boolean {
-    downstreamSet.nodes.forEach { launch { it.schedule(evalScope) } }
-    downstreamSet.muxMovers.forEach { launch { it.scheduleMover(evalScope) } }
-    downstreamSet.outputs.forEach { launch { it.schedule(evalScope) } }
+    downstreamSet.nodes.forEach { it.schedule(logIndent, evalScope) }
+    downstreamSet.muxMovers.forEach { it.scheduleMover(logIndent, evalScope) }
+    downstreamSet.outputs.forEach { it.schedule(logIndent, evalScope) }
     downstreamSet.stateWriters.forEach { evalScope.schedule(it) }
     return downstreamSet.isNotEmpty()
 }
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/IncrementalImpl.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/IncrementalImpl.kt
new file mode 100644
index 0000000..9b4778a
--- /dev/null
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/IncrementalImpl.kt
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.kairos.internal
+
+import com.android.systemui.kairos.internal.store.StoreEntry
+import com.android.systemui.kairos.util.MapPatch
+import com.android.systemui.kairos.util.Maybe
+import com.android.systemui.kairos.util.map
+import com.android.systemui.kairos.util.toMaybe
+
+internal class IncrementalImpl<K, out V>(
+    name: String?,
+    operatorName: String,
+    changes: EventsImpl<Map<K, V>>,
+    val patches: EventsImpl<Map<K, Maybe<V>>>,
+    store: StateStore<Map<K, V>>,
+) : StateImpl<Map<K, V>>(name, operatorName, changes, store)
+
+internal fun <K, V> constIncremental(
+    name: String?,
+    operatorName: String,
+    init: Map<K, V>,
+): IncrementalImpl<K, V> =
+    IncrementalImpl(name, operatorName, neverImpl, neverImpl, StateSource(init))
+
+internal inline fun <K, V> activatedIncremental(
+    name: String?,
+    operatorName: String,
+    evalScope: EvalScope,
+    crossinline getPatches: EvalScope.() -> EventsImpl<Map<K, Maybe<V>>>,
+    init: Lazy<Map<K, V>>,
+): IncrementalImpl<K, V> {
+    val store = StateSource(init)
+    val maybeChanges =
+        mapImpl(getPatches) { patch, _ ->
+                val (current, _) = store.getCurrentWithEpoch(evalScope = this)
+                current.applyPatchCalm(patch).toMaybe()
+            }
+            .cached()
+    val calm = filterPresentImpl { maybeChanges }
+    val changes = mapImpl({ calm }) { (_, change), _ -> change }
+    val patches = mapImpl({ calm }) { (patch, _), _ -> patch }
+    evalScope.scheduleOutput(
+        OneShot {
+            changes.activate(evalScope = this, downstream = Schedulable.S(store))?.let {
+                (connection, needsEval) ->
+                store.upstreamConnection = connection
+                if (needsEval) {
+                    schedule(store)
+                }
+            }
+        }
+    )
+    return IncrementalImpl(name, operatorName, changes, patches, store)
+}
+
+private fun <K, V> Map<K, V>.applyPatchCalm(
+    patch: MapPatch<K, V>
+): Pair<MapPatch<K, V>, Map<K, V>>? {
+    val current = this
+    val filteredPatch = mutableMapOf<K, Maybe<V>>()
+    val new = current.toMutableMap()
+    for ((key, change) in patch) {
+        when (change) {
+            is Maybe.Present -> {
+                if (key !in current || current.getValue(key) != change.value) {
+                    filteredPatch[key] = change
+                    new[key] = change.value
+                }
+            }
+            Maybe.Absent -> {
+                if (key in current) {
+                    filteredPatch[key] = change
+                    new.remove(key)
+                }
+            }
+        }
+    }
+    return if (filteredPatch.isNotEmpty()) filteredPatch to new else null
+}
+
+internal inline fun <K, V> EventsImpl<Map<K, Maybe<V>>>.calmUpdates(
+    state: StateDerived<Map<K, V>>
+): Pair<EventsImpl<Map<K, Maybe<V>>>, EventsImpl<Map<K, V>>> {
+    val maybeUpdate =
+        mapImpl({ this@calmUpdates }) { patch, _ ->
+                val (current, _) = state.getCurrentWithEpoch(evalScope = this)
+                current
+                    .applyPatchCalm(patch)
+                    ?.also { (_, newMap) -> state.setCacheFromPush(newMap, epoch) }
+                    .toMaybe()
+            }
+            .cached()
+    val calm = filterPresentImpl { maybeUpdate }
+    val patches = mapImpl({ calm }) { (p, _), _ -> p }
+    val changes = mapImpl({ calm }) { (_, s), _ -> s }
+    return patches to changes
+}
+
+internal fun <K, A, B> mapValuesImpl(
+    incrementalImpl: InitScope.() -> IncrementalImpl<K, A>,
+    name: String?,
+    operatorName: String,
+    transform: EvalScope.(Map.Entry<K, A>) -> B,
+): IncrementalImpl<K, B> {
+    val store = DerivedMap(incrementalImpl) { map -> map.mapValues { transform(it) } }
+    val mappedPatches =
+        mapImpl({ incrementalImpl().patches }) { patch, _ ->
+                patch.mapValues { (k, mv) -> mv.map { v -> transform(StoreEntry(k, v)) } }
+            }
+            .cached()
+    val (calmPatches, calmChanges) = mappedPatches.calmUpdates(store)
+    return IncrementalImpl(name, operatorName, calmChanges, calmPatches, store)
+}
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Init.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Init.kt
index 57db9a4..4fa1070 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Init.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Init.kt
@@ -17,44 +17,40 @@
 package com.android.systemui.kairos.internal
 
 import com.android.systemui.kairos.util.Maybe
-import com.android.systemui.kairos.util.just
-import com.android.systemui.kairos.util.none
-import java.util.concurrent.atomic.AtomicBoolean
-import kotlinx.coroutines.CompletableDeferred
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 
 /** Performs actions once, when the reactive component is first connected to the network. */
-internal class Init<out A>(val name: String?, private val block: suspend InitScope.() -> A) {
-
-    /** Has the initialization logic been evaluated yet? */
-    private val initialized = AtomicBoolean()
+internal class Init<out A>(val name: String?, private val block: InitScope.() -> A) {
 
     /**
      * Stores the result after initialization, as well as the id of the [Network] it's been
      * initialized with.
      */
-    private val cache = CompletableDeferred<Pair<Any, A>>()
+    private val cache = CompletableLazy<Pair<Any, A>>()
 
-    suspend fun connect(evalScope: InitScope): A =
-        if (initialized.getAndSet(true)) {
+    fun connect(evalScope: InitScope): A =
+        if (cache.isInitialized()) {
             // Read from cache
-            val (networkId, result) = cache.await()
+            val (networkId, result) = cache.value
             check(networkId == evalScope.networkId) { "Network mismatch" }
             result
         } else {
             // Write to cache
-            block(evalScope).also { cache.complete(evalScope.networkId to it) }
+            block(evalScope).also { cache.setValue(evalScope.networkId to it) }
         }
 
     @OptIn(ExperimentalCoroutinesApi::class)
     fun getUnsafe(): Maybe<A> =
-        if (cache.isCompleted) {
-            just(cache.getCompleted().second)
+        if (cache.isInitialized()) {
+            Maybe.present(cache.value.second)
         } else {
-            none
+            Maybe.absent
         }
 }
 
-internal fun <A> init(name: String?, block: suspend InitScope.() -> A) = Init(name, block)
+@Suppress("NOTHING_TO_INLINE")
+internal inline fun <A> init(name: String?, noinline block: InitScope.() -> A): Init<A> =
+    Init(name, block)
 
-internal fun <A> constInit(name: String?, value: A) = init(name) { value }
+@Suppress("NOTHING_TO_INLINE")
+internal inline fun <A> constInit(name: String?, value: A): Init<A> = init(name) { value }
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Inputs.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Inputs.kt
index 8efaf79..d7cbe80 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Inputs.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Inputs.kt
@@ -16,105 +16,103 @@
 
 package com.android.systemui.kairos.internal
 
-import com.android.systemui.kairos.internal.util.Key
-import com.android.systemui.kairos.util.Maybe
-import com.android.systemui.kairos.util.just
+import com.android.systemui.kairos.internal.util.logDuration
 import java.util.concurrent.atomic.AtomicBoolean
-import kotlinx.coroutines.coroutineScope
-import kotlinx.coroutines.sync.Mutex
-import kotlinx.coroutines.sync.withLock
 
 internal class InputNode<A>(
-    private val activate: suspend EvalScope.() -> Unit = {},
+    private val activate: EvalScope.() -> Unit = {},
     private val deactivate: () -> Unit = {},
-) : PushNode<A>, Key<A> {
+) : PushNode<A> {
 
-    internal val downstreamSet = DownstreamSet()
-    private val mutex = Mutex()
-    private val activated = AtomicBoolean(false)
+    private val downstreamSet = DownstreamSet()
+    val activated = AtomicBoolean(false)
+
+    private val transactionCache = TransactionCache<A>()
+    private val epoch
+        get() = transactionCache.epoch
 
     override val depthTracker: DepthTracker = DepthTracker()
 
-    override suspend fun hasCurrentValue(transactionStore: TransactionStore): Boolean =
-        transactionStore.contains(this)
+    override fun hasCurrentValue(logIndent: Int, evalScope: EvalScope): Boolean =
+        epoch == evalScope.epoch
 
-    suspend fun visit(evalScope: EvalScope, value: A) {
-        evalScope.setResult(this, value)
-        coroutineScope {
-            if (!mutex.withLock { scheduleAll(downstreamSet, evalScope) }) {
-                evalScope.scheduleDeactivation(this@InputNode)
-            }
+    fun visit(evalScope: EvalScope, value: A) {
+        transactionCache.put(evalScope, value)
+        if (!scheduleAll(0, downstreamSet, evalScope)) {
+            evalScope.scheduleDeactivation(this@InputNode)
         }
     }
 
-    override suspend fun removeDownstream(downstream: Schedulable) {
-        mutex.withLock { downstreamSet.remove(downstream) }
+    override fun removeDownstream(downstream: Schedulable) {
+        downstreamSet.remove(downstream)
     }
 
-    override suspend fun deactivateIfNeeded() {
-        if (mutex.withLock { downstreamSet.isEmpty() && activated.getAndSet(false) }) {
+    override fun deactivateIfNeeded() {
+        if (downstreamSet.isEmpty() && activated.getAndSet(false)) {
             deactivate()
         }
     }
 
-    override suspend fun scheduleDeactivationIfNeeded(evalScope: EvalScope) {
-        if (mutex.withLock { downstreamSet.isEmpty() }) {
+    override fun scheduleDeactivationIfNeeded(evalScope: EvalScope) {
+        if (downstreamSet.isEmpty()) {
             evalScope.scheduleDeactivation(this)
         }
     }
 
-    override suspend fun addDownstream(downstream: Schedulable) {
-        mutex.withLock { downstreamSet.add(downstream) }
+    override fun addDownstream(downstream: Schedulable) {
+        downstreamSet.add(downstream)
     }
 
-    suspend fun addDownstreamAndActivateIfNeeded(downstream: Schedulable, evalScope: EvalScope) {
-        val needsActivation =
-            mutex.withLock {
-                val wasEmpty = downstreamSet.isEmpty()
-                downstreamSet.add(downstream)
-                wasEmpty && !activated.getAndSet(true)
-            }
+    fun addDownstreamAndActivateIfNeeded(downstream: Schedulable, evalScope: EvalScope) {
+        val needsActivation = run {
+            val wasEmpty = downstreamSet.isEmpty()
+            downstreamSet.add(downstream)
+            wasEmpty && !activated.getAndSet(true)
+        }
         if (needsActivation) {
             activate(evalScope)
         }
     }
 
-    override suspend fun removeDownstreamAndDeactivateIfNeeded(downstream: Schedulable) {
-        val needsDeactivation =
-            mutex.withLock {
-                downstreamSet.remove(downstream)
-                downstreamSet.isEmpty() && activated.getAndSet(false)
-            }
+    override fun removeDownstreamAndDeactivateIfNeeded(downstream: Schedulable) {
+        downstreamSet.remove(downstream)
+        val needsDeactivation = downstreamSet.isEmpty() && activated.getAndSet(false)
         if (needsDeactivation) {
             deactivate()
         }
     }
 
-    override suspend fun getPushEvent(evalScope: EvalScope): Maybe<A> =
-        evalScope.getCurrentValue(this)
+    override fun getPushEvent(logIndent: Int, evalScope: EvalScope): A =
+        logDuration(logIndent, "Input.getPushEvent", false) {
+            transactionCache.getCurrentValue(evalScope)
+        }
 }
 
-internal fun <A> InputNode<A>.activated() = TFlowCheap { downstream ->
+internal fun <A> InputNode<A>.activated() = EventsImplCheap { downstream ->
     val input = this@activated
     addDownstreamAndActivateIfNeeded(downstream, evalScope = this)
-    ActivationResult(connection = NodeConnection(input, input), needsEval = hasCurrentValue(input))
+    ActivationResult(
+        connection = NodeConnection(input, input),
+        needsEval = input.hasCurrentValue(0, evalScope = this),
+    )
 }
 
 internal data object AlwaysNode : PushNode<Unit> {
 
     override val depthTracker = DepthTracker()
 
-    override suspend fun hasCurrentValue(transactionStore: TransactionStore): Boolean = true
+    override fun hasCurrentValue(logIndent: Int, evalScope: EvalScope): Boolean = true
 
-    override suspend fun removeDownstream(downstream: Schedulable) {}
+    override fun removeDownstream(downstream: Schedulable) {}
 
-    override suspend fun deactivateIfNeeded() {}
+    override fun deactivateIfNeeded() {}
 
-    override suspend fun scheduleDeactivationIfNeeded(evalScope: EvalScope) {}
+    override fun scheduleDeactivationIfNeeded(evalScope: EvalScope) {}
 
-    override suspend fun addDownstream(downstream: Schedulable) {}
+    override fun addDownstream(downstream: Schedulable) {}
 
-    override suspend fun removeDownstreamAndDeactivateIfNeeded(downstream: Schedulable) {}
+    override fun removeDownstreamAndDeactivateIfNeeded(downstream: Schedulable) {}
 
-    override suspend fun getPushEvent(evalScope: EvalScope): Maybe<Unit> = just(Unit)
+    override fun getPushEvent(logIndent: Int, evalScope: EvalScope) =
+        logDuration(logIndent, "Always.getPushEvent", false) { Unit }
 }
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/InternalScopes.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/InternalScopes.kt
index 69994ba..cd22143 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/InternalScopes.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/InternalScopes.kt
@@ -16,39 +16,25 @@
 
 package com.android.systemui.kairos.internal
 
-import com.android.systemui.kairos.FrpBuildScope
-import com.android.systemui.kairos.FrpStateScope
-import com.android.systemui.kairos.FrpTransactionScope
-import com.android.systemui.kairos.TFlow
-import com.android.systemui.kairos.internal.util.HeteroMap
-import com.android.systemui.kairos.internal.util.Key
-import com.android.systemui.kairos.util.Maybe
+import com.android.systemui.kairos.BuildScope
+import com.android.systemui.kairos.Events
+import com.android.systemui.kairos.StateScope
+import com.android.systemui.kairos.TransactionScope
 
 internal interface InitScope {
     val networkId: Any
 }
 
-internal interface EvalScope : NetworkScope, DeferScope {
-    val frpScope: FrpTransactionScope
+internal interface EvalScope : NetworkScope, DeferScope, TransactionScope
 
-    suspend fun <R> runInTransactionScope(block: suspend FrpTransactionScope.() -> R): R
+internal interface InternalStateScope : EvalScope, StateScope {
+    val endSignal: Events<Any>
+    val endSignalOnce: Events<Any>
+
+    fun childStateScope(newEnd: Events<Any>): InternalStateScope
 }
 
-internal interface StateScope : EvalScope {
-    override val frpScope: FrpStateScope
-
-    suspend fun <R> runInStateScope(block: suspend FrpStateScope.() -> R): R
-
-    val endSignal: TFlow<Any>
-
-    fun childStateScope(newEnd: TFlow<Any>): StateScope
-}
-
-internal interface BuildScope : StateScope {
-    override val frpScope: FrpBuildScope
-
-    suspend fun <R> runInBuildScope(block: suspend FrpBuildScope.() -> R): R
-}
+internal interface InternalBuildScope : InternalStateScope, BuildScope
 
 internal interface NetworkScope : InitScope {
 
@@ -58,23 +44,15 @@
     val compactor: Scheduler
     val scheduler: Scheduler
 
-    val transactionStore: HeteroMap
+    val transactionStore: TransactionStore
 
     fun scheduleOutput(output: Output<*>)
 
-    fun scheduleMuxMover(muxMover: MuxDeferredNode<*, *>)
+    fun scheduleMuxMover(muxMover: MuxDeferredNode<*, *, *>)
 
-    fun schedule(state: TStateSource<*>)
+    fun schedule(state: StateSource<*>)
 
     fun scheduleDeactivation(node: PushNode<*>)
 
     fun scheduleDeactivation(output: Output<*>)
 }
-
-internal fun <A> NetworkScope.setResult(node: Key<A>, result: A) {
-    transactionStore[node] = result
-}
-
-internal fun <A> NetworkScope.getCurrentValue(key: Key<A>): Maybe<A> = transactionStore[key]
-
-internal fun NetworkScope.hasCurrentValue(key: Key<*>): Boolean = transactionStore.contains(key)
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Mux.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Mux.kt
index af68a1e..268fec4 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Mux.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Mux.kt
@@ -18,31 +18,43 @@
 
 package com.android.systemui.kairos.internal
 
-import com.android.systemui.kairos.internal.util.ConcurrentNullableHashMap
+import com.android.systemui.kairos.internal.store.MapHolder
+import com.android.systemui.kairos.internal.store.MapK
+import com.android.systemui.kairos.internal.store.MutableMapK
+import com.android.systemui.kairos.internal.store.asMapHolder
 import com.android.systemui.kairos.internal.util.hashString
-import com.android.systemui.kairos.util.Just
-import java.util.concurrent.ConcurrentHashMap
-import kotlinx.coroutines.coroutineScope
-import kotlinx.coroutines.sync.Mutex
-import kotlinx.coroutines.sync.withLock
+import com.android.systemui.kairos.internal.util.logDuration
 
-/** Base class for muxing nodes, which have a potentially dynamic collection of upstream nodes. */
-internal sealed class MuxNode<K : Any, V, Output>(val lifecycle: MuxLifecycle<Output>) :
-    PushNode<Output> {
+internal typealias MuxResult<W, K, V> = MapK<W, K, PullNode<V>>
 
-    inline val mutex
-        get() = lifecycle.mutex
+/** Base class for muxing nodes, which have a (potentially dynamic) collection of upstream nodes. */
+internal sealed class MuxNode<W, K, V>(
+    val lifecycle: MuxLifecycle<W, K, V>,
+    protected val storeFactory: MutableMapK.Factory<W, K>,
+) : PushNode<MuxResult<W, K, V>> {
 
-    // TODO: preserve insertion order?
-    val upstreamData = ConcurrentNullableHashMap<K, V>()
-    val switchedIn = ConcurrentHashMap<K, MuxBranchNode<K, V>>()
+    lateinit var upstreamData: MutableMapK<W, K, PullNode<V>>
+    lateinit var switchedIn: MutableMapK<W, K, BranchNode>
+
+    @Volatile var markedForCompaction = false
+    @Volatile var markedForEvaluation = false
+
     val downstreamSet: DownstreamSet = DownstreamSet()
 
     // TODO: inline DepthTracker? would need to be added to PushNode signature
     final override val depthTracker = DepthTracker()
 
-    final override suspend fun addDownstream(downstream: Schedulable) {
-        mutex.withLock { addDownstreamLocked(downstream) }
+    val transactionCache = TransactionCache<MuxResult<W, K, V>>()
+    val epoch
+        get() = transactionCache.epoch
+
+    inline fun hasCurrentValueLocked(evalScope: EvalScope): Boolean = epoch == evalScope.epoch
+
+    override fun hasCurrentValue(logIndent: Int, evalScope: EvalScope): Boolean =
+        hasCurrentValueLocked(evalScope)
+
+    final override fun addDownstream(downstream: Schedulable) {
+        addDownstreamLocked(downstream)
     }
 
     /**
@@ -55,140 +67,124 @@
         downstreamSet.add(downstream)
     }
 
-    final override suspend fun removeDownstream(downstream: Schedulable) {
+    final override fun removeDownstream(downstream: Schedulable) {
         // TODO: return boolean?
-        mutex.withLock { downstreamSet.remove(downstream) }
+        downstreamSet.remove(downstream)
     }
 
-    final override suspend fun removeDownstreamAndDeactivateIfNeeded(downstream: Schedulable) {
-        val deactivate =
-            mutex.withLock {
-                downstreamSet.remove(downstream)
-                downstreamSet.isEmpty()
-            }
+    final override fun removeDownstreamAndDeactivateIfNeeded(downstream: Schedulable) {
+        downstreamSet.remove(downstream)
+        val deactivate = downstreamSet.isEmpty()
         if (deactivate) {
             doDeactivate()
         }
     }
 
-    final override suspend fun deactivateIfNeeded() {
-        if (mutex.withLock { downstreamSet.isEmpty() }) {
+    final override fun deactivateIfNeeded() {
+        if (downstreamSet.isEmpty()) {
             doDeactivate()
         }
     }
 
     /** visit this node from the scheduler (push eval) */
-    abstract suspend fun visit(evalScope: EvalScope)
+    abstract fun visit(logIndent: Int, evalScope: EvalScope)
 
     /** perform deactivation logic, propagating to all upstream nodes. */
-    protected abstract suspend fun doDeactivate()
+    protected abstract fun doDeactivate()
 
-    final override suspend fun scheduleDeactivationIfNeeded(evalScope: EvalScope) {
-        if (mutex.withLock { downstreamSet.isEmpty() }) {
+    final override fun scheduleDeactivationIfNeeded(evalScope: EvalScope) {
+        if (downstreamSet.isEmpty()) {
             evalScope.scheduleDeactivation(this)
         }
     }
 
-    suspend fun adjustDirectUpstream(scheduler: Scheduler, oldDepth: Int, newDepth: Int) {
-        mutex.withLock {
-            if (depthTracker.addDirectUpstream(oldDepth, newDepth)) {
-                depthTracker.schedule(scheduler, this)
-            }
+    fun adjustDirectUpstream(scheduler: Scheduler, oldDepth: Int, newDepth: Int) {
+
+        if (depthTracker.addDirectUpstream(oldDepth, newDepth)) {
+            depthTracker.schedule(scheduler, this)
         }
     }
 
-    suspend fun moveIndirectUpstreamToDirect(
+    fun moveIndirectUpstreamToDirect(
         scheduler: Scheduler,
         oldIndirectDepth: Int,
-        oldIndirectRoots: Set<MuxDeferredNode<*, *>>,
+        oldIndirectRoots: Set<MuxDeferredNode<*, *, *>>,
         newDepth: Int,
     ) {
-        mutex.withLock {
-            if (
-                depthTracker.addDirectUpstream(oldDepth = null, newDepth) or
-                    depthTracker.removeIndirectUpstream(depth = oldIndirectDepth) or
-                    depthTracker.updateIndirectRoots(removals = oldIndirectRoots)
-            ) {
-                depthTracker.schedule(scheduler, this)
-            }
+        if (
+            depthTracker.addDirectUpstream(oldDepth = null, newDepth) or
+                depthTracker.removeIndirectUpstream(depth = oldIndirectDepth) or
+                depthTracker.updateIndirectRoots(removals = oldIndirectRoots)
+        ) {
+            depthTracker.schedule(scheduler, this)
         }
     }
 
-    suspend fun adjustIndirectUpstream(
+    fun adjustIndirectUpstream(
         scheduler: Scheduler,
         oldDepth: Int,
         newDepth: Int,
-        removals: Set<MuxDeferredNode<*, *>>,
-        additions: Set<MuxDeferredNode<*, *>>,
+        removals: Set<MuxDeferredNode<*, *, *>>,
+        additions: Set<MuxDeferredNode<*, *, *>>,
     ) {
-        mutex.withLock {
-            if (
-                depthTracker.addIndirectUpstream(oldDepth, newDepth) or
-                    depthTracker.updateIndirectRoots(
-                        additions,
-                        removals,
-                        butNot = this as? MuxDeferredNode<*, *>,
-                    )
-            ) {
-                depthTracker.schedule(scheduler, this)
-            }
+        if (
+            depthTracker.addIndirectUpstream(oldDepth, newDepth) or
+                depthTracker.updateIndirectRoots(
+                    additions,
+                    removals,
+                    butNot = this as? MuxDeferredNode<*, *, *>,
+                )
+        ) {
+            depthTracker.schedule(scheduler, this)
         }
     }
 
-    suspend fun moveDirectUpstreamToIndirect(
+    fun moveDirectUpstreamToIndirect(
         scheduler: Scheduler,
         oldDepth: Int,
         newDepth: Int,
-        newIndirectSet: Set<MuxDeferredNode<*, *>>,
+        newIndirectSet: Set<MuxDeferredNode<*, *, *>>,
     ) {
-        mutex.withLock {
-            if (
-                depthTracker.addIndirectUpstream(oldDepth = null, newDepth) or
-                    depthTracker.removeDirectUpstream(oldDepth) or
-                    depthTracker.updateIndirectRoots(
-                        additions = newIndirectSet,
-                        butNot = this as? MuxDeferredNode<*, *>,
-                    )
-            ) {
-                depthTracker.schedule(scheduler, this)
-            }
+        if (
+            depthTracker.addIndirectUpstream(oldDepth = null, newDepth) or
+                depthTracker.removeDirectUpstream(oldDepth) or
+                depthTracker.updateIndirectRoots(
+                    additions = newIndirectSet,
+                    butNot = this as? MuxDeferredNode<*, *, *>,
+                )
+        ) {
+            depthTracker.schedule(scheduler, this)
         }
     }
 
-    suspend fun removeDirectUpstream(scheduler: Scheduler, depth: Int, key: K) {
-        mutex.withLock {
-            switchedIn.remove(key)
-            if (depthTracker.removeDirectUpstream(depth)) {
-                depthTracker.schedule(scheduler, this)
-            }
+    fun removeDirectUpstream(scheduler: Scheduler, depth: Int, key: K) {
+        switchedIn.remove(key)
+        if (depthTracker.removeDirectUpstream(depth)) {
+            depthTracker.schedule(scheduler, this)
         }
     }
 
-    suspend fun removeIndirectUpstream(
+    fun removeIndirectUpstream(
         scheduler: Scheduler,
         oldDepth: Int,
-        indirectSet: Set<MuxDeferredNode<*, *>>,
+        indirectSet: Set<MuxDeferredNode<*, *, *>>,
         key: K,
     ) {
-        mutex.withLock {
-            switchedIn.remove(key)
-            if (
-                depthTracker.removeIndirectUpstream(oldDepth) or
-                    depthTracker.updateIndirectRoots(removals = indirectSet)
-            ) {
-                depthTracker.schedule(scheduler, this)
-            }
+        switchedIn.remove(key)
+        if (
+            depthTracker.removeIndirectUpstream(oldDepth) or
+                depthTracker.updateIndirectRoots(removals = indirectSet)
+        ) {
+            depthTracker.schedule(scheduler, this)
         }
     }
 
-    suspend fun visitCompact(scheduler: Scheduler) = coroutineScope {
+    fun visitCompact(scheduler: Scheduler) {
         if (depthTracker.isDirty()) {
-            depthTracker.applyChanges(coroutineScope = this, scheduler, downstreamSet, this@MuxNode)
+            depthTracker.applyChanges(scheduler, downstreamSet, this@MuxNode)
         }
     }
 
-    abstract fun hasCurrentValueLocked(transactionStore: TransactionStore): Boolean
-
     fun schedule(evalScope: EvalScope) {
         // TODO: Potential optimization
         //  Detect if this node is guaranteed to have a single upstream within this transaction,
@@ -196,139 +192,202 @@
         //  MuxNode as a Pull (effectively making it a mapCheap).
         depthTracker.schedule(evalScope.scheduler, this)
     }
-}
 
-/** An input branch of a mux node, associated with a key. */
-internal class MuxBranchNode<K : Any, V>(private val muxNode: MuxNode<K, V, *>, val key: K) :
-    SchedulableNode {
+    /** An input branch of a mux node, associated with a key. */
+    inner class BranchNode(val key: K) : SchedulableNode {
 
-    val schedulable = Schedulable.N(this)
+        val schedulable = Schedulable.N(this)
 
-    @Volatile lateinit var upstream: NodeConnection<V>
+        lateinit var upstream: NodeConnection<V>
 
-    override suspend fun schedule(evalScope: EvalScope) {
-        val upstreamResult = upstream.getPushEvent(evalScope)
-        if (upstreamResult is Just) {
-            muxNode.upstreamData[key] = upstreamResult.value
-            muxNode.schedule(evalScope)
+        override fun schedule(logIndent: Int, evalScope: EvalScope) {
+            logDuration(logIndent, "MuxBranchNode.schedule") {
+                if (this@MuxNode is MuxPromptNode && this@MuxNode.name != null) {
+                    logLn("[${this@MuxNode}] scheduling $key")
+                }
+                upstreamData[key] = upstream.directUpstream
+                this@MuxNode.schedule(evalScope)
+            }
         }
-    }
 
-    override suspend fun adjustDirectUpstream(scheduler: Scheduler, oldDepth: Int, newDepth: Int) {
-        muxNode.adjustDirectUpstream(scheduler, oldDepth, newDepth)
-    }
+        override fun adjustDirectUpstream(scheduler: Scheduler, oldDepth: Int, newDepth: Int) {
+            this@MuxNode.adjustDirectUpstream(scheduler, oldDepth, newDepth)
+        }
 
-    override suspend fun moveIndirectUpstreamToDirect(
-        scheduler: Scheduler,
-        oldIndirectDepth: Int,
-        oldIndirectSet: Set<MuxDeferredNode<*, *>>,
-        newDirectDepth: Int,
-    ) {
-        muxNode.moveIndirectUpstreamToDirect(
-            scheduler,
-            oldIndirectDepth,
-            oldIndirectSet,
-            newDirectDepth,
-        )
-    }
+        override fun moveIndirectUpstreamToDirect(
+            scheduler: Scheduler,
+            oldIndirectDepth: Int,
+            oldIndirectSet: Set<MuxDeferredNode<*, *, *>>,
+            newDirectDepth: Int,
+        ) {
+            this@MuxNode.moveIndirectUpstreamToDirect(
+                scheduler,
+                oldIndirectDepth,
+                oldIndirectSet,
+                newDirectDepth,
+            )
+        }
 
-    override suspend fun adjustIndirectUpstream(
-        scheduler: Scheduler,
-        oldDepth: Int,
-        newDepth: Int,
-        removals: Set<MuxDeferredNode<*, *>>,
-        additions: Set<MuxDeferredNode<*, *>>,
-    ) {
-        muxNode.adjustIndirectUpstream(scheduler, oldDepth, newDepth, removals, additions)
-    }
+        override fun adjustIndirectUpstream(
+            scheduler: Scheduler,
+            oldDepth: Int,
+            newDepth: Int,
+            removals: Set<MuxDeferredNode<*, *, *>>,
+            additions: Set<MuxDeferredNode<*, *, *>>,
+        ) {
+            this@MuxNode.adjustIndirectUpstream(scheduler, oldDepth, newDepth, removals, additions)
+        }
 
-    override suspend fun moveDirectUpstreamToIndirect(
-        scheduler: Scheduler,
-        oldDirectDepth: Int,
-        newIndirectDepth: Int,
-        newIndirectSet: Set<MuxDeferredNode<*, *>>,
-    ) {
-        muxNode.moveDirectUpstreamToIndirect(
-            scheduler,
-            oldDirectDepth,
-            newIndirectDepth,
-            newIndirectSet,
-        )
-    }
+        override fun moveDirectUpstreamToIndirect(
+            scheduler: Scheduler,
+            oldDirectDepth: Int,
+            newIndirectDepth: Int,
+            newIndirectSet: Set<MuxDeferredNode<*, *, *>>,
+        ) {
+            this@MuxNode.moveDirectUpstreamToIndirect(
+                scheduler,
+                oldDirectDepth,
+                newIndirectDepth,
+                newIndirectSet,
+            )
+        }
 
-    override suspend fun removeDirectUpstream(scheduler: Scheduler, depth: Int) {
-        muxNode.removeDirectUpstream(scheduler, depth, key)
-    }
+        override fun removeDirectUpstream(scheduler: Scheduler, depth: Int) {
+            removeDirectUpstream(scheduler, depth, key)
+        }
 
-    override suspend fun removeIndirectUpstream(
-        scheduler: Scheduler,
-        depth: Int,
-        indirectSet: Set<MuxDeferredNode<*, *>>,
-    ) {
-        muxNode.removeIndirectUpstream(scheduler, depth, indirectSet, key)
-    }
+        override fun removeIndirectUpstream(
+            scheduler: Scheduler,
+            depth: Int,
+            indirectSet: Set<MuxDeferredNode<*, *, *>>,
+        ) {
+            removeIndirectUpstream(scheduler, depth, indirectSet, key)
+        }
 
-    override fun toString(): String = "MuxBranchNode(key=$key, mux=$muxNode)"
+        override fun toString(): String = "MuxBranchNode(key=$key, mux=${this@MuxNode})"
+    }
 }
 
+internal typealias BranchNode<W, K, V> = MuxNode<W, K, V>.BranchNode
+
 /** Tracks lifecycle of MuxNode in the network. Essentially a mutable ref for MuxLifecycleState. */
-internal class MuxLifecycle<A>(@Volatile var lifecycleState: MuxLifecycleState<A>) : TFlowImpl<A> {
-    val mutex = Mutex()
+internal class MuxLifecycle<W, K, V>(var lifecycleState: MuxLifecycleState<W, K, V>) :
+    EventsImpl<MuxResult<W, K, V>> {
 
-    override fun toString(): String = "TFlowLifecycle[$hashString][$lifecycleState][$mutex]"
+    override fun toString(): String = "MuxLifecycle[$hashString][$lifecycleState]"
 
-    override suspend fun activate(
+    override fun activate(
         evalScope: EvalScope,
         downstream: Schedulable,
-    ): ActivationResult<A>? =
-        mutex.withLock {
-            when (val state = lifecycleState) {
-                is MuxLifecycleState.Dead -> null
-                is MuxLifecycleState.Active -> {
-                    state.node.addDownstreamLocked(downstream)
-                    ActivationResult(
-                        connection = NodeConnection(state.node, state.node),
-                        needsEval = state.node.hasCurrentValueLocked(evalScope.transactionStore),
-                    )
-                }
-                is MuxLifecycleState.Inactive -> {
-                    state.spec
-                        .activate(evalScope, this@MuxLifecycle)
-                        .also { node ->
-                            lifecycleState =
-                                if (node == null) {
-                                    MuxLifecycleState.Dead
-                                } else {
-                                    MuxLifecycleState.Active(node)
-                                }
-                        }
-                        ?.let { node ->
-                            node.addDownstreamLocked(downstream)
-                            ActivationResult(
-                                connection = NodeConnection(node, node),
-                                needsEval = false,
-                            )
-                        }
-                }
+    ): ActivationResult<MuxResult<W, K, V>>? =
+        when (val state = lifecycleState) {
+            is MuxLifecycleState.Dead -> {
+                null
+            }
+            is MuxLifecycleState.Active -> {
+                state.node.addDownstreamLocked(downstream)
+                ActivationResult(
+                    connection = NodeConnection(state.node, state.node),
+                    needsEval = state.node.hasCurrentValueLocked(evalScope),
+                )
+            }
+            is MuxLifecycleState.Inactive -> {
+                state.spec
+                    .activate(evalScope, this@MuxLifecycle)
+                    .also { node ->
+                        lifecycleState =
+                            if (node == null) {
+                                MuxLifecycleState.Dead
+                            } else {
+                                MuxLifecycleState.Active(node.first)
+                            }
+                    }
+                    ?.let { (node, postActivate) ->
+                        postActivate?.invoke()
+                        node.addDownstreamLocked(downstream)
+                        ActivationResult(connection = NodeConnection(node, node), needsEval = false)
+                    }
             }
         }
 }
 
-internal sealed interface MuxLifecycleState<out A> {
-    class Inactive<A>(val spec: MuxActivator<A>) : MuxLifecycleState<A> {
+internal sealed interface MuxLifecycleState<out W, out K, out V> {
+    class Inactive<W, K, V>(val spec: MuxActivator<W, K, V>) : MuxLifecycleState<W, K, V> {
         override fun toString(): String = "Inactive"
     }
 
-    class Active<A>(val node: MuxNode<*, *, A>) : MuxLifecycleState<A> {
+    class Active<W, K, V>(val node: MuxNode<W, K, V>) : MuxLifecycleState<W, K, V> {
         override fun toString(): String = "Active(node=$node)"
     }
 
-    data object Dead : MuxLifecycleState<Nothing>
+    data object Dead : MuxLifecycleState<Nothing, Nothing, Nothing>
 }
 
-internal interface MuxActivator<A> {
-    suspend fun activate(evalScope: EvalScope, lifecycle: MuxLifecycle<A>): MuxNode<*, *, A>?
+internal interface MuxActivator<W, K, V> {
+    fun activate(
+        evalScope: EvalScope,
+        lifecycle: MuxLifecycle<W, K, V>,
+    ): Pair<MuxNode<W, K, V>, (() -> Unit)?>?
 }
 
-internal inline fun <A> MuxLifecycle(onSubscribe: MuxActivator<A>): TFlowImpl<A> =
-    MuxLifecycle(MuxLifecycleState.Inactive(onSubscribe))
+internal inline fun <W, K, V> MuxLifecycle(
+    onSubscribe: MuxActivator<W, K, V>
+): EventsImpl<MuxResult<W, K, V>> = MuxLifecycle(MuxLifecycleState.Inactive(onSubscribe))
+
+internal fun <K, V> EventsImpl<MuxResult<MapHolder.W, K, V>>.awaitValues(): EventsImpl<Map<K, V>> =
+    mapImpl({ this@awaitValues }) { results, logIndent ->
+        results.asMapHolder().unwrapped.mapValues { it.value.getPushEvent(logIndent, this) }
+    }
+
+// activation logic
+
+internal fun <W, K, V> MuxNode<W, K, V>.initializeUpstream(
+    evalScope: EvalScope,
+    getStorage: EvalScope.() -> Iterable<Map.Entry<K, EventsImpl<V>>>,
+    storeFactory: MutableMapK.Factory<W, K>,
+) {
+    val storage = getStorage(evalScope)
+    val initUpstream = buildList {
+        storage.forEach { (key, events) ->
+            val branchNode = BranchNode(key)
+            add(
+                events.activate(evalScope, branchNode.schedulable)?.let { (conn, needsEval) ->
+                    Triple(
+                        key,
+                        branchNode.apply { upstream = conn },
+                        if (needsEval) conn.directUpstream else null,
+                    )
+                }
+            )
+        }
+    }
+    switchedIn = storeFactory.create(initUpstream.size)
+    upstreamData = storeFactory.create(initUpstream.size)
+    for (triple in initUpstream) {
+        triple?.let { (key, branch, upstream) ->
+            switchedIn[key] = branch
+            upstream?.let { upstreamData[key] = upstream }
+        }
+    }
+}
+
+internal fun <W, K, V> MuxNode<W, K, V>.initializeDepth() {
+    switchedIn.forEach { (_, branch) ->
+        val conn = branch.upstream
+        if (conn.depthTracker.snapshotIsDirect) {
+            depthTracker.addDirectUpstream(
+                oldDepth = null,
+                newDepth = conn.depthTracker.snapshotDirectDepth,
+            )
+        } else {
+            depthTracker.addIndirectUpstream(
+                oldDepth = null,
+                newDepth = conn.depthTracker.snapshotIndirectDepth,
+            )
+            depthTracker.updateIndirectRoots(
+                additions = conn.depthTracker.snapshotIndirectRoots,
+                butNot = null,
+            )
+        }
+    }
+}
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/MuxDeferred.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/MuxDeferred.kt
index 3b9502a..c11eb12 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/MuxDeferred.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/MuxDeferred.kt
@@ -16,66 +16,71 @@
 
 package com.android.systemui.kairos.internal
 
-import com.android.systemui.kairos.internal.util.Key
-import com.android.systemui.kairos.internal.util.associateByIndexTo
+import com.android.systemui.kairos.internal.store.MapK
+import com.android.systemui.kairos.internal.store.MutableArrayMapK
+import com.android.systemui.kairos.internal.store.MutableMapK
+import com.android.systemui.kairos.internal.store.SingletonMapK
+import com.android.systemui.kairos.internal.store.StoreEntry
+import com.android.systemui.kairos.internal.store.asArrayHolder
+import com.android.systemui.kairos.internal.store.asSingle
+import com.android.systemui.kairos.internal.store.singleOf
 import com.android.systemui.kairos.internal.util.hashString
-import com.android.systemui.kairos.internal.util.mapParallel
-import com.android.systemui.kairos.internal.util.mapValuesNotNullParallelTo
-import com.android.systemui.kairos.util.Just
-import com.android.systemui.kairos.util.Left
+import com.android.systemui.kairos.internal.util.logDuration
+import com.android.systemui.kairos.internal.util.logLn
 import com.android.systemui.kairos.util.Maybe
-import com.android.systemui.kairos.util.None
-import com.android.systemui.kairos.util.Right
+import com.android.systemui.kairos.util.Maybe.Absent
+import com.android.systemui.kairos.util.Maybe.Present
 import com.android.systemui.kairos.util.These
 import com.android.systemui.kairos.util.flatMap
 import com.android.systemui.kairos.util.getMaybe
-import com.android.systemui.kairos.util.just
-import com.android.systemui.kairos.util.maybeThat
-import com.android.systemui.kairos.util.maybeThis
+import com.android.systemui.kairos.util.maybeFirst
+import com.android.systemui.kairos.util.maybeSecond
 import com.android.systemui.kairos.util.merge
-import com.android.systemui.kairos.util.orElseGet
-import com.android.systemui.kairos.util.partitionEithers
+import com.android.systemui.kairos.util.orError
 import com.android.systemui.kairos.util.these
-import java.util.TreeMap
-import kotlinx.coroutines.coroutineScope
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.sync.withLock
 
-internal class MuxDeferredNode<K : Any, V>(
-    lifecycle: MuxLifecycle<Map<K, V>>,
-    val spec: MuxActivator<Map<K, V>>,
-) : MuxNode<K, V, Map<K, V>>(lifecycle), Key<Map<K, V>> {
+internal class MuxDeferredNode<W, K, V>(
+    val name: String?,
+    lifecycle: MuxLifecycle<W, K, V>,
+    val spec: MuxActivator<W, K, V>,
+    factory: MutableMapK.Factory<W, K>,
+) : MuxNode<W, K, V>(lifecycle, factory) {
 
     val schedulable = Schedulable.M(this)
+    var patches: NodeConnection<Iterable<Map.Entry<K, Maybe<EventsImpl<V>>>>>? = null
+    var patchData: Iterable<Map.Entry<K, Maybe<EventsImpl<V>>>>? = null
 
-    @Volatile var patches: NodeConnection<Map<K, Maybe<TFlowImpl<V>>>>? = null
-    @Volatile var patchData: Map<K, Maybe<TFlowImpl<V>>>? = null
-
-    override fun hasCurrentValueLocked(transactionStore: TransactionStore): Boolean =
-        transactionStore.contains(this)
-
-    override suspend fun hasCurrentValue(transactionStore: TransactionStore): Boolean =
-        mutex.withLock { hasCurrentValueLocked(transactionStore) }
-
-    override suspend fun visit(evalScope: EvalScope) {
-        val result = upstreamData.toMap()
-        upstreamData.clear()
-        val scheduleDownstream = result.isNotEmpty()
-        val compactDownstream = depthTracker.isDirty()
-        if (scheduleDownstream || compactDownstream) {
-            coroutineScope {
-                mutex.withLock {
-                    if (compactDownstream) {
+    override fun visit(logIndent: Int, evalScope: EvalScope) {
+        check(epoch < evalScope.epoch) { "node unexpectedly visited multiple times in transaction" }
+        logDuration(logIndent, "MuxDeferred[$name].visit") {
+            val scheduleDownstream: Boolean
+            val result: MapK<W, K, PullNode<V>>
+            logDuration("copying upstream data", false) {
+                scheduleDownstream = upstreamData.isNotEmpty()
+                result = upstreamData.readOnlyCopy()
+                upstreamData.clear()
+            }
+            if (name != null) {
+                logLn("[${this@MuxDeferredNode}] result = $result")
+            }
+            val compactDownstream = depthTracker.isDirty()
+            if (scheduleDownstream || compactDownstream) {
+                if (compactDownstream) {
+                    logDuration("compactDownstream", false) {
                         depthTracker.applyChanges(
-                            coroutineScope = this,
                             evalScope.scheduler,
                             downstreamSet,
                             muxNode = this@MuxDeferredNode,
                         )
                     }
-                    if (scheduleDownstream) {
-                        evalScope.setResult(this@MuxDeferredNode, result)
-                        if (!scheduleAll(downstreamSet, evalScope)) {
+                }
+                if (scheduleDownstream) {
+                    logDuration("scheduleDownstream") {
+                        if (name != null) {
+                            logLn("[${this@MuxDeferredNode}] scheduling")
+                        }
+                        transactionCache.put(evalScope, result)
+                        if (!scheduleAll(currentLogIndent, downstreamSet, evalScope)) {
                             evalScope.scheduleDeactivation(this@MuxDeferredNode)
                         }
                     }
@@ -84,26 +89,26 @@
         }
     }
 
-    override suspend fun getPushEvent(evalScope: EvalScope): Maybe<Map<K, V>> =
-        evalScope.getCurrentValue(key = this)
+    override fun getPushEvent(logIndent: Int, evalScope: EvalScope): MuxResult<W, K, V> =
+        logDuration(logIndent, "MuxDeferred.getPushEvent") {
+            transactionCache.getCurrentValue(evalScope).also {
+                if (name != null) {
+                    logLn("[${this@MuxDeferredNode}] getPushEvent = $it")
+                }
+            }
+        }
 
-    private suspend fun compactIfNeeded(evalScope: EvalScope) {
+    private fun compactIfNeeded(evalScope: EvalScope) {
         depthTracker.propagateChanges(evalScope.compactor, this)
     }
 
-    override suspend fun doDeactivate() {
+    override fun doDeactivate() {
         // Update lifecycle
-        lifecycle.mutex.withLock {
-            if (lifecycle.lifecycleState !is MuxLifecycleState.Active) return@doDeactivate
-            lifecycle.lifecycleState = MuxLifecycleState.Inactive(spec)
-        }
+        if (lifecycle.lifecycleState !is MuxLifecycleState.Active) return@doDeactivate
+        lifecycle.lifecycleState = MuxLifecycleState.Inactive(spec)
         // Process branch nodes
-        coroutineScope {
-            switchedIn.values.forEach { branchNode ->
-                branchNode.upstream.let {
-                    launch { it.removeDownstreamAndDeactivateIfNeeded(branchNode.schedulable) }
-                }
-            }
+        switchedIn.forEach { (_, branchNode) ->
+            branchNode.upstream.removeDownstreamAndDeactivateIfNeeded(branchNode.schedulable)
         }
         // Process patch node
         patches?.removeDownstreamAndDeactivateIfNeeded(schedulable)
@@ -112,362 +117,344 @@
     // MOVE phase
     //  - concurrent moves may be occurring, but no more evals. all depth recalculations are
     //    deferred to the end of this phase.
-    suspend fun performMove(evalScope: EvalScope) {
+    fun performMove(logIndent: Int, evalScope: EvalScope) {
+        if (name != null) {
+            logLn(logIndent, "[${this@MuxDeferredNode}] performMove (patchData = $patchData)")
+        }
+
         val patch = patchData ?: return
         patchData = null
 
-        // TODO: this logic is very similar to what's in MuxPromptMoving, maybe turn into an inline
-        //  fun?
+        // TODO: this logic is very similar to what's in MuxPrompt, maybe turn into an inline fun?
 
         // We have a patch, process additions/updates and removals
-        val (adds, removes) =
-            patch
-                .asSequence()
-                .map { (k, newUpstream: Maybe<TFlowImpl<V>>) ->
-                    when (newUpstream) {
-                        is Just -> Left(k to newUpstream.value)
-                        None -> Right(k)
-                    }
-                }
-                .partitionEithers()
+        val adds = mutableListOf<Pair<K, EventsImpl<V>>>()
+        val removes = mutableListOf<K>()
+        patch.forEach { (k, newUpstream) ->
+            when (newUpstream) {
+                is Present -> adds.add(k to newUpstream.value)
+                Absent -> removes.add(k)
+            }
+        }
 
         val severed = mutableListOf<NodeConnection<*>>()
 
-        coroutineScope {
-            // remove and sever
-            removes.forEach { k ->
-                switchedIn.remove(k)?.let { branchNode: MuxBranchNode<K, V> ->
-                    val conn = branchNode.upstream
-                    severed.add(conn)
-                    launch { conn.removeDownstream(downstream = branchNode.schedulable) }
-                    depthTracker.removeDirectUpstream(conn.depthTracker.snapshotDirectDepth)
-                }
+        // remove and sever
+        removes.forEach { k ->
+            switchedIn.remove(k)?.let { branchNode: BranchNode ->
+                val conn = branchNode.upstream
+                severed.add(conn)
+                conn.removeDownstream(downstream = branchNode.schedulable)
+                depthTracker.removeDirectUpstream(conn.depthTracker.snapshotDirectDepth)
             }
-
-            // add or replace
-            adds
-                .mapParallel { (k, newUpstream: TFlowImpl<V>) ->
-                    val branchNode = MuxBranchNode(this@MuxDeferredNode, k)
-                    k to
-                        newUpstream.activate(evalScope, branchNode.schedulable)?.let { (conn, _) ->
-                            branchNode.apply { upstream = conn }
-                        }
-                }
-                .forEach { (k, newBranch: MuxBranchNode<K, V>?) ->
-                    // remove old and sever, if present
-                    switchedIn.remove(k)?.let { branchNode ->
-                        val conn = branchNode.upstream
-                        severed.add(conn)
-                        launch { conn.removeDownstream(downstream = branchNode.schedulable) }
-                        depthTracker.removeDirectUpstream(conn.depthTracker.snapshotDirectDepth)
-                    }
-
-                    // add new
-                    newBranch?.let {
-                        switchedIn[k] = newBranch
-                        val branchDepthTracker = newBranch.upstream.depthTracker
-                        if (branchDepthTracker.snapshotIsDirect) {
-                            depthTracker.addDirectUpstream(
-                                oldDepth = null,
-                                newDepth = branchDepthTracker.snapshotDirectDepth,
-                            )
-                        } else {
-                            depthTracker.addIndirectUpstream(
-                                oldDepth = null,
-                                newDepth = branchDepthTracker.snapshotIndirectDepth,
-                            )
-                            depthTracker.updateIndirectRoots(
-                                additions = branchDepthTracker.snapshotIndirectRoots,
-                                butNot = this@MuxDeferredNode,
-                            )
-                        }
-                    }
-                }
         }
 
-        coroutineScope {
-            for (severedNode in severed) {
-                launch { severedNode.scheduleDeactivationIfNeeded(evalScope) }
+        // add or replace
+        adds.forEach { (k, newUpstream: EventsImpl<V>) ->
+            // remove old and sever, if present
+            switchedIn.remove(k)?.let { branchNode ->
+                val conn = branchNode.upstream
+                severed.add(conn)
+                conn.removeDownstream(downstream = branchNode.schedulable)
+                depthTracker.removeDirectUpstream(conn.depthTracker.snapshotDirectDepth)
             }
+
+            // add new
+            val newBranch = BranchNode(k)
+            newUpstream.activate(evalScope, newBranch.schedulable)?.let { (conn, _) ->
+                newBranch.upstream = conn
+                switchedIn[k] = newBranch
+                val branchDepthTracker = newBranch.upstream.depthTracker
+                if (branchDepthTracker.snapshotIsDirect) {
+                    depthTracker.addDirectUpstream(
+                        oldDepth = null,
+                        newDepth = branchDepthTracker.snapshotDirectDepth,
+                    )
+                } else {
+                    depthTracker.addIndirectUpstream(
+                        oldDepth = null,
+                        newDepth = branchDepthTracker.snapshotIndirectDepth,
+                    )
+                    depthTracker.updateIndirectRoots(
+                        additions = branchDepthTracker.snapshotIndirectRoots,
+                        butNot = this@MuxDeferredNode,
+                    )
+                }
+            }
+        }
+
+        for (severedNode in severed) {
+            severedNode.scheduleDeactivationIfNeeded(evalScope)
         }
 
         compactIfNeeded(evalScope)
     }
 
-    suspend fun removeDirectPatchNode(scheduler: Scheduler) {
-        mutex.withLock {
-            if (
-                depthTracker.removeIndirectUpstream(depth = 0) or
-                    depthTracker.setIsIndirectRoot(false)
-            ) {
-                depthTracker.schedule(scheduler, this)
-            }
-            patches = null
+    fun removeDirectPatchNode(scheduler: Scheduler) {
+        if (
+            depthTracker.removeIndirectUpstream(depth = 0) or depthTracker.setIsIndirectRoot(false)
+        ) {
+            depthTracker.schedule(scheduler, this)
         }
+        patches = null
     }
 
-    suspend fun removeIndirectPatchNode(
+    fun removeIndirectPatchNode(
         scheduler: Scheduler,
         depth: Int,
-        indirectSet: Set<MuxDeferredNode<*, *>>,
+        indirectSet: Set<MuxDeferredNode<*, *, *>>,
     ) {
         // indirectly connected patches forward the indirectSet
-        mutex.withLock {
-            if (
-                depthTracker.updateIndirectRoots(removals = indirectSet) or
-                    depthTracker.removeIndirectUpstream(depth)
-            ) {
-                depthTracker.schedule(scheduler, this)
-            }
-            patches = null
+        if (
+            depthTracker.updateIndirectRoots(removals = indirectSet) or
+                depthTracker.removeIndirectUpstream(depth)
+        ) {
+            depthTracker.schedule(scheduler, this)
         }
+        patches = null
     }
 
-    suspend fun moveIndirectPatchNodeToDirect(
+    fun moveIndirectPatchNodeToDirect(
         scheduler: Scheduler,
         oldIndirectDepth: Int,
-        oldIndirectSet: Set<MuxDeferredNode<*, *>>,
+        oldIndirectSet: Set<MuxDeferredNode<*, *, *>>,
     ) {
         // directly connected patches are stored as an indirect singleton set of the patchNode
-        mutex.withLock {
-            if (
-                depthTracker.updateIndirectRoots(removals = oldIndirectSet) or
-                    depthTracker.removeIndirectUpstream(oldIndirectDepth) or
-                    depthTracker.setIsIndirectRoot(true)
-            ) {
-                depthTracker.schedule(scheduler, this)
-            }
+        if (
+            depthTracker.updateIndirectRoots(removals = oldIndirectSet) or
+                depthTracker.removeIndirectUpstream(oldIndirectDepth) or
+                depthTracker.setIsIndirectRoot(true)
+        ) {
+            depthTracker.schedule(scheduler, this)
         }
     }
 
-    suspend fun moveDirectPatchNodeToIndirect(
+    fun moveDirectPatchNodeToIndirect(
         scheduler: Scheduler,
         newIndirectDepth: Int,
-        newIndirectSet: Set<MuxDeferredNode<*, *>>,
+        newIndirectSet: Set<MuxDeferredNode<*, *, *>>,
     ) {
         // indirectly connected patches forward the indirectSet
-        mutex.withLock {
-            if (
-                depthTracker.setIsIndirectRoot(false) or
-                    depthTracker.updateIndirectRoots(additions = newIndirectSet, butNot = this) or
-                    depthTracker.addIndirectUpstream(oldDepth = null, newDepth = newIndirectDepth)
-            ) {
-                depthTracker.schedule(scheduler, this)
-            }
+        if (
+            depthTracker.setIsIndirectRoot(false) or
+                depthTracker.updateIndirectRoots(additions = newIndirectSet, butNot = this) or
+                depthTracker.addIndirectUpstream(oldDepth = null, newDepth = newIndirectDepth)
+        ) {
+            depthTracker.schedule(scheduler, this)
         }
     }
 
-    suspend fun adjustIndirectPatchNode(
+    fun adjustIndirectPatchNode(
         scheduler: Scheduler,
         oldDepth: Int,
         newDepth: Int,
-        removals: Set<MuxDeferredNode<*, *>>,
-        additions: Set<MuxDeferredNode<*, *>>,
+        removals: Set<MuxDeferredNode<*, *, *>>,
+        additions: Set<MuxDeferredNode<*, *, *>>,
     ) {
         // indirectly connected patches forward the indirectSet
-        mutex.withLock {
-            if (
-                depthTracker.updateIndirectRoots(
-                    additions = additions,
-                    removals = removals,
-                    butNot = this,
-                ) or depthTracker.addIndirectUpstream(oldDepth = oldDepth, newDepth = newDepth)
-            ) {
-                depthTracker.schedule(scheduler, this)
-            }
+        if (
+            depthTracker.updateIndirectRoots(
+                additions = additions,
+                removals = removals,
+                butNot = this,
+            ) or depthTracker.addIndirectUpstream(oldDepth = oldDepth, newDepth = newDepth)
+        ) {
+            depthTracker.schedule(scheduler, this)
         }
     }
 
-    suspend fun scheduleMover(evalScope: EvalScope) {
-        patchData =
-            checkNotNull(patches) { "mux mover scheduled with unset patches upstream node" }
-                .getPushEvent(evalScope)
-                .orElseGet { null }
-        evalScope.scheduleMuxMover(this)
+    fun scheduleMover(logIndent: Int, evalScope: EvalScope) {
+        logDuration(logIndent, "MuxDeferred.scheduleMover") {
+            patchData =
+                checkNotNull(patches) { "mux mover scheduled with unset patches upstream node" }
+                    .getPushEvent(currentLogIndent, evalScope)
+            evalScope.scheduleMuxMover(this@MuxDeferredNode)
+        }
     }
 
-    override fun toString(): String = "${this::class.simpleName}@$hashString"
+    override fun toString(): String =
+        "${this::class.simpleName}@$hashString${name?.let { "[$it]" }.orEmpty()}"
 }
 
 internal inline fun <A> switchDeferredImplSingle(
-    crossinline getStorage: suspend EvalScope.() -> TFlowImpl<A>,
-    crossinline getPatches: suspend EvalScope.() -> TFlowImpl<TFlowImpl<A>>,
-): TFlowImpl<A> =
-    mapImpl({
+    name: String? = null,
+    crossinline getStorage: EvalScope.() -> EventsImpl<A>,
+    crossinline getPatches: EvalScope.() -> EventsImpl<EventsImpl<A>>,
+): EventsImpl<A> {
+    val patches =
+        mapImpl(getPatches) { newEvents, _ -> singleOf(Maybe.present(newEvents)).asIterable() }
+    val switchDeferredImpl =
         switchDeferredImpl(
-            getStorage = { mapOf(Unit to getStorage()) },
-            getPatches = { mapImpl(getPatches) { newFlow -> mapOf(Unit to just(newFlow)) } },
+            name = name,
+            getStorage = { singleOf(getStorage()).asIterable() },
+            getPatches = { patches },
+            storeFactory = SingletonMapK.Factory(),
         )
-    }) { map ->
-        map.getValue(Unit)
+    return mapImpl({ switchDeferredImpl }) { map, logIndent ->
+        map.asSingle().getValue(Unit).getPushEvent(logIndent, this).also {
+            if (name != null) {
+                logLn(logIndent, "[$name] extracting single mux: $it")
+            }
+        }
     }
+}
 
-internal fun <K : Any, A> switchDeferredImpl(
-    getStorage: suspend EvalScope.() -> Map<K, TFlowImpl<A>>,
-    getPatches: suspend EvalScope.() -> TFlowImpl<Map<K, Maybe<TFlowImpl<A>>>>,
-): TFlowImpl<Map<K, A>> =
-    MuxLifecycle(
-        object : MuxActivator<Map<K, A>> {
-            override suspend fun activate(
-                evalScope: EvalScope,
-                lifecycle: MuxLifecycle<Map<K, A>>,
-            ): MuxNode<*, *, Map<K, A>>? {
-                val storage: Map<K, TFlowImpl<A>> = getStorage(evalScope)
-                // Initialize mux node and switched-in connections.
-                val muxNode =
-                    MuxDeferredNode(lifecycle, this).apply {
-                        storage.mapValuesNotNullParallelTo(switchedIn) { (key, flow) ->
-                            val branchNode = MuxBranchNode(this@apply, key)
-                            flow.activate(evalScope, branchNode.schedulable)?.let {
-                                (conn, needsEval) ->
-                                branchNode
-                                    .apply { upstream = conn }
-                                    .also {
-                                        if (needsEval) {
-                                            val result = conn.getPushEvent(evalScope)
-                                            if (result is Just) {
-                                                upstreamData[key] = result.value
-                                            }
-                                        }
-                                    }
-                            }
-                        }
-                    }
+internal fun <W, K, V> switchDeferredImpl(
+    name: String? = null,
+    getStorage: EvalScope.() -> Iterable<Map.Entry<K, EventsImpl<V>>>,
+    getPatches: EvalScope.() -> EventsImpl<Iterable<Map.Entry<K, Maybe<EventsImpl<V>>>>>,
+    storeFactory: MutableMapK.Factory<W, K>,
+): EventsImpl<MuxResult<W, K, V>> =
+    MuxLifecycle(MuxDeferredActivator(name, getStorage, storeFactory, getPatches))
+
+private class MuxDeferredActivator<W, K, V>(
+    private val name: String?,
+    private val getStorage: EvalScope.() -> Iterable<Map.Entry<K, EventsImpl<V>>>,
+    private val storeFactory: MutableMapK.Factory<W, K>,
+    private val getPatches: EvalScope.() -> EventsImpl<Iterable<Map.Entry<K, Maybe<EventsImpl<V>>>>>,
+) : MuxActivator<W, K, V> {
+    override fun activate(
+        evalScope: EvalScope,
+        lifecycle: MuxLifecycle<W, K, V>,
+    ): Pair<MuxNode<W, K, V>, (() -> Unit)?>? {
+        // Initialize mux node and switched-in connections.
+        val muxNode =
+            MuxDeferredNode(name, lifecycle, this, storeFactory).apply {
+                initializeUpstream(evalScope, getStorage, storeFactory)
                 // Update depth based on all initial switched-in nodes.
-                muxNode.switchedIn.values.forEach { branch ->
-                    val conn = branch.upstream
-                    if (conn.depthTracker.snapshotIsDirect) {
-                        muxNode.depthTracker.addDirectUpstream(
-                            oldDepth = null,
-                            newDepth = conn.depthTracker.snapshotDirectDepth,
-                        )
-                    } else {
-                        muxNode.depthTracker.addIndirectUpstream(
-                            oldDepth = null,
-                            newDepth = conn.depthTracker.snapshotIndirectDepth,
-                        )
-                        muxNode.depthTracker.updateIndirectRoots(
-                            additions = conn.depthTracker.snapshotIndirectRoots,
-                            butNot = muxNode,
-                        )
-                    }
-                }
+                initializeDepth()
                 // We don't have our patches connection established yet, so for now pretend we have
                 // a direct connection to patches. We will update downstream nodes later if this
                 // turns out to be a lie.
-                muxNode.depthTracker.setIsIndirectRoot(true)
-                muxNode.depthTracker.reset()
+                depthTracker.setIsIndirectRoot(true)
+                depthTracker.reset()
+            }
 
+        // Schedule for evaluation if any switched-in nodes have already emitted within
+        // this transaction.
+        if (muxNode.upstreamData.isNotEmpty()) {
+            muxNode.schedule(evalScope)
+        }
+
+        return muxNode to
+            fun() {
                 // Setup patches connection; deferring allows for a recursive connection, where
                 // muxNode is downstream of itself via patches.
-                var isIndirect = true
-                evalScope.deferAction {
-                    val (patchesConn, needsEval) =
-                        getPatches(evalScope).activate(evalScope, downstream = muxNode.schedulable)
-                            ?: run {
-                                isIndirect = false
-                                // Turns out we can't connect to patches, so update our depth and
-                                // propagate
-                                muxNode.mutex.withLock {
-                                    if (muxNode.depthTracker.setIsIndirectRoot(false)) {
-                                        muxNode.depthTracker.schedule(evalScope.scheduler, muxNode)
-                                    }
-                                }
-                                return@deferAction
-                            }
-                    muxNode.patches = patchesConn
-
-                    if (!patchesConn.schedulerUpstream.depthTracker.snapshotIsDirect) {
-                        // Turns out patches is indirect, so we are not a root. Update depth and
-                        // propagate.
-                        muxNode.mutex.withLock {
-                            if (
-                                muxNode.depthTracker.setIsIndirectRoot(false) or
-                                    muxNode.depthTracker.addIndirectUpstream(
-                                        oldDepth = null,
-                                        newDepth = patchesConn.depthTracker.snapshotIndirectDepth,
-                                    ) or
-                                    muxNode.depthTracker.updateIndirectRoots(
-                                        additions = patchesConn.depthTracker.snapshotIndirectRoots
-                                    )
-                            ) {
+                val (patchesConn, needsEval) =
+                    getPatches(evalScope).activate(evalScope, downstream = muxNode.schedulable)
+                        ?: run {
+                            // Turns out we can't connect to patches, so update our depth and
+                            // propagate
+                            if (muxNode.depthTracker.setIsIndirectRoot(false)) {
+                                // TODO: schedules might not be necessary now that we're not
+                                // parallel?
                                 muxNode.depthTracker.schedule(evalScope.scheduler, muxNode)
                             }
+                            return
                         }
-                    }
-                    // Schedule mover to process patch emission at the end of this transaction, if
-                    // needed.
-                    if (needsEval) {
-                        val result = patchesConn.getPushEvent(evalScope)
-                        if (result is Just) {
-                            muxNode.patchData = result.value
-                            evalScope.scheduleMuxMover(muxNode)
-                        }
-                    }
-                }
+                muxNode.patches = patchesConn
 
-                // Schedule for evaluation if any switched-in nodes have already emitted within
-                // this transaction.
-                if (muxNode.upstreamData.isNotEmpty()) {
-                    muxNode.schedule(evalScope)
+                if (!patchesConn.schedulerUpstream.depthTracker.snapshotIsDirect) {
+                    // Turns out patches is indirect, so we are not a root. Update depth and
+                    // propagate.
+                    if (
+                        muxNode.depthTracker.setIsIndirectRoot(false) or
+                            muxNode.depthTracker.addIndirectUpstream(
+                                oldDepth = null,
+                                newDepth = patchesConn.depthTracker.snapshotIndirectDepth,
+                            ) or
+                            muxNode.depthTracker.updateIndirectRoots(
+                                additions = patchesConn.depthTracker.snapshotIndirectRoots
+                            )
+                    ) {
+                        muxNode.depthTracker.schedule(evalScope.scheduler, muxNode)
+                    }
                 }
-                return muxNode.takeUnless { muxNode.switchedIn.isEmpty() && !isIndirect }
+                // Schedule mover to process patch emission at the end of this transaction, if
+                // needed.
+                if (needsEval) {
+                    muxNode.patchData = patchesConn.getPushEvent(0, evalScope)
+                    evalScope.scheduleMuxMover(muxNode)
+                }
             }
-        }
-    )
+    }
+}
 
 internal inline fun <A> mergeNodes(
-    crossinline getPulse: suspend EvalScope.() -> TFlowImpl<A>,
-    crossinline getOther: suspend EvalScope.() -> TFlowImpl<A>,
-    crossinline f: suspend EvalScope.(A, A) -> A,
-): TFlowImpl<A> {
+    crossinline getPulse: EvalScope.() -> EventsImpl<A>,
+    crossinline getOther: EvalScope.() -> EventsImpl<A>,
+    name: String? = null,
+    crossinline f: EvalScope.(A, A) -> A,
+): EventsImpl<A> {
+    val mergedThese = mergeNodes(name, getPulse, getOther)
     val merged =
-        mapImpl({ mergeNodes(getPulse, getOther) }) { these ->
-            these.merge { thiz, that -> f(thiz, that) }
-        }
+        mapImpl({ mergedThese }) { these, _ -> these.merge { thiz, that -> f(thiz, that) } }
     return merged.cached()
 }
 
+internal fun <T> Iterable<T>.asIterableWithIndex(): Iterable<Map.Entry<Int, T>> =
+    asSequence().mapIndexed { i, t -> StoreEntry(i, t) }.asIterable()
+
 internal inline fun <A, B> mergeNodes(
-    crossinline getPulse: suspend EvalScope.() -> TFlowImpl<A>,
-    crossinline getOther: suspend EvalScope.() -> TFlowImpl<B>,
-): TFlowImpl<These<A, B>> {
+    name: String? = null,
+    crossinline getPulse: EvalScope.() -> EventsImpl<A>,
+    crossinline getOther: EvalScope.() -> EventsImpl<B>,
+): EventsImpl<These<A, B>> {
     val storage =
-        mapOf(
-            0 to mapImpl(getPulse) { These.thiz<A, B>(it) },
-            1 to mapImpl(getOther) { These.that(it) },
+        listOf(
+                mapImpl(getPulse) { it, _ -> These.first(it) },
+                mapImpl(getOther) { it, _ -> These.second(it) },
+            )
+            .asIterableWithIndex()
+    val switchNode =
+        switchDeferredImpl(
+            name = name,
+            getStorage = { storage },
+            getPatches = { neverImpl },
+            storeFactory = MutableArrayMapK.Factory(),
         )
-    val switchNode = switchDeferredImpl(getStorage = { storage }, getPatches = { neverImpl })
     val merged =
-        mapImpl({ switchNode }) { mergeResults ->
-            val first = mergeResults.getMaybe(0).flatMap { it.maybeThis() }
-            val second = mergeResults.getMaybe(1).flatMap { it.maybeThat() }
-            these(first, second).orElseGet { error("unexpected missing merge result") }
+        mapImpl({ switchNode }) { it, logIndent ->
+            val mergeResults = it.asArrayHolder()
+            val first =
+                mergeResults.getMaybe(0).flatMap { it.getPushEvent(logIndent, this).maybeFirst() }
+            val second =
+                mergeResults.getMaybe(1).flatMap { it.getPushEvent(logIndent, this).maybeSecond() }
+            these(first, second).orError { "unexpected missing merge result" }
         }
     return merged.cached()
 }
 
 internal inline fun <A> mergeNodes(
-    crossinline getPulses: suspend EvalScope.() -> Iterable<TFlowImpl<A>>
-): TFlowImpl<List<A>> {
+    crossinline getPulses: EvalScope.() -> Iterable<EventsImpl<A>>
+): EventsImpl<List<A>> {
     val switchNode =
         switchDeferredImpl(
-            getStorage = { getPulses().associateByIndexTo(TreeMap()) },
+            getStorage = { getPulses().asIterableWithIndex() },
             getPatches = { neverImpl },
+            storeFactory = MutableArrayMapK.Factory(),
         )
-    val merged = mapImpl({ switchNode }) { mergeResults -> mergeResults.values.toList() }
+    val merged =
+        mapImpl({ switchNode }) { it, logIndent ->
+            val mergeResults = it.asArrayHolder()
+            mergeResults.map { (_, node) -> node.getPushEvent(logIndent, this) }
+        }
     return merged.cached()
 }
 
 internal inline fun <A> mergeNodesLeft(
-    crossinline getPulses: suspend EvalScope.() -> Iterable<TFlowImpl<A>>
-): TFlowImpl<A> {
+    crossinline getPulses: EvalScope.() -> Iterable<EventsImpl<A>>
+): EventsImpl<A> {
     val switchNode =
         switchDeferredImpl(
-            getStorage = { getPulses().associateByIndexTo(TreeMap()) },
+            getStorage = { getPulses().asIterableWithIndex() },
             getPatches = { neverImpl },
+            storeFactory = MutableArrayMapK.Factory(),
         )
     val merged =
-        mapImpl({ switchNode }) { mergeResults: Map<Int, A> -> mergeResults.values.first() }
+        mapImpl({ switchNode }) { it, logIndent ->
+            val mergeResults = it.asArrayHolder()
+            mergeResults.values.first().getPushEvent(logIndent, this)
+        }
     return merged.cached()
 }
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/MuxPrompt.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/MuxPrompt.kt
index b291c87..cb2c6e5 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/MuxPrompt.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/MuxPrompt.kt
@@ -16,217 +16,183 @@
 
 package com.android.systemui.kairos.internal
 
-import com.android.systemui.kairos.internal.util.Key
-import com.android.systemui.kairos.internal.util.launchImmediate
-import com.android.systemui.kairos.internal.util.mapParallel
-import com.android.systemui.kairos.internal.util.mapValuesNotNullParallelTo
-import com.android.systemui.kairos.util.Just
-import com.android.systemui.kairos.util.Left
+import com.android.systemui.kairos.internal.store.MutableMapK
+import com.android.systemui.kairos.internal.store.SingletonMapK
+import com.android.systemui.kairos.internal.store.asSingle
+import com.android.systemui.kairos.internal.store.singleOf
+import com.android.systemui.kairos.internal.util.LogIndent
+import com.android.systemui.kairos.internal.util.hashString
+import com.android.systemui.kairos.internal.util.logDuration
 import com.android.systemui.kairos.util.Maybe
-import com.android.systemui.kairos.util.None
-import com.android.systemui.kairos.util.Right
-import com.android.systemui.kairos.util.filterJust
-import com.android.systemui.kairos.util.map
-import com.android.systemui.kairos.util.partitionEithers
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.async
-import kotlinx.coroutines.awaitAll
-import kotlinx.coroutines.coroutineScope
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.sync.withLock
+import com.android.systemui.kairos.util.Maybe.Absent
+import com.android.systemui.kairos.util.Maybe.Present
 
-internal class MuxPromptMovingNode<K : Any, V>(
-    lifecycle: MuxLifecycle<Pair<Map<K, V>, Map<K, PullNode<V>>?>>,
-    private val spec: MuxActivator<Pair<Map<K, V>, Map<K, PullNode<V>>?>>,
-) :
-    MuxNode<K, V, Pair<Map<K, V>, Map<K, PullNode<V>>?>>(lifecycle),
-    Key<Pair<Map<K, V>, Map<K, PullNode<V>>?>> {
+internal class MuxPromptNode<W, K, V>(
+    val name: String?,
+    lifecycle: MuxLifecycle<W, K, V>,
+    private val spec: MuxActivator<W, K, V>,
+    factory: MutableMapK.Factory<W, K>,
+) : MuxNode<W, K, V>(lifecycle, factory) {
 
-    @Volatile var patchData: Map<K, Maybe<TFlowImpl<V>>>? = null
-    @Volatile var patches: MuxPromptPatchNode<K, V>? = null
+    var patchData: Iterable<Map.Entry<K, Maybe<EventsImpl<V>>>>? = null
+    var patches: PatchNode? = null
 
-    @Volatile private var reEval: Pair<Map<K, V>, Map<K, PullNode<V>>?>? = null
+    override fun visit(logIndent: Int, evalScope: EvalScope) {
+        check(epoch < evalScope.epoch) { "node unexpectedly visited multiple times in transaction" }
+        logDuration(logIndent, "MuxPrompt.visit") {
+            val patch: Iterable<Map.Entry<K, Maybe<EventsImpl<V>>>>? = patchData
+            patchData = null
 
-    override fun hasCurrentValueLocked(transactionStore: TransactionStore): Boolean =
-        transactionStore.contains(this)
-
-    override suspend fun hasCurrentValue(transactionStore: TransactionStore): Boolean =
-        mutex.withLock { hasCurrentValueLocked(transactionStore) }
-
-    override suspend fun visit(evalScope: EvalScope) {
-        val preSwitchResults: Map<K, V> = upstreamData.toMap()
-        upstreamData.clear()
-
-        val patch: Map<K, Maybe<TFlowImpl<V>>>? = patchData
-        patchData = null
-
-        val (reschedule, evalResult) =
-            reEval?.let { false to it }
-                ?: if (preSwitchResults.isNotEmpty() || patch?.isNotEmpty() == true) {
-                    doEval(preSwitchResults, patch, evalScope)
-                } else {
-                    false to null
-                }
-        reEval = null
-
-        if (reschedule || depthTracker.dirty_depthIncreased()) {
-            reEval = evalResult
-            // Can't schedule downstream yet, need to compact first
-            if (depthTracker.dirty_depthIncreased()) {
-                depthTracker.schedule(evalScope.compactor, node = this)
-            }
-            schedule(evalScope)
-        } else {
-            val compactDownstream = depthTracker.isDirty()
-            if (evalResult != null || compactDownstream) {
-                coroutineScope {
-                    mutex.withLock {
-                        if (compactDownstream) {
-                            adjustDownstreamDepths(evalScope, coroutineScope = this)
-                        }
-                        if (evalResult != null) {
-                            evalScope.setResult(this@MuxPromptMovingNode, evalResult)
-                            if (!scheduleAll(downstreamSet, evalScope)) {
-                                evalScope.scheduleDeactivation(this@MuxPromptMovingNode)
-                            }
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    private suspend fun doEval(
-        preSwitchResults: Map<K, V>,
-        patch: Map<K, Maybe<TFlowImpl<V>>>?,
-        evalScope: EvalScope,
-    ): Pair<Boolean, Pair<Map<K, V>, Map<K, PullNode<V>>?>?> {
-        val newlySwitchedIn: Map<K, PullNode<V>>? =
+            // If there's a patch, process it.
             patch?.let {
-                // We have a patch, process additions/updates and removals
-                val (adds, removes) =
-                    patch
-                        .asSequence()
-                        .map { (k, newUpstream: Maybe<TFlowImpl<V>>) ->
-                            when (newUpstream) {
-                                is Just -> Left(k to newUpstream.value)
-                                None -> Right(k)
-                            }
-                        }
-                        .partitionEithers()
-
-                val additionsAndUpdates = mutableMapOf<K, PullNode<V>>()
-                val severed = mutableListOf<NodeConnection<*>>()
-
-                coroutineScope {
-                    // remove and sever
-                    removes.forEach { k ->
-                        switchedIn.remove(k)?.let { branchNode: MuxBranchNode<K, V> ->
-                            val conn: NodeConnection<V> = branchNode.upstream
-                            severed.add(conn)
-                            launchImmediate {
-                                conn.removeDownstream(downstream = branchNode.schedulable)
-                            }
-                            depthTracker.removeDirectUpstream(conn.depthTracker.snapshotDirectDepth)
-                        }
+                val needsReschedule = processPatch(patch, evalScope)
+                // We may need to reschedule if newly-switched-in nodes have not yet been
+                // visited within this transaction.
+                val depthIncreased = depthTracker.dirty_depthIncreased()
+                if (needsReschedule || depthIncreased) {
+                    if (depthIncreased) {
+                        depthTracker.schedule(evalScope.compactor, this@MuxPromptNode)
                     }
-
-                    // add or replace
-                    adds
-                        .mapParallel { (k, newUpstream: TFlowImpl<V>) ->
-                            val branchNode = MuxBranchNode(this@MuxPromptMovingNode, k)
-                            k to
-                                newUpstream.activate(evalScope, branchNode.schedulable)?.let {
-                                    (conn, _) ->
-                                    branchNode.apply { upstream = conn }
-                                }
-                        }
-                        .forEach { (k, newBranch: MuxBranchNode<K, V>?) ->
-                            // remove old and sever, if present
-                            switchedIn.remove(k)?.let { oldBranch: MuxBranchNode<K, V> ->
-                                val conn: NodeConnection<V> = oldBranch.upstream
-                                severed.add(conn)
-                                launchImmediate {
-                                    conn.removeDownstream(downstream = oldBranch.schedulable)
-                                }
-                                depthTracker.removeDirectUpstream(
-                                    conn.depthTracker.snapshotDirectDepth
-                                )
-                            }
-
-                            // add new
-                            newBranch?.let {
-                                switchedIn[k] = newBranch
-                                additionsAndUpdates[k] = newBranch.upstream.directUpstream
-                                val branchDepthTracker = newBranch.upstream.depthTracker
-                                if (branchDepthTracker.snapshotIsDirect) {
-                                    depthTracker.addDirectUpstream(
-                                        oldDepth = null,
-                                        newDepth = branchDepthTracker.snapshotDirectDepth,
-                                    )
-                                } else {
-                                    depthTracker.addIndirectUpstream(
-                                        oldDepth = null,
-                                        newDepth = branchDepthTracker.snapshotIndirectDepth,
-                                    )
-                                    depthTracker.updateIndirectRoots(
-                                        additions = branchDepthTracker.snapshotIndirectRoots,
-                                        butNot = null,
-                                    )
-                                }
-                            }
-                        }
-                }
-
-                coroutineScope {
-                    for (severedNode in severed) {
-                        launch { severedNode.scheduleDeactivationIfNeeded(evalScope) }
+                    if (name != null) {
+                        logLn(
+                            "[${this@MuxPromptNode}] rescheduling (reschedule=$needsReschedule, depthIncrease=$depthIncreased)"
+                        )
                     }
+                    schedule(evalScope)
+                    return
                 }
-
-                additionsAndUpdates.takeIf { it.isNotEmpty() }
             }
+            val results = upstreamData.readOnlyCopy().also { upstreamData.clear() }
 
-        return if (preSwitchResults.isNotEmpty() || newlySwitchedIn != null) {
-            (newlySwitchedIn != null) to (preSwitchResults to newlySwitchedIn)
-        } else {
-            false to null
+            // If we don't need to reschedule, or there wasn't a patch at all, then we proceed
+            // with merging pre-switch and post-switch results
+            val hasResult = results.isNotEmpty()
+            val compactDownstream = depthTracker.isDirty()
+            if (hasResult || compactDownstream) {
+                if (compactDownstream) {
+                    adjustDownstreamDepths(evalScope)
+                }
+                if (hasResult) {
+                    transactionCache.put(evalScope, results)
+                    if (!scheduleAll(currentLogIndent, downstreamSet, evalScope)) {
+                        evalScope.scheduleDeactivation(this@MuxPromptNode)
+                    }
+                }
+            }
         }
     }
 
-    private suspend fun adjustDownstreamDepths(
+    // side-effect: this will populate `upstreamData` with any immediately available results
+    private fun LogIndent.processPatch(
+        patch: Iterable<Map.Entry<K, Maybe<EventsImpl<V>>>>,
         evalScope: EvalScope,
-        coroutineScope: CoroutineScope,
-    ) {
+    ): Boolean {
+        var needsReschedule = false
+        // We have a patch, process additions/updates and removals
+        val adds = mutableListOf<Pair<K, EventsImpl<V>>>()
+        val removes = mutableListOf<K>()
+        patch.forEach { (k, newUpstream) ->
+            when (newUpstream) {
+                is Present -> adds.add(k to newUpstream.value)
+                Absent -> removes.add(k)
+            }
+        }
+
+        val severed = mutableListOf<NodeConnection<*>>()
+
+        // remove and sever
+        removes.forEach { k ->
+            switchedIn.remove(k)?.let { branchNode: BranchNode ->
+                if (name != null) {
+                    logLn("[${this@MuxPromptNode}] removing $k")
+                }
+                val conn: NodeConnection<V> = branchNode.upstream
+                severed.add(conn)
+                conn.removeDownstream(downstream = branchNode.schedulable)
+                depthTracker.removeDirectUpstream(conn.depthTracker.snapshotDirectDepth)
+            }
+        }
+
+        // add or replace
+        adds.forEach { (k, newUpstream: EventsImpl<V>) ->
+            // remove old and sever, if present
+            switchedIn.remove(k)?.let { oldBranch: BranchNode ->
+                if (name != null) {
+                    logLn("[${this@MuxPromptNode}] replacing $k")
+                }
+                val conn: NodeConnection<V> = oldBranch.upstream
+                severed.add(conn)
+                conn.removeDownstream(downstream = oldBranch.schedulable)
+                depthTracker.removeDirectUpstream(conn.depthTracker.snapshotDirectDepth)
+            }
+
+            // add new
+            val newBranch = BranchNode(k)
+            newUpstream.activate(evalScope, newBranch.schedulable)?.let { (conn, needsEval) ->
+                newBranch.upstream = conn
+                if (name != null) {
+                    logLn("[${this@MuxPromptNode}] switching in $k")
+                }
+                switchedIn[k] = newBranch
+                if (needsEval) {
+                    upstreamData[k] = newBranch.upstream.directUpstream
+                } else {
+                    needsReschedule = true
+                }
+                val branchDepthTracker = newBranch.upstream.depthTracker
+                if (branchDepthTracker.snapshotIsDirect) {
+                    depthTracker.addDirectUpstream(
+                        oldDepth = null,
+                        newDepth = branchDepthTracker.snapshotDirectDepth,
+                    )
+                } else {
+                    depthTracker.addIndirectUpstream(
+                        oldDepth = null,
+                        newDepth = branchDepthTracker.snapshotIndirectDepth,
+                    )
+                    depthTracker.updateIndirectRoots(
+                        additions = branchDepthTracker.snapshotIndirectRoots,
+                        butNot = null,
+                    )
+                }
+            }
+        }
+
+        for (severedNode in severed) {
+            severedNode.scheduleDeactivationIfNeeded(evalScope)
+        }
+
+        return needsReschedule
+    }
+
+    private fun adjustDownstreamDepths(evalScope: EvalScope) {
         if (depthTracker.dirty_depthIncreased()) {
             // schedule downstream nodes on the compaction scheduler; this scheduler is drained at
             // the end of this eval depth, so that all depth increases are applied before we advance
             // the eval step
-            depthTracker.schedule(evalScope.compactor, node = this@MuxPromptMovingNode)
+            depthTracker.schedule(evalScope.compactor, node = this@MuxPromptNode)
         } else if (depthTracker.isDirty()) {
             // schedule downstream nodes on the eval scheduler; this is more efficient and is only
             // safe if the depth hasn't increased
             depthTracker.applyChanges(
-                coroutineScope,
                 evalScope.scheduler,
                 downstreamSet,
-                muxNode = this@MuxPromptMovingNode,
+                muxNode = this@MuxPromptNode,
             )
         }
     }
 
-    override suspend fun getPushEvent(
-        evalScope: EvalScope
-    ): Maybe<Pair<Map<K, V>, Map<K, PullNode<V>>?>> = evalScope.getCurrentValue(key = this)
-
-    override suspend fun doDeactivate() {
-        // Update lifecycle
-        lifecycle.mutex.withLock {
-            if (lifecycle.lifecycleState !is MuxLifecycleState.Active) return@doDeactivate
-            lifecycle.lifecycleState = MuxLifecycleState.Inactive(spec)
+    override fun getPushEvent(logIndent: Int, evalScope: EvalScope): MuxResult<W, K, V> =
+        logDuration(logIndent, "MuxPrompt.getPushEvent") {
+            transactionCache.getCurrentValue(evalScope)
         }
+
+    override fun doDeactivate() {
+        // Update lifecycle
+        if (lifecycle.lifecycleState !is MuxLifecycleState.Active) return
+        lifecycle.lifecycleState = MuxLifecycleState.Inactive(spec)
         // Process branch nodes
-        switchedIn.values.forEach { branchNode ->
+        switchedIn.forEach { (_, branchNode) ->
             branchNode.upstream.removeDownstreamAndDeactivateIfNeeded(
                 downstream = branchNode.schedulable
             )
@@ -237,236 +203,191 @@
         }
     }
 
-    suspend fun removeIndirectPatchNode(
+    fun removeIndirectPatchNode(
         scheduler: Scheduler,
         oldDepth: Int,
-        indirectSet: Set<MuxDeferredNode<*, *>>,
+        indirectSet: Set<MuxDeferredNode<*, *, *>>,
     ) {
-        mutex.withLock {
-            patches = null
-            if (
-                depthTracker.removeIndirectUpstream(oldDepth) or
-                    depthTracker.updateIndirectRoots(removals = indirectSet)
-            ) {
-                depthTracker.schedule(scheduler, this)
-            }
+        patches = null
+        if (
+            depthTracker.removeIndirectUpstream(oldDepth) or
+                depthTracker.updateIndirectRoots(removals = indirectSet)
+        ) {
+            depthTracker.schedule(scheduler, this)
         }
     }
 
-    suspend fun removeDirectPatchNode(scheduler: Scheduler, depth: Int) {
-        mutex.withLock {
-            patches = null
-            if (depthTracker.removeDirectUpstream(depth)) {
-                depthTracker.schedule(scheduler, this)
-            }
-        }
-    }
-}
-
-internal class MuxPromptEvalNode<K, V>(
-    private val movingNode: PullNode<Pair<Map<K, V>, Map<K, PullNode<V>>?>>
-) : PullNode<Map<K, V>> {
-    override suspend fun getPushEvent(evalScope: EvalScope): Maybe<Map<K, V>> =
-        movingNode.getPushEvent(evalScope).map { (preSwitchResults, newlySwitchedIn) ->
-            coroutineScope {
-                newlySwitchedIn
-                    ?.map { (k, v) -> async { v.getPushEvent(evalScope).map { k to it } } }
-                    ?.awaitAll()
-                    ?.asSequence()
-                    ?.filterJust()
-                    ?.toMap(preSwitchResults.toMutableMap()) ?: preSwitchResults
-            }
-        }
-}
-
-// TODO: inner class?
-internal class MuxPromptPatchNode<K : Any, V>(private val muxNode: MuxPromptMovingNode<K, V>) :
-    SchedulableNode {
-
-    val schedulable = Schedulable.N(this)
-
-    lateinit var upstream: NodeConnection<Map<K, Maybe<TFlowImpl<V>>>>
-
-    override suspend fun schedule(evalScope: EvalScope) {
-        val upstreamResult = upstream.getPushEvent(evalScope)
-        if (upstreamResult is Just) {
-            muxNode.patchData = upstreamResult.value
-            muxNode.schedule(evalScope)
+    fun removeDirectPatchNode(scheduler: Scheduler, depth: Int) {
+        patches = null
+        if (depthTracker.removeDirectUpstream(depth)) {
+            depthTracker.schedule(scheduler, this)
         }
     }
 
-    override suspend fun adjustDirectUpstream(scheduler: Scheduler, oldDepth: Int, newDepth: Int) {
-        muxNode.adjustDirectUpstream(scheduler, oldDepth, newDepth)
-    }
+    override fun toString(): String =
+        "${this::class.simpleName}@$hashString${name?.let { "[$it]" }.orEmpty()}"
 
-    override suspend fun moveIndirectUpstreamToDirect(
-        scheduler: Scheduler,
-        oldIndirectDepth: Int,
-        oldIndirectSet: Set<MuxDeferredNode<*, *>>,
-        newDirectDepth: Int,
-    ) {
-        muxNode.moveIndirectUpstreamToDirect(
-            scheduler,
-            oldIndirectDepth,
-            oldIndirectSet,
-            newDirectDepth,
-        )
-    }
+    inner class PatchNode : SchedulableNode {
 
-    override suspend fun adjustIndirectUpstream(
-        scheduler: Scheduler,
-        oldDepth: Int,
-        newDepth: Int,
-        removals: Set<MuxDeferredNode<*, *>>,
-        additions: Set<MuxDeferredNode<*, *>>,
-    ) {
-        muxNode.adjustIndirectUpstream(scheduler, oldDepth, newDepth, removals, additions)
-    }
+        val schedulable = Schedulable.N(this)
 
-    override suspend fun moveDirectUpstreamToIndirect(
-        scheduler: Scheduler,
-        oldDirectDepth: Int,
-        newIndirectDepth: Int,
-        newIndirectSet: Set<MuxDeferredNode<*, *>>,
-    ) {
-        muxNode.moveDirectUpstreamToIndirect(
-            scheduler,
-            oldDirectDepth,
-            newIndirectDepth,
-            newIndirectSet,
-        )
-    }
+        lateinit var upstream: NodeConnection<Iterable<Map.Entry<K, Maybe<EventsImpl<V>>>>>
 
-    override suspend fun removeDirectUpstream(scheduler: Scheduler, depth: Int) {
-        muxNode.removeDirectPatchNode(scheduler, depth)
-    }
-
-    override suspend fun removeIndirectUpstream(
-        scheduler: Scheduler,
-        depth: Int,
-        indirectSet: Set<MuxDeferredNode<*, *>>,
-    ) {
-        muxNode.removeIndirectPatchNode(scheduler, depth, indirectSet)
-    }
-}
-
-internal fun <K : Any, A> switchPromptImpl(
-    getStorage: suspend EvalScope.() -> Map<K, TFlowImpl<A>>,
-    getPatches: suspend EvalScope.() -> TFlowImpl<Map<K, Maybe<TFlowImpl<A>>>>,
-): TFlowImpl<Map<K, A>> {
-    val moving =
-        MuxLifecycle(
-            object : MuxActivator<Pair<Map<K, A>, Map<K, PullNode<A>>?>> {
-                override suspend fun activate(
-                    evalScope: EvalScope,
-                    lifecycle: MuxLifecycle<Pair<Map<K, A>, Map<K, PullNode<A>>?>>,
-                ): MuxNode<*, *, Pair<Map<K, A>, Map<K, PullNode<A>>?>>? {
-                    val storage: Map<K, TFlowImpl<A>> = getStorage(evalScope)
-                    // Initialize mux node and switched-in connections.
-                    val movingNode =
-                        MuxPromptMovingNode(lifecycle, this).apply {
-                            coroutineScope {
-                                launch {
-                                    storage.mapValuesNotNullParallelTo(switchedIn) { (key, flow) ->
-                                        val branchNode = MuxBranchNode(this@apply, key)
-                                        flow
-                                            .activate(
-                                                evalScope = evalScope,
-                                                downstream = branchNode.schedulable,
-                                            )
-                                            ?.let { (conn, needsEval) ->
-                                                branchNode
-                                                    .apply { upstream = conn }
-                                                    .also {
-                                                        if (needsEval) {
-                                                            val result =
-                                                                conn.getPushEvent(evalScope)
-                                                            if (result is Just) {
-                                                                upstreamData[key] = result.value
-                                                            }
-                                                        }
-                                                    }
-                                            }
-                                    }
-                                }
-                                // Setup patches connection
-                                val patchNode = MuxPromptPatchNode(this@apply)
-                                getPatches(evalScope)
-                                    .activate(
-                                        evalScope = evalScope,
-                                        downstream = patchNode.schedulable,
-                                    )
-                                    ?.let { (conn, needsEval) ->
-                                        patchNode.upstream = conn
-                                        patches = patchNode
-
-                                        if (needsEval) {
-                                            val result = conn.getPushEvent(evalScope)
-                                            if (result is Just) {
-                                                patchData = result.value
-                                            }
-                                        }
-                                    }
-                            }
-                        }
-                    // Update depth based on all initial switched-in nodes.
-                    movingNode.switchedIn.values.forEach { branch ->
-                        val conn = branch.upstream
-                        if (conn.depthTracker.snapshotIsDirect) {
-                            movingNode.depthTracker.addDirectUpstream(
-                                oldDepth = null,
-                                newDepth = conn.depthTracker.snapshotDirectDepth,
-                            )
-                        } else {
-                            movingNode.depthTracker.addIndirectUpstream(
-                                oldDepth = null,
-                                newDepth = conn.depthTracker.snapshotIndirectDepth,
-                            )
-                            movingNode.depthTracker.updateIndirectRoots(
-                                additions = conn.depthTracker.snapshotIndirectRoots,
-                                butNot = null,
-                            )
-                        }
-                    }
-                    // Update depth based on patches node.
-                    movingNode.patches?.upstream?.let { conn ->
-                        if (conn.depthTracker.snapshotIsDirect) {
-                            movingNode.depthTracker.addDirectUpstream(
-                                oldDepth = null,
-                                newDepth = conn.depthTracker.snapshotDirectDepth,
-                            )
-                        } else {
-                            movingNode.depthTracker.addIndirectUpstream(
-                                oldDepth = null,
-                                newDepth = conn.depthTracker.snapshotIndirectDepth,
-                            )
-                            movingNode.depthTracker.updateIndirectRoots(
-                                additions = conn.depthTracker.snapshotIndirectRoots,
-                                butNot = null,
-                            )
-                        }
-                    }
-                    movingNode.depthTracker.reset()
-
-                    // Schedule for evaluation if any switched-in nodes or the patches node have
-                    // already emitted within this transaction.
-                    if (movingNode.patchData != null || movingNode.upstreamData.isNotEmpty()) {
-                        movingNode.schedule(evalScope)
-                    }
-
-                    return movingNode.takeUnless { it.patches == null && it.switchedIn.isEmpty() }
-                }
+        override fun schedule(logIndent: Int, evalScope: EvalScope) {
+            logDuration(logIndent, "MuxPromptPatchNode.schedule") {
+                patchData = upstream.getPushEvent(currentLogIndent, evalScope)
+                this@MuxPromptNode.schedule(evalScope)
             }
-        )
+        }
 
-    val eval = TFlowCheap { downstream ->
-        moving.activate(evalScope = this, downstream)?.let { (connection, needsEval) ->
-            val evalNode = MuxPromptEvalNode(connection.directUpstream)
-            ActivationResult(
-                connection = NodeConnection(evalNode, connection.schedulerUpstream),
-                needsEval = needsEval,
+        override fun adjustDirectUpstream(scheduler: Scheduler, oldDepth: Int, newDepth: Int) {
+            this@MuxPromptNode.adjustDirectUpstream(scheduler, oldDepth, newDepth)
+        }
+
+        override fun moveIndirectUpstreamToDirect(
+            scheduler: Scheduler,
+            oldIndirectDepth: Int,
+            oldIndirectSet: Set<MuxDeferredNode<*, *, *>>,
+            newDirectDepth: Int,
+        ) {
+            this@MuxPromptNode.moveIndirectUpstreamToDirect(
+                scheduler,
+                oldIndirectDepth,
+                oldIndirectSet,
+                newDirectDepth,
             )
         }
+
+        override fun adjustIndirectUpstream(
+            scheduler: Scheduler,
+            oldDepth: Int,
+            newDepth: Int,
+            removals: Set<MuxDeferredNode<*, *, *>>,
+            additions: Set<MuxDeferredNode<*, *, *>>,
+        ) {
+            this@MuxPromptNode.adjustIndirectUpstream(
+                scheduler,
+                oldDepth,
+                newDepth,
+                removals,
+                additions,
+            )
+        }
+
+        override fun moveDirectUpstreamToIndirect(
+            scheduler: Scheduler,
+            oldDirectDepth: Int,
+            newIndirectDepth: Int,
+            newIndirectSet: Set<MuxDeferredNode<*, *, *>>,
+        ) {
+            this@MuxPromptNode.moveDirectUpstreamToIndirect(
+                scheduler,
+                oldDirectDepth,
+                newIndirectDepth,
+                newIndirectSet,
+            )
+        }
+
+        override fun removeDirectUpstream(scheduler: Scheduler, depth: Int) {
+            this@MuxPromptNode.removeDirectPatchNode(scheduler, depth)
+        }
+
+        override fun removeIndirectUpstream(
+            scheduler: Scheduler,
+            depth: Int,
+            indirectSet: Set<MuxDeferredNode<*, *, *>>,
+        ) {
+            this@MuxPromptNode.removeIndirectPatchNode(scheduler, depth, indirectSet)
+        }
     }
-    return eval.cached()
+}
+
+internal inline fun <A> switchPromptImplSingle(
+    crossinline getStorage: EvalScope.() -> EventsImpl<A>,
+    crossinline getPatches: EvalScope.() -> EventsImpl<EventsImpl<A>>,
+): EventsImpl<A> {
+    val switchPromptImpl =
+        switchPromptImpl(
+            getStorage = { singleOf(getStorage()).asIterable() },
+            getPatches = {
+                mapImpl(getPatches) { newEvents, _ ->
+                    singleOf(Maybe.present(newEvents)).asIterable()
+                }
+            },
+            storeFactory = SingletonMapK.Factory(),
+        )
+    return mapImpl({ switchPromptImpl }) { map, logIndent ->
+        map.asSingle().getValue(Unit).getPushEvent(logIndent, this)
+    }
+}
+
+internal fun <W, K, V> switchPromptImpl(
+    name: String? = null,
+    getStorage: EvalScope.() -> Iterable<Map.Entry<K, EventsImpl<V>>>,
+    getPatches: EvalScope.() -> EventsImpl<Iterable<Map.Entry<K, Maybe<EventsImpl<V>>>>>,
+    storeFactory: MutableMapK.Factory<W, K>,
+): EventsImpl<MuxResult<W, K, V>> =
+    MuxLifecycle(MuxPromptActivator(name, getStorage, storeFactory, getPatches))
+
+private class MuxPromptActivator<W, K, V>(
+    private val name: String?,
+    private val getStorage: EvalScope.() -> Iterable<Map.Entry<K, EventsImpl<V>>>,
+    private val storeFactory: MutableMapK.Factory<W, K>,
+    private val getPatches: EvalScope.() -> EventsImpl<Iterable<Map.Entry<K, Maybe<EventsImpl<V>>>>>,
+) : MuxActivator<W, K, V> {
+    override fun activate(
+        evalScope: EvalScope,
+        lifecycle: MuxLifecycle<W, K, V>,
+    ): Pair<MuxNode<W, K, V>, (() -> Unit)?>? {
+        // Initialize mux node and switched-in connections.
+        val movingNode =
+            MuxPromptNode(name, lifecycle, this, storeFactory).apply {
+                initializeUpstream(evalScope, getStorage, storeFactory)
+                // Setup patches connection
+                val patchNode = PatchNode()
+                getPatches(evalScope)
+                    .activate(evalScope = evalScope, downstream = patchNode.schedulable)
+                    ?.let { (conn, needsEval) ->
+                        patchNode.upstream = conn
+                        patches = patchNode
+                        if (needsEval) {
+                            patchData = conn.getPushEvent(0, evalScope)
+                        }
+                    }
+                // Update depth based on all initial switched-in nodes.
+                initializeDepth()
+                // Update depth based on patches node.
+                patches?.upstream?.let { conn ->
+                    if (conn.depthTracker.snapshotIsDirect) {
+                        depthTracker.addDirectUpstream(
+                            oldDepth = null,
+                            newDepth = conn.depthTracker.snapshotDirectDepth,
+                        )
+                    } else {
+                        depthTracker.addIndirectUpstream(
+                            oldDepth = null,
+                            newDepth = conn.depthTracker.snapshotIndirectDepth,
+                        )
+                        depthTracker.updateIndirectRoots(
+                            additions = conn.depthTracker.snapshotIndirectRoots,
+                            butNot = null,
+                        )
+                    }
+                }
+                // Reset all depth adjustments, since no downstream has been notified
+                depthTracker.reset()
+            }
+
+        // Schedule for evaluation if any switched-in nodes or the patches node have
+        // already emitted within this transaction.
+        if (movingNode.patchData != null || movingNode.upstreamData.isNotEmpty()) {
+            movingNode.schedule(evalScope)
+        }
+
+        return if (movingNode.patches == null && movingNode.switchedIn.isEmpty()) {
+            null
+        } else {
+            movingNode to null
+        }
+    }
 }
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Network.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Network.kt
index 599b186..6e86dd1 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Network.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Network.kt
@@ -16,23 +16,22 @@
 
 package com.android.systemui.kairos.internal
 
-import com.android.systemui.kairos.TState
+import com.android.systemui.kairos.State
 import com.android.systemui.kairos.internal.util.HeteroMap
-import com.android.systemui.kairos.util.Just
+import com.android.systemui.kairos.internal.util.logDuration
+import com.android.systemui.kairos.internal.util.logLn
 import com.android.systemui.kairos.util.Maybe
-import com.android.systemui.kairos.util.just
-import com.android.systemui.kairos.util.none
-import java.util.concurrent.ConcurrentHashMap
-import java.util.concurrent.ConcurrentLinkedDeque
-import java.util.concurrent.ConcurrentLinkedQueue
+import com.android.systemui.kairos.util.Maybe.Present
 import java.util.concurrent.atomic.AtomicLong
 import kotlin.coroutines.ContinuationInterceptor
+import kotlin.time.measureTime
 import kotlinx.coroutines.CompletableDeferred
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Deferred
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.channels.Channel
 import kotlinx.coroutines.coroutineScope
+import kotlinx.coroutines.isActive
 import kotlinx.coroutines.job
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.sync.Mutex
@@ -52,32 +51,41 @@
     override val network
         get() = this
 
-    override val compactor = SchedulerImpl()
-    override val scheduler = SchedulerImpl()
-    override val transactionStore = HeteroMap()
+    override val compactor = SchedulerImpl {
+        if (it.markedForCompaction) false
+        else {
+            it.markedForCompaction = true
+            true
+        }
+    }
+    override val scheduler = SchedulerImpl {
+        if (it.markedForEvaluation) false
+        else {
+            it.markedForEvaluation = true
+            true
+        }
+    }
+    override val transactionStore = TransactionStore()
 
-    private val stateWrites = ConcurrentLinkedQueue<TStateSource<*>>()
-    private val outputsByDispatcher =
-        ConcurrentHashMap<ContinuationInterceptor, ConcurrentLinkedQueue<Output<*>>>()
-    private val muxMovers = ConcurrentLinkedQueue<MuxDeferredNode<*, *>>()
-    private val deactivations = ConcurrentLinkedDeque<PushNode<*>>()
-    private val outputDeactivations = ConcurrentLinkedQueue<Output<*>>()
+    private val stateWrites = ArrayDeque<StateSource<*>>()
+    private val outputsByDispatcher = HashMap<ContinuationInterceptor, ArrayDeque<Output<*>>>()
+    private val muxMovers = ArrayDeque<MuxDeferredNode<*, *, *>>()
+    private val deactivations = ArrayDeque<PushNode<*>>()
+    private val outputDeactivations = ArrayDeque<Output<*>>()
     private val transactionMutex = Mutex()
     private val inputScheduleChan = Channel<ScheduledAction<*>>()
 
     override fun scheduleOutput(output: Output<*>) {
         val continuationInterceptor =
             output.context[ContinuationInterceptor] ?: Dispatchers.Unconfined
-        outputsByDispatcher
-            .computeIfAbsent(continuationInterceptor) { ConcurrentLinkedQueue() }
-            .add(output)
+        outputsByDispatcher.computeIfAbsent(continuationInterceptor) { ArrayDeque() }.add(output)
     }
 
-    override fun scheduleMuxMover(muxMover: MuxDeferredNode<*, *>) {
+    override fun scheduleMuxMover(muxMover: MuxDeferredNode<*, *, *>) {
         muxMovers.add(muxMover)
     }
 
-    override fun schedule(state: TStateSource<*>) {
+    override fun schedule(state: StateSource<*>) {
         stateWrites.add(state)
     }
 
@@ -89,7 +97,7 @@
         outputDeactivations.add(output)
     }
 
-    /** Listens for external events and starts FRP transactions. Runs forever. */
+    /** Listens for external events and starts Kairos transactions. Runs forever. */
     suspend fun runInputScheduler() {
         val actions = mutableListOf<ScheduledAction<*>>()
         for (first in inputScheduleChan) {
@@ -101,18 +109,37 @@
                 actions.add(func)
             }
             transactionMutex.withLock {
-                // Run all actions
-                evalScope {
-                    for (action in actions) {
-                        launch { action.started(evalScope = this@evalScope) }
+                val e = epoch
+                val duration = measureTime {
+                    logLn(0, "===starting transaction $e===")
+                    try {
+                        logDuration(1, "init actions") {
+                            // Run all actions
+                            evalScope {
+                                for (action in actions) {
+                                    action.started(evalScope = this@evalScope)
+                                }
+                            }
+                        }
+                        // Step through the network
+                        doTransaction(1)
+                    } catch (e: Exception) {
+                        // Signal failure
+                        while (actions.isNotEmpty()) {
+                            actions.removeLast().fail(e)
+                        }
+                        // re-throw, cancelling this coroutine
+                        throw e
+                    } finally {
+                        logDuration(1, "signal completions") {
+                            // Signal completion
+                            while (actions.isNotEmpty()) {
+                                actions.removeLast().completed()
+                            }
+                        }
                     }
                 }
-                // Step through the network
-                doTransaction()
-                // Signal completion
-                while (actions.isNotEmpty()) {
-                    actions.removeLast().completed()
-                }
+                logLn(0, "===transaction $e took $duration===")
             }
         }
     }
@@ -120,6 +147,10 @@
     /** Evaluates [block] inside of a new transaction when the network is ready. */
     fun <R> transaction(reason: String, block: suspend EvalScope.() -> R): Deferred<R> =
         CompletableDeferred<R>(parent = coroutineScope.coroutineContext.job).also { onResult ->
+            if (!coroutineScope.isActive) {
+                onResult.cancel()
+                return@also
+            }
             val job =
                 coroutineScope.launch {
                     inputScheduleChan.send(
@@ -129,33 +160,47 @@
             onResult.invokeOnCompletion { job.cancel() }
         }
 
-    suspend fun <R> evalScope(block: suspend EvalScope.() -> R): R = deferScope {
+    inline fun <R> evalScope(block: EvalScope.() -> R): R = deferScope {
         block(EvalScopeImpl(this@Network, this))
     }
 
-    /** Performs a transactional update of the FRP network. */
-    private suspend fun doTransaction() {
+    /** Performs a transactional update of the Kairos network. */
+    private suspend fun doTransaction(logIndent: Int) {
         // Traverse network, then run outputs
-        do {
-            scheduler.drainEval(this)
-        } while (evalScope { evalOutputs(this) })
+        logDuration(logIndent, "traverse network") {
+            do {
+                val numNodes =
+                    logDuration("drainEval") { scheduler.drainEval(currentLogIndent, this@Network) }
+                logLn("drained $numNodes nodes")
+            } while (logDuration("evalOutputs") { evalScope { evalOutputs(this) } })
+        }
         // Update states
-        evalScope { evalStateWriters(this) }
-        transactionStore.clear()
-        // Perform deferred switches
-        evalScope { evalMuxMovers(this) }
-        // Compact depths
-        scheduler.drainCompact()
-        compactor.drainCompact()
-        // Deactivate nodes with no downstream
-        evalDeactivations()
+        logDuration(logIndent, "update states") {
+            evalScope { evalStateWriters(currentLogIndent, this) }
+        }
+        // Invalidate caches
+        // Note: this needs to occur before deferred switches
+        logDuration(logIndent, "clear store") { transactionStore.clear() }
         epoch++
+        // Perform deferred switches
+        logDuration(logIndent, "evalMuxMovers") {
+            evalScope { evalMuxMovers(currentLogIndent, this) }
+        }
+        // Compact depths
+        logDuration(logIndent, "compact") {
+            scheduler.drainCompact(currentLogIndent)
+            compactor.drainCompact(currentLogIndent)
+        }
+        // Deactivate nodes with no downstream
+        logDuration(logIndent, "deactivations") { evalDeactivations() }
     }
 
     /** Invokes all [Output]s that have received data within this transaction. */
     private suspend fun evalOutputs(evalScope: EvalScope): Boolean {
+        if (outputsByDispatcher.isEmpty()) {
+            return false
+        }
         // Outputs can enqueue other outputs, so we need two loops
-        if (outputsByDispatcher.isEmpty()) return false
         while (outputsByDispatcher.isNotEmpty()) {
             var launchedAny = false
             coroutineScope {
@@ -164,57 +209,50 @@
                         launchedAny = true
                         launch(key) {
                             while (outputs.isNotEmpty()) {
-                                val output = outputs.remove()
+                                val output = outputs.removeFirst()
                                 launch { output.visit(evalScope) }
                             }
                         }
                     }
                 }
             }
-            if (!launchedAny) outputsByDispatcher.clear()
+            if (!launchedAny) {
+                outputsByDispatcher.clear()
+            }
         }
         return true
     }
 
-    private suspend fun evalMuxMovers(evalScope: EvalScope) {
+    private fun evalMuxMovers(logIndent: Int, evalScope: EvalScope) {
         while (muxMovers.isNotEmpty()) {
-            coroutineScope {
-                val toMove = muxMovers.remove()
-                launch { toMove.performMove(evalScope) }
-            }
+            val toMove = muxMovers.removeFirst()
+            toMove.performMove(logIndent, evalScope)
         }
     }
 
-    /** Updates all [TState]es that have changed within this transaction. */
-    private suspend fun evalStateWriters(evalScope: EvalScope) {
-        coroutineScope {
-            while (stateWrites.isNotEmpty()) {
-                val latch = stateWrites.remove()
-                launch { latch.updateState(evalScope) }
-            }
+    /** Updates all [State]es that have changed within this transaction. */
+    private fun evalStateWriters(logIndent: Int, evalScope: EvalScope) {
+        while (stateWrites.isNotEmpty()) {
+            val latch = stateWrites.removeFirst()
+            latch.updateState(logIndent, evalScope)
         }
     }
 
-    private suspend fun evalDeactivations() {
-        coroutineScope {
-            launch {
-                while (deactivations.isNotEmpty()) {
-                    // traverse in reverse order
-                    //   - deactivations are added in depth-order during the node traversal phase
-                    //   - perform deactivations in reverse order, in case later ones propagate to
-                    //     earlier ones
-                    val toDeactivate = deactivations.removeLast()
-                    launch { toDeactivate.deactivateIfNeeded() }
-                }
-            }
-            while (outputDeactivations.isNotEmpty()) {
-                val toDeactivate = outputDeactivations.remove()
-                launch {
-                    toDeactivate.upstream?.removeDownstreamAndDeactivateIfNeeded(
-                        downstream = toDeactivate.schedulable
-                    )
-                }
-            }
+    private fun evalDeactivations() {
+        while (deactivations.isNotEmpty()) {
+            // traverse in reverse order
+            //   - deactivations are added in depth-order during the node traversal phase
+            //   - perform deactivations in reverse order, in case later ones propagate to
+            //     earlier ones
+            val toDeactivate = deactivations.removeLast()
+            toDeactivate.deactivateIfNeeded()
+        }
+
+        while (outputDeactivations.isNotEmpty()) {
+            val toDeactivate = outputDeactivations.removeFirst()
+            toDeactivate.upstream?.removeDownstreamAndDeactivateIfNeeded(
+                downstream = toDeactivate.schedulable
+            )
         }
         check(deactivations.isEmpty()) { "unexpected lingering deactivations" }
         check(outputDeactivations.isEmpty()) { "unexpected lingering output deactivations" }
@@ -226,21 +264,61 @@
     private val onResult: CompletableDeferred<T>? = null,
     private val onStartTransaction: suspend EvalScope.() -> T,
 ) {
-    private var result: Maybe<T> = none
+    private var result: Maybe<T> = Maybe.absent
 
     suspend fun started(evalScope: EvalScope) {
-        result = just(onStartTransaction(evalScope))
+        result = Maybe.present(onStartTransaction(evalScope))
+    }
+
+    fun fail(ex: Exception) {
+        result = Maybe.absent
+        onResult?.completeExceptionally(ex)
     }
 
     fun completed() {
         if (onResult != null) {
             when (val result = result) {
-                is Just -> onResult.complete(result.value)
+                is Present -> onResult.complete(result.value)
                 else -> {}
             }
         }
-        result = none
+        result = Maybe.absent
     }
 }
 
-internal typealias TransactionStore = HeteroMap
+internal class TransactionStore private constructor(private val storage: HeteroMap) {
+    constructor(capacity: Int) : this(HeteroMap(capacity))
+
+    constructor() : this(HeteroMap())
+
+    operator fun <A> get(key: HeteroMap.Key<A>): A =
+        storage.getOrError(key) { "no value for $key in this transaction" }
+
+    operator fun <A> set(key: HeteroMap.Key<A>, value: A) {
+        storage[key] = value
+    }
+
+    fun clear() = storage.clear()
+}
+
+internal class TransactionCache<A> {
+    private val key = object : HeteroMap.Key<A> {}
+    @Volatile
+    var epoch: Long = Long.MIN_VALUE
+        private set
+
+    fun getOrPut(evalScope: EvalScope, block: () -> A): A =
+        if (epoch < evalScope.epoch) {
+            epoch = evalScope.epoch
+            block().also { evalScope.transactionStore[key] = it }
+        } else {
+            evalScope.transactionStore[key]
+        }
+
+    fun put(evalScope: EvalScope, value: A) {
+        epoch = evalScope.epoch
+        evalScope.transactionStore[key] = value
+    }
+
+    fun getCurrentValue(evalScope: EvalScope): A = evalScope.transactionStore[key]
+}
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/NoScope.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/NoScope.kt
index fbd9689..f662f19 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/NoScope.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/NoScope.kt
@@ -16,32 +16,6 @@
 
 package com.android.systemui.kairos.internal
 
-import com.android.systemui.kairos.FrpScope
-import kotlin.coroutines.Continuation
-import kotlin.coroutines.CoroutineContext
-import kotlin.coroutines.EmptyCoroutineContext
-import kotlin.coroutines.coroutineContext
-import kotlin.coroutines.startCoroutine
-import kotlinx.coroutines.CompletableDeferred
-import kotlinx.coroutines.completeWith
-import kotlinx.coroutines.job
+import com.android.systemui.kairos.KairosScope
 
-internal object NoScope {
-    private object FrpScopeImpl : FrpScope
-
-    suspend fun <R> runInFrpScope(block: suspend FrpScope.() -> R): R {
-        val complete = CompletableDeferred<R>(coroutineContext.job)
-        block.startCoroutine(
-            FrpScopeImpl,
-            object : Continuation<R> {
-                override val context: CoroutineContext
-                    get() = EmptyCoroutineContext
-
-                override fun resumeWith(result: Result<R>) {
-                    complete.completeWith(result)
-                }
-            },
-        )
-        return complete.await()
-    }
-}
+internal object NoScope : KairosScope
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/NodeTypes.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/NodeTypes.kt
index 0002407..39b8bfe 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/NodeTypes.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/NodeTypes.kt
@@ -16,47 +16,45 @@
 
 package com.android.systemui.kairos.internal
 
-import com.android.systemui.kairos.util.Maybe
-
 /*
 Dmux
 Muxes + Branch
 */
 internal sealed interface SchedulableNode {
     /** schedule this node w/ given NodeEvalScope */
-    suspend fun schedule(evalScope: EvalScope)
+    fun schedule(logIndent: Int, evalScope: EvalScope)
 
-    suspend fun adjustDirectUpstream(scheduler: Scheduler, oldDepth: Int, newDepth: Int)
+    fun adjustDirectUpstream(scheduler: Scheduler, oldDepth: Int, newDepth: Int)
 
-    suspend fun moveIndirectUpstreamToDirect(
+    fun moveIndirectUpstreamToDirect(
         scheduler: Scheduler,
         oldIndirectDepth: Int,
-        oldIndirectSet: Set<MuxDeferredNode<*, *>>,
+        oldIndirectSet: Set<MuxDeferredNode<*, *, *>>,
         newDirectDepth: Int,
     )
 
-    suspend fun adjustIndirectUpstream(
+    fun adjustIndirectUpstream(
         scheduler: Scheduler,
         oldDepth: Int,
         newDepth: Int,
-        removals: Set<MuxDeferredNode<*, *>>,
-        additions: Set<MuxDeferredNode<*, *>>,
+        removals: Set<MuxDeferredNode<*, *, *>>,
+        additions: Set<MuxDeferredNode<*, *, *>>,
     )
 
-    suspend fun moveDirectUpstreamToIndirect(
+    fun moveDirectUpstreamToIndirect(
         scheduler: Scheduler,
         oldDirectDepth: Int,
         newIndirectDepth: Int,
-        newIndirectSet: Set<MuxDeferredNode<*, *>>,
+        newIndirectSet: Set<MuxDeferredNode<*, *, *>>,
     )
 
-    suspend fun removeIndirectUpstream(
+    fun removeIndirectUpstream(
         scheduler: Scheduler,
         depth: Int,
-        indirectSet: Set<MuxDeferredNode<*, *>>,
+        indirectSet: Set<MuxDeferredNode<*, *, *>>,
     )
 
-    suspend fun removeDirectUpstream(scheduler: Scheduler, depth: Int)
+    fun removeDirectUpstream(scheduler: Scheduler, depth: Int)
 }
 
 /*
@@ -68,7 +66,7 @@
      * will read from the cache, otherwise it will perform a full evaluation, even if invoked
      * multiple times within a transaction.
      */
-    suspend fun getPushEvent(evalScope: EvalScope): Maybe<A>
+    fun getPushEvent(logIndent: Int, evalScope: EvalScope): A
 }
 
 /*
@@ -76,19 +74,19 @@
  */
 internal sealed interface PushNode<A> : PullNode<A> {
 
-    suspend fun hasCurrentValue(transactionStore: TransactionStore): Boolean
+    fun hasCurrentValue(logIndent: Int, evalScope: EvalScope): Boolean
 
     val depthTracker: DepthTracker
 
-    suspend fun removeDownstream(downstream: Schedulable)
+    fun removeDownstream(downstream: Schedulable)
 
     /** called during cleanup phase */
-    suspend fun deactivateIfNeeded()
+    fun deactivateIfNeeded()
 
     /** called from mux nodes after severs */
-    suspend fun scheduleDeactivationIfNeeded(evalScope: EvalScope)
+    fun scheduleDeactivationIfNeeded(evalScope: EvalScope)
 
-    suspend fun addDownstream(downstream: Schedulable)
+    fun addDownstream(downstream: Schedulable)
 
-    suspend fun removeDownstreamAndDeactivateIfNeeded(downstream: Schedulable)
+    fun removeDownstreamAndDeactivateIfNeeded(downstream: Schedulable)
 }
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Output.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Output.kt
index a3af2d3..38d8cf7 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Output.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Output.kt
@@ -16,14 +16,13 @@
 
 package com.android.systemui.kairos.internal
 
-import com.android.systemui.kairos.util.Just
 import kotlin.coroutines.CoroutineContext
 import kotlin.coroutines.EmptyCoroutineContext
 
 internal class Output<A>(
     val context: CoroutineContext = EmptyCoroutineContext,
-    val onDeath: suspend () -> Unit = {},
-    val onEmit: suspend EvalScope.(A) -> Unit,
+    val onDeath: () -> Unit = {},
+    val onEmit: EvalScope.(A) -> Unit,
 ) {
 
     val schedulable = Schedulable.O(this)
@@ -34,26 +33,24 @@
     private object NoResult
 
     // invoked by network
-    suspend fun visit(evalScope: EvalScope) {
+    fun visit(evalScope: EvalScope) {
         val upstreamResult = result
         check(upstreamResult !== NoResult) { "output visited with null upstream result" }
-        result = null
+        result = NoResult
         @Suppress("UNCHECKED_CAST") evalScope.onEmit(upstreamResult as A)
     }
 
-    suspend fun kill() {
+    fun kill() {
         onDeath()
     }
 
-    suspend fun schedule(evalScope: EvalScope) {
-        val upstreamResult =
-            checkNotNull(upstream) { "output scheduled with null upstream" }.getPushEvent(evalScope)
-        if (upstreamResult is Just) {
-            result = upstreamResult.value
-            evalScope.scheduleOutput(this)
-        }
+    fun schedule(logIndent: Int, evalScope: EvalScope) {
+        result =
+            checkNotNull(upstream) { "output scheduled with null upstream" }
+                .getPushEvent(logIndent, evalScope)
+        evalScope.scheduleOutput(this)
     }
 }
 
-internal inline fun OneShot(crossinline onEmit: suspend EvalScope.() -> Unit): Output<Unit> =
+internal inline fun OneShot(crossinline onEmit: EvalScope.() -> Unit): Output<Unit> =
     Output<Unit>(onEmit = { onEmit() }).apply { result = Unit }
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/PullNodes.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/PullNodes.kt
index dac98e0..517e54f 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/PullNodes.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/PullNodes.kt
@@ -16,24 +16,24 @@
 
 package com.android.systemui.kairos.internal
 
-import com.android.systemui.kairos.internal.util.Key
-import com.android.systemui.kairos.util.Maybe
-import com.android.systemui.kairos.util.map
-import kotlinx.coroutines.CoroutineStart
-import kotlinx.coroutines.Deferred
+import com.android.systemui.kairos.internal.util.logDuration
 
-internal val neverImpl: TFlowImpl<Nothing> = TFlowCheap { null }
+internal val neverImpl: EventsImpl<Nothing> = EventsImplCheap { null }
 
-internal class MapNode<A, B>(val upstream: PullNode<A>, val transform: suspend EvalScope.(A) -> B) :
+internal class MapNode<A, B>(val upstream: PullNode<A>, val transform: EvalScope.(A, Int) -> B) :
     PullNode<B> {
-    override suspend fun getPushEvent(evalScope: EvalScope): Maybe<B> =
-        upstream.getPushEvent(evalScope).map { evalScope.transform(it) }
+    override fun getPushEvent(logIndent: Int, evalScope: EvalScope): B =
+        logDuration(logIndent, "MapNode.getPushEvent") {
+            val upstream =
+                logDuration("upstream event") { upstream.getPushEvent(currentLogIndent, evalScope) }
+            logDuration("transform") { evalScope.transform(upstream, currentLogIndent) }
+        }
 }
 
 internal inline fun <A, B> mapImpl(
-    crossinline upstream: suspend EvalScope.() -> TFlowImpl<A>,
-    noinline transform: suspend EvalScope.(A) -> B,
-): TFlowImpl<B> = TFlowCheap { downstream ->
+    crossinline upstream: EvalScope.() -> EventsImpl<A>,
+    noinline transform: EvalScope.(A, Int) -> B,
+): EventsImpl<B> = EventsImplCheap { downstream ->
     upstream().activate(evalScope = this, downstream)?.let { (connection, needsEval) ->
         ActivationResult(
             connection =
@@ -46,20 +46,29 @@
     }
 }
 
-internal class CachedNode<A>(val key: Key<Deferred<Maybe<A>>>, val upstream: PullNode<A>) :
-    PullNode<A> {
-    override suspend fun getPushEvent(evalScope: EvalScope): Maybe<A> {
-        val deferred =
-            evalScope.transactionStore.getOrPut(key) {
-                evalScope.deferAsync(CoroutineStart.LAZY) { upstream.getPushEvent(evalScope) }
-            }
-        return deferred.await()
-    }
+internal class CachedNode<A>(
+    private val transactionCache: TransactionCache<Lazy<A>>,
+    val upstream: PullNode<A>,
+) : PullNode<A> {
+    override fun getPushEvent(logIndent: Int, evalScope: EvalScope): A =
+        logDuration(logIndent, "CachedNode.getPushEvent") {
+            val deferred =
+                logDuration("CachedNode.getOrPut", false) {
+                    transactionCache.getOrPut(evalScope) {
+                        evalScope.deferAsync {
+                            logDuration("CachedNode.getUpstreamEvent") {
+                                upstream.getPushEvent(currentLogIndent, evalScope)
+                            }
+                        }
+                    }
+                }
+            logDuration("await") { deferred.value }
+        }
 }
 
-internal fun <A> TFlowImpl<A>.cached(): TFlowImpl<A> {
-    val key = object : Key<Deferred<Maybe<A>>> {}
-    return TFlowCheap {
+internal fun <A> EventsImpl<A>.cached(): EventsImpl<A> {
+    val key = TransactionCache<Lazy<A>>()
+    return EventsImplCheap { it ->
         activate(this, it)?.let { (connection, needsEval) ->
             ActivationResult(
                 connection =
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Scheduler.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Scheduler.kt
index c12ef6a..0529bcb 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Scheduler.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/Scheduler.kt
@@ -14,15 +14,10 @@
  * limitations under the License.
  */
 
-@file:OptIn(ExperimentalCoroutinesApi::class)
-
 package com.android.systemui.kairos.internal
 
-import java.util.concurrent.ConcurrentHashMap
-import java.util.concurrent.PriorityBlockingQueue
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.coroutineScope
-import kotlinx.coroutines.launch
+import com.android.systemui.kairos.internal.util.LogIndent
+import java.util.PriorityQueue
 
 internal interface Scheduler {
     fun schedule(depth: Int, node: MuxNode<*, *, *>)
@@ -30,12 +25,11 @@
     fun scheduleIndirect(indirectDepth: Int, node: MuxNode<*, *, *>)
 }
 
-internal class SchedulerImpl : Scheduler {
-    val enqueued = ConcurrentHashMap<MuxNode<*, *, *>, Any>()
-    val scheduledQ = PriorityBlockingQueue<Pair<Int, MuxNode<*, *, *>>>(16, compareBy { it.first })
+internal class SchedulerImpl(private val enqueue: (MuxNode<*, *, *>) -> Boolean) : Scheduler {
+    private val scheduledQ = PriorityQueue<Pair<Int, MuxNode<*, *, *>>>(compareBy { it.first })
 
     override fun schedule(depth: Int, node: MuxNode<*, *, *>) {
-        if (enqueued.putIfAbsent(node, node) == null) {
+        if (enqueue(node)) {
             scheduledQ.add(Pair(depth, node))
         }
     }
@@ -44,33 +38,52 @@
         schedule(Int.MIN_VALUE + indirectDepth, node)
     }
 
-    internal suspend fun drainEval(network: Network) {
-        drain { runStep ->
-            runStep { muxNode -> network.evalScope { muxNode.visit(this) } }
+    internal fun drainEval(logIndent: Int, network: Network): Int =
+        drain(logIndent) { runStep ->
+            runStep { muxNode ->
+                network.evalScope {
+                    muxNode.markedForEvaluation = false
+                    muxNode.visit(currentLogIndent, this)
+                }
+            }
             // If any visited MuxPromptNodes had their depths increased, eagerly propagate those
             // depth changes now before performing further network evaluation.
-            network.compactor.drainCompact()
+            val numNodes = network.compactor.drainCompact(currentLogIndent)
+            logLn("promptly compacted $numNodes nodes")
         }
-    }
 
-    internal suspend fun drainCompact() {
-        drain { runStep -> runStep { muxNode -> muxNode.visitCompact(scheduler = this) } }
-    }
+    internal fun drainCompact(logIndent: Int): Int =
+        drain(logIndent) { runStep ->
+            runStep { muxNode ->
+                muxNode.markedForCompaction = false
+                muxNode.visitCompact(scheduler = this@SchedulerImpl)
+            }
+        }
 
-    private suspend inline fun drain(
+    private inline fun drain(
+        logIndent: Int,
         crossinline onStep:
-            suspend (runStep: suspend (visit: suspend (MuxNode<*, *, *>) -> Unit) -> Unit) -> Unit
-    ): Unit = coroutineScope {
+            LogIndent.(
+                runStep: LogIndent.(visit: LogIndent.(MuxNode<*, *, *>) -> Unit) -> Unit
+            ) -> Unit,
+    ): Int {
+        var total = 0
         while (scheduledQ.isNotEmpty()) {
             val maxDepth = scheduledQ.peek()?.first ?: error("Unexpected empty scheduler")
-            onStep { visit -> runStep(maxDepth, visit) }
+            LogIndent(logIndent).onStep { visit ->
+                logDuration("step $maxDepth") {
+                    val subtotal = runStep(maxDepth) { visit(it) }
+                    logLn("visited $subtotal nodes")
+                    total += subtotal
+                }
+            }
         }
+        return total
     }
 
-    private suspend inline fun runStep(
-        maxDepth: Int,
-        crossinline visit: suspend (MuxNode<*, *, *>) -> Unit,
-    ) = coroutineScope {
+    private inline fun runStep(maxDepth: Int, crossinline visit: (MuxNode<*, *, *>) -> Unit): Int {
+        var total = 0
+        val toVisit = mutableListOf<MuxNode<*, *, *>>()
         while (scheduledQ.peek()?.first?.let { it <= maxDepth } == true) {
             val (d, node) = scheduledQ.remove()
             if (
@@ -79,11 +92,15 @@
             ) {
                 scheduledQ.add(node.depthTracker.dirty_directDepth to node)
             } else {
-                launch {
-                    enqueued.remove(node)
-                    visit(node)
-                }
+                total++
+                toVisit.add(node)
             }
         }
+
+        for (node in toVisit) {
+            visit(node)
+        }
+
+        return total
     }
 }
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/StateImpl.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/StateImpl.kt
new file mode 100644
index 0000000..da83258
--- /dev/null
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/StateImpl.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.systemui.kairos.internal
+
+import com.android.systemui.kairos.internal.store.ConcurrentHashMapK
+import com.android.systemui.kairos.internal.store.MutableArrayMapK
+import com.android.systemui.kairos.internal.store.MutableMapK
+import com.android.systemui.kairos.internal.store.StoreEntry
+import com.android.systemui.kairos.internal.util.hashString
+import com.android.systemui.kairos.util.Maybe
+
+internal open class StateImpl<out A>(
+    val name: String?,
+    val operatorName: String,
+    val changes: EventsImpl<A>,
+    val store: StateStore<A>,
+) {
+    fun getCurrentWithEpoch(evalScope: EvalScope): Pair<A, Long> =
+        store.getCurrentWithEpoch(evalScope)
+}
+
+internal sealed class StateDerived<A> : StateStore<A>() {
+
+    @Volatile
+    var invalidatedEpoch = Long.MIN_VALUE
+        private set
+
+    @Volatile
+    protected var validatedEpoch = Long.MIN_VALUE
+        private set
+
+    @Volatile
+    protected var cache: Any? = EmptyCache
+        private set
+
+    private val transactionCache = TransactionCache<Lazy<Pair<A, Long>>>()
+
+    override fun getCurrentWithEpoch(evalScope: EvalScope): Pair<A, Long> =
+        transactionCache.getOrPut(evalScope) { evalScope.deferAsync { pull(evalScope) } }.value
+
+    fun pull(evalScope: EvalScope): Pair<A, Long> {
+        @Suppress("UNCHECKED_CAST")
+        val result =
+            recalc(evalScope)?.let { (newValue, epoch) ->
+                newValue.also {
+                    if (epoch > validatedEpoch) {
+                        validatedEpoch = epoch
+                        if (cache != newValue) {
+                            cache = newValue
+                            invalidatedEpoch = epoch
+                        }
+                    }
+                }
+            } ?: (cache as A)
+        return result to invalidatedEpoch
+    }
+
+    fun getCachedUnsafe(): Maybe<A> {
+        @Suppress("UNCHECKED_CAST")
+        return if (cache == EmptyCache) Maybe.absent else Maybe.present(cache as A)
+    }
+
+    protected abstract fun recalc(evalScope: EvalScope): Pair<A, Long>?
+
+    fun setCacheFromPush(value: A, epoch: Long) {
+        cache = value
+        validatedEpoch = epoch + 1
+        invalidatedEpoch = epoch + 1
+    }
+
+    private data object EmptyCache
+}
+
+internal sealed class StateStore<out S> {
+    abstract fun getCurrentWithEpoch(evalScope: EvalScope): Pair<S, Long>
+}
+
+internal class StateSource<S>(init: Lazy<S>) : StateStore<S>() {
+    constructor(init: S) : this(CompletableLazy(init))
+
+    lateinit var upstreamConnection: NodeConnection<S>
+
+    // Note: Don't need to synchronize; we will never interleave reads and writes, since all writes
+    // are performed at the end of a network step, after any reads would have taken place.
+
+    @Volatile private var _current: Lazy<S> = init
+
+    @Volatile
+    var writeEpoch = 0L
+        private set
+
+    override fun getCurrentWithEpoch(evalScope: EvalScope): Pair<S, Long> =
+        _current.value to writeEpoch
+
+    /** called by network after eval phase has completed */
+    fun updateState(logIndent: Int, evalScope: EvalScope) {
+        // write the latch
+        _current = CompletableLazy(upstreamConnection.getPushEvent(logIndent, evalScope))
+        writeEpoch = evalScope.epoch + 1
+    }
+
+    override fun toString(): String = "StateImpl(current=$_current, writeEpoch=$writeEpoch)"
+
+    fun getStorageUnsafe(): Maybe<S> =
+        if (_current.isInitialized()) Maybe.present(_current.value) else Maybe.absent
+}
+
+internal fun <A> constState(name: String?, operatorName: String, init: A): StateImpl<A> =
+    StateImpl(name, operatorName, neverImpl, StateSource(init))
+
+internal inline fun <A> activatedStateSource(
+    name: String?,
+    operatorName: String,
+    evalScope: EvalScope,
+    crossinline getChanges: EvalScope.() -> EventsImpl<A>,
+    init: Lazy<A>,
+): StateImpl<A> {
+    val store = StateSource(init)
+    val calm: EventsImpl<A> =
+        filterImpl(getChanges) { new -> new != store.getCurrentWithEpoch(evalScope = this).first }
+    evalScope.scheduleOutput(
+        OneShot {
+            calm.activate(evalScope = this, downstream = Schedulable.S(store))?.let {
+                (connection, needsEval) ->
+                store.upstreamConnection = connection
+                if (needsEval) {
+                    schedule(store)
+                }
+            }
+        }
+    )
+    return StateImpl(name, operatorName, calm, store)
+}
+
+private inline fun <A> EventsImpl<A>.calm(state: StateDerived<A>): EventsImpl<A> =
+    filterImpl({ this@calm }) { new ->
+            val (current, _) = state.getCurrentWithEpoch(evalScope = this)
+            if (new != current) {
+                state.setCacheFromPush(new, epoch)
+                true
+            } else {
+                false
+            }
+        }
+        .cached()
+
+internal fun <A, B> mapStateImplCheap(
+    stateImpl: Init<StateImpl<A>>,
+    name: String?,
+    operatorName: String,
+    transform: EvalScope.(A) -> B,
+): StateImpl<B> =
+    StateImpl(
+        name = name,
+        operatorName = operatorName,
+        changes = mapImpl({ stateImpl.connect(this).changes }) { it, _ -> transform(it) },
+        store = DerivedMapCheap(stateImpl, transform),
+    )
+
+internal class DerivedMapCheap<A, B>(
+    val upstream: Init<StateImpl<A>>,
+    private val transform: EvalScope.(A) -> B,
+) : StateStore<B>() {
+
+    override fun getCurrentWithEpoch(evalScope: EvalScope): Pair<B, Long> {
+        val (a, epoch) = upstream.connect(evalScope).getCurrentWithEpoch(evalScope)
+        return evalScope.transform(a) to epoch
+    }
+
+    override fun toString(): String = "${this::class.simpleName}@$hashString"
+}
+
+internal fun <A, B> mapStateImpl(
+    stateImpl: InitScope.() -> StateImpl<A>,
+    name: String?,
+    operatorName: String,
+    transform: EvalScope.(A) -> B,
+): StateImpl<B> {
+    val store = DerivedMap(stateImpl, transform)
+    val mappedChanges =
+        mapImpl({ stateImpl().changes }) { it, _ -> transform(it) }.cached().calm(store)
+    return StateImpl(name, operatorName, mappedChanges, store)
+}
+
+internal class DerivedMap<A, B>(
+    val upstream: InitScope.() -> StateImpl<A>,
+    private val transform: EvalScope.(A) -> B,
+) : StateDerived<B>() {
+    override fun toString(): String = "${this::class.simpleName}@$hashString"
+
+    override fun recalc(evalScope: EvalScope): Pair<B, Long>? {
+        val (a, epoch) = evalScope.upstream().getCurrentWithEpoch(evalScope)
+        return if (epoch > validatedEpoch) {
+            evalScope.transform(a) to epoch
+        } else {
+            null
+        }
+    }
+}
+
+internal fun <A> flattenStateImpl(
+    stateImpl: InitScope.() -> StateImpl<StateImpl<A>>,
+    name: String?,
+    operator: String,
+): StateImpl<A> {
+    // emits the current value of the new inner state, when that state is emitted
+    val switchEvents =
+        mapImpl({ stateImpl().changes }) { newInner, _ -> newInner.getCurrentWithEpoch(this).first }
+    // emits the new value of the new inner state when that state is emitted, or
+    // falls back to the current value if a new state is *not* being emitted this
+    // transaction
+    val innerChanges =
+        mapImpl({ stateImpl().changes }) { newInner, _ ->
+            mergeNodes({ switchEvents }, { newInner.changes }) { _, new -> new }
+        }
+    val switchedChanges: EventsImpl<A> =
+        switchPromptImplSingle(
+            getStorage = { stateImpl().getCurrentWithEpoch(evalScope = this).first.changes },
+            getPatches = { innerChanges },
+        )
+    val store: DerivedFlatten<A> = DerivedFlatten(stateImpl)
+    return StateImpl(name, operator, switchedChanges.calm(store), store)
+}
+
+internal class DerivedFlatten<A>(val upstream: InitScope.() -> StateImpl<StateImpl<A>>) :
+    StateDerived<A>() {
+    override fun recalc(evalScope: EvalScope): Pair<A, Long> {
+        val (inner, epoch0) = evalScope.upstream().getCurrentWithEpoch(evalScope)
+        val (a, epoch1) = inner.getCurrentWithEpoch(evalScope)
+        return a to maxOf(epoch0, epoch1)
+    }
+
+    override fun toString(): String = "${this::class.simpleName}@$hashString"
+}
+
+@Suppress("NOTHING_TO_INLINE")
+internal inline fun <A, B> flatMapStateImpl(
+    noinline stateImpl: InitScope.() -> StateImpl<A>,
+    name: String?,
+    operatorName: String,
+    noinline transform: EvalScope.(A) -> StateImpl<B>,
+): StateImpl<B> {
+    val mapped = mapStateImpl(stateImpl, null, operatorName, transform)
+    return flattenStateImpl({ mapped }, name, operatorName)
+}
+
+internal fun <A, B, Z> zipStates(
+    name: String?,
+    operatorName: String,
+    l1: Init<StateImpl<A>>,
+    l2: Init<StateImpl<B>>,
+    transform: EvalScope.(A, B) -> Z,
+): StateImpl<Z> {
+    val zipped =
+        zipStateList(
+            null,
+            operatorName,
+            2,
+            init(null) { listOf(l1.connect(this), l2.connect(this)) },
+        )
+    return mapStateImpl({ zipped }, name, operatorName) {
+        @Suppress("UNCHECKED_CAST") transform(it[0] as A, it[1] as B)
+    }
+}
+
+internal fun <A, B, C, Z> zipStates(
+    name: String?,
+    operatorName: String,
+    l1: Init<StateImpl<A>>,
+    l2: Init<StateImpl<B>>,
+    l3: Init<StateImpl<C>>,
+    transform: EvalScope.(A, B, C) -> Z,
+): StateImpl<Z> {
+    val zipped =
+        zipStateList(
+            null,
+            operatorName,
+            3,
+            init(null) { listOf(l1.connect(this), l2.connect(this), l3.connect(this)) },
+        )
+    return mapStateImpl({ zipped }, name, operatorName) {
+        @Suppress("UNCHECKED_CAST") transform(it[0] as A, it[1] as B, it[2] as C)
+    }
+}
+
+internal fun <A, B, C, D, Z> zipStates(
+    name: String?,
+    operatorName: String,
+    l1: Init<StateImpl<A>>,
+    l2: Init<StateImpl<B>>,
+    l3: Init<StateImpl<C>>,
+    l4: Init<StateImpl<D>>,
+    transform: EvalScope.(A, B, C, D) -> Z,
+): StateImpl<Z> {
+    val zipped =
+        zipStateList(
+            null,
+            operatorName,
+            4,
+            init(null) {
+                listOf(l1.connect(this), l2.connect(this), l3.connect(this), l4.connect(this))
+            },
+        )
+    return mapStateImpl({ zipped }, name, operatorName) {
+        @Suppress("UNCHECKED_CAST") transform(it[0] as A, it[1] as B, it[2] as C, it[3] as D)
+    }
+}
+
+internal fun <A, B, C, D, E, Z> zipStates(
+    name: String?,
+    operatorName: String,
+    l1: Init<StateImpl<A>>,
+    l2: Init<StateImpl<B>>,
+    l3: Init<StateImpl<C>>,
+    l4: Init<StateImpl<D>>,
+    l5: Init<StateImpl<E>>,
+    transform: EvalScope.(A, B, C, D, E) -> Z,
+): StateImpl<Z> {
+    val zipped =
+        zipStateList(
+            null,
+            operatorName,
+            5,
+            init(null) {
+                listOf(
+                    l1.connect(this),
+                    l2.connect(this),
+                    l3.connect(this),
+                    l4.connect(this),
+                    l5.connect(this),
+                )
+            },
+        )
+    return mapStateImpl({ zipped }, name, operatorName) {
+        @Suppress("UNCHECKED_CAST")
+        transform(it[0] as A, it[1] as B, it[2] as C, it[3] as D, it[4] as E)
+    }
+}
+
+internal fun <K, V> zipStateMap(
+    name: String?,
+    operatorName: String,
+    numStates: Int,
+    states: Init<Map<K, StateImpl<V>>>,
+): StateImpl<Map<K, V>> =
+    zipStates(
+        name = name,
+        operatorName = operatorName,
+        numStates = numStates,
+        states = init(null) { states.connect(this).asIterable() },
+        storeFactory = ConcurrentHashMapK.Factory(),
+    )
+
+internal fun <V> zipStateList(
+    name: String?,
+    operatorName: String,
+    numStates: Int,
+    states: Init<List<StateImpl<V>>>,
+): StateImpl<List<V>> {
+    val zipped =
+        zipStates(
+            name = name,
+            operatorName = operatorName,
+            numStates = numStates,
+            states = init(name) { states.connect(this).asIterableWithIndex() },
+            storeFactory = MutableArrayMapK.Factory(),
+        )
+    // Like mapCheap, but with caching (or like map, but without the calm changes, as they are not
+    // necessary).
+    return StateImpl(
+        name = name,
+        operatorName = operatorName,
+        changes = mapImpl({ zipped.changes }) { arrayStore, _ -> arrayStore.values.toList() },
+        DerivedMap(upstream = { zipped }, transform = { arrayStore -> arrayStore.values.toList() }),
+    )
+}
+
+internal fun <W, K, A> zipStates(
+    name: String?,
+    operatorName: String,
+    numStates: Int,
+    states: Init<Iterable<Map.Entry<K, StateImpl<A>>>>,
+    storeFactory: MutableMapK.Factory<W, K>,
+): StateImpl<MutableMapK<W, K, A>> {
+    if (numStates == 0) {
+        return constState(name, operatorName, storeFactory.create(0))
+    }
+    val stateStore = DerivedZipped(numStates, states, storeFactory)
+    // No need for calm; invariant ensures that changes will only emit when there's a difference
+    val switchDeferredImpl =
+        switchDeferredImpl(
+            getStorage = {
+                states
+                    .connect(this)
+                    .asSequence()
+                    .map { (k, v) -> StoreEntry(k, v.changes) }
+                    .asIterable()
+            },
+            getPatches = { neverImpl },
+            storeFactory = storeFactory,
+        )
+    val changes =
+        mapImpl({ switchDeferredImpl }) { patch, logIndent ->
+                val muxStore = storeFactory.create<A>(numStates)
+                states.connect(this).forEach { (k, state) ->
+                    muxStore[k] =
+                        if (patch.contains(k)) {
+                            patch.getValue(k).getPushEvent(logIndent, evalScope = this@mapImpl)
+                        } else {
+                            state.getCurrentWithEpoch(evalScope = this@mapImpl).first
+                        }
+                }
+                // Read the current value so that it is cached in this transaction and won't be
+                // clobbered by the cache write
+                stateStore.getCurrentWithEpoch(evalScope = this)
+                muxStore.also { stateStore.setCacheFromPush(it, epoch) }
+            }
+            .cached()
+    return StateImpl(name, operatorName, changes, stateStore)
+}
+
+internal class DerivedZipped<W, K, A>(
+    private val upstreamSize: Int,
+    val upstream: Init<Iterable<Map.Entry<K, StateImpl<A>>>>,
+    private val storeFactory: MutableMapK.Factory<W, K>,
+) : StateDerived<MutableMapK<W, K, A>>() {
+    override fun recalc(evalScope: EvalScope): Pair<MutableMapK<W, K, A>, Long> {
+        var newEpoch = 0L
+        val store = storeFactory.create<A>(upstreamSize)
+        for ((key, value) in upstream.connect(evalScope)) {
+            val (a, epoch) = value.getCurrentWithEpoch(evalScope)
+            newEpoch = maxOf(newEpoch, epoch)
+            store[key] = a
+        }
+        return store to newEpoch
+    }
+
+    override fun toString(): String = "${this::class.simpleName}@$hashString"
+}
+
+@Suppress("NOTHING_TO_INLINE")
+internal inline fun <A> zipStates(
+    name: String?,
+    operatorName: String,
+    numStates: Int,
+    states: Init<List<StateImpl<A>>>,
+): StateImpl<List<A>> =
+    if (numStates <= 0) {
+        constState(name, operatorName, emptyList())
+    } else {
+        zipStateList(null, operatorName, numStates, states)
+    }
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/StateScopeImpl.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/StateScopeImpl.kt
index baf4101..53a704a 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/StateScopeImpl.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/StateScopeImpl.kt
@@ -16,242 +16,157 @@
 
 package com.android.systemui.kairos.internal
 
-import com.android.systemui.kairos.FrpDeferredValue
-import com.android.systemui.kairos.FrpStateScope
-import com.android.systemui.kairos.FrpStateful
-import com.android.systemui.kairos.FrpTransactionScope
-import com.android.systemui.kairos.GroupedTFlow
-import com.android.systemui.kairos.TFlow
-import com.android.systemui.kairos.TFlowInit
-import com.android.systemui.kairos.TFlowLoop
-import com.android.systemui.kairos.TState
-import com.android.systemui.kairos.TStateInit
-import com.android.systemui.kairos.emptyTFlow
+import com.android.systemui.kairos.DeferredValue
+import com.android.systemui.kairos.Events
+import com.android.systemui.kairos.EventsInit
+import com.android.systemui.kairos.EventsLoop
+import com.android.systemui.kairos.GroupedEvents
+import com.android.systemui.kairos.Incremental
+import com.android.systemui.kairos.IncrementalInit
+import com.android.systemui.kairos.State
+import com.android.systemui.kairos.StateInit
+import com.android.systemui.kairos.StateScope
+import com.android.systemui.kairos.Stateful
+import com.android.systemui.kairos.emptyEvents
 import com.android.systemui.kairos.groupByKey
 import com.android.systemui.kairos.init
-import com.android.systemui.kairos.internal.util.mapValuesParallel
 import com.android.systemui.kairos.mapCheap
 import com.android.systemui.kairos.merge
-import com.android.systemui.kairos.switch
+import com.android.systemui.kairos.switchEvents
 import com.android.systemui.kairos.util.Maybe
 import com.android.systemui.kairos.util.map
-import kotlin.coroutines.Continuation
-import kotlin.coroutines.CoroutineContext
-import kotlin.coroutines.EmptyCoroutineContext
-import kotlin.coroutines.startCoroutine
-import kotlinx.coroutines.CompletableDeferred
-import kotlinx.coroutines.Deferred
-import kotlinx.coroutines.completeWith
-import kotlinx.coroutines.job
 
-internal class StateScopeImpl(val evalScope: EvalScope, override val endSignal: TFlow<Any>) :
-    StateScope, EvalScope by evalScope {
+internal class StateScopeImpl(val evalScope: EvalScope, val endSignalLazy: Lazy<Events<Any>>) :
+    InternalStateScope, EvalScope by evalScope {
 
-    private val endSignalOnce: TFlow<Any> = endSignal.nextOnlyInternal("StateScope.endSignal")
+    override val endSignal: Events<Any> by endSignalLazy
 
-    private fun <A> TFlow<A>.truncateToScope(operatorName: String): TFlow<A> =
-        if (endSignalOnce === emptyTFlow) {
-            this
-        } else {
-            endSignalOnce.mapCheap { emptyTFlow }.toTStateInternal(operatorName, this).switch()
-        }
-
-    private fun <A> TFlow<A>.nextOnlyInternal(operatorName: String): TFlow<A> =
-        if (this === emptyTFlow) {
-            this
-        } else {
-            TFlowLoop<A>().apply {
-                loopback =
-                    mapCheap { emptyTFlow }
-                        .toTStateInternal(operatorName, this@nextOnlyInternal)
-                        .switch()
-            }
-        }
-
-    private fun <A> TFlow<A>.toTStateInternal(operatorName: String, init: A): TState<A> =
-        toTStateInternalDeferred(operatorName, CompletableDeferred(init))
-
-    private fun <A> TFlow<A>.toTStateInternalDeferred(
-        operatorName: String,
-        init: Deferred<A>,
-    ): TState<A> {
-        val changes = this@toTStateInternalDeferred
-        val name = operatorName
-        val impl =
-            mkState(name, operatorName, evalScope, { changes.init.connect(evalScope = this) }, init)
-        return TStateInit(constInit(name, impl))
+    override val endSignalOnce: Events<Any> by lazy {
+        endSignal.nextOnlyInternal("StateScope.endSignal")
     }
 
-    private fun <R> deferredInternal(block: suspend FrpStateScope.() -> R): FrpDeferredValue<R> =
-        FrpDeferredValue(deferAsync { runInStateScope(block) })
+    override fun <A> deferredStateScope(block: StateScope.() -> A): DeferredValue<A> =
+        DeferredValue(deferAsync { block() })
 
-    private fun <A> TFlow<A>.toTStateDeferredInternal(
-        initialValue: FrpDeferredValue<A>
-    ): TState<A> {
-        val operatorName = "toTStateDeferred"
+    override fun <A> Events<A>.holdStateDeferred(initialValue: DeferredValue<A>): State<A> {
+        val operatorName = "holdStateDeferred"
         // Ensure state is only collected until the end of this scope
         return truncateToScope(operatorName)
-            .toTStateInternalDeferred(operatorName, initialValue.unwrapped)
+            .holdStateInternalDeferred(operatorName, initialValue.unwrapped)
     }
 
-    private fun <K : Any, V> TFlow<Map<K, Maybe<TFlow<V>>>>.mergeIncrementallyInternal(
-        storage: TState<Map<K, TFlow<V>>>
-    ): TFlow<Map<K, V>> {
-        val name = "mergeIncrementally"
-        return TFlowInit(
+    override fun <K, V> Events<Map<K, Maybe<V>>>.foldStateMapIncrementally(
+        initialValues: DeferredValue<Map<K, V>>
+    ): Incremental<K, V> {
+        val operatorName = "foldStateMapIncrementally"
+        val name = operatorName
+        return IncrementalInit(
             constInit(
-                name,
-                switchDeferredImpl(
-                    getStorage = {
-                        storage.init
-                            .connect(this)
-                            .getCurrentWithEpoch(this)
-                            .first
-                            .mapValuesParallel { (_, flow) -> flow.init.connect(this) }
-                    },
-                    getPatches = {
-                        mapImpl({ init.connect(this) }) { patch ->
-                            patch.mapValuesParallel { (_, m) ->
-                                m.map { flow -> flow.init.connect(this) }
-                            }
-                        }
-                    },
+                operatorName,
+                activatedIncremental(
+                    name,
+                    operatorName,
+                    evalScope,
+                    { init.connect(this) },
+                    initialValues.unwrapped,
                 ),
             )
         )
     }
 
-    private fun <K : Any, V> TFlow<Map<K, Maybe<TFlow<V>>>>.mergeIncrementallyPromptInternal(
-        storage: TState<Map<K, TFlow<V>>>
-    ): TFlow<Map<K, V>> {
-        val name = "mergeIncrementallyPrompt"
-        return TFlowInit(
-            constInit(
-                name,
-                switchPromptImpl(
-                    getStorage = {
-                        storage.init
-                            .connect(this)
-                            .getCurrentWithEpoch(this)
-                            .first
-                            .mapValuesParallel { (_, flow) -> flow.init.connect(this) }
-                    },
-                    getPatches = {
-                        mapImpl({ init.connect(this) }) { patch ->
-                            patch.mapValuesParallel { (_, m) ->
-                                m.map { flow -> flow.init.connect(this) }
-                            }
-                        }
-                    },
-                ),
-            )
-        )
-    }
-
-    private fun <K, A, B> TFlow<Map<K, Maybe<FrpStateful<A>>>>.applyLatestStatefulForKeyInternal(
-        init: FrpDeferredValue<Map<K, FrpStateful<B>>>,
+    override fun <K, A, B> Events<Map<K, Maybe<Stateful<A>>>>.applyLatestStatefulForKey(
+        init: DeferredValue<Map<K, Stateful<B>>>,
         numKeys: Int?,
-    ): Pair<TFlow<Map<K, Maybe<A>>>, FrpDeferredValue<Map<K, B>>> {
-        val eventsByKey: GroupedTFlow<K, Maybe<FrpStateful<A>>> = groupByKey(numKeys)
-        val initOut: Deferred<Map<K, B>> = deferAsync {
-            init.unwrapped.await().mapValuesParallel { (k, stateful) ->
-                val newEnd = with(frpScope) { eventsByKey[k].skipNext() }
+    ): Pair<Events<Map<K, Maybe<A>>>, DeferredValue<Map<K, B>>> {
+        val eventsByKey: GroupedEvents<K, Maybe<Stateful<A>>> = groupByKey(numKeys)
+        val initOut: Lazy<Map<K, B>> = deferAsync {
+            init.unwrapped.value.mapValues { (k, stateful) ->
+                val newEnd = eventsByKey[k]
                 val newScope = childStateScope(newEnd)
-                newScope.runInStateScope(stateful)
+                newScope.stateful()
             }
         }
-        val changesNode: TFlowImpl<Map<K, Maybe<A>>> =
-            mapImpl(
-                upstream = { this@applyLatestStatefulForKeyInternal.init.connect(evalScope = this) }
-            ) { upstreamMap ->
-                upstreamMap.mapValuesParallel { (k: K, ma: Maybe<FrpStateful<A>>) ->
+        val changesNode: EventsImpl<Map<K, Maybe<A>>> =
+            mapImpl(upstream = { this@applyLatestStatefulForKey.init.connect(evalScope = this) }) {
+                upstreamMap,
+                _ ->
+                upstreamMap.mapValues { (k: K, ma: Maybe<Stateful<A>>) ->
                     reenterStateScope(this@StateScopeImpl).run {
                         ma.map { stateful ->
-                            val newEnd = with(frpScope) { eventsByKey[k].skipNext() }
+                            val newEnd = eventsByKey[k].skipNext()
                             val newScope = childStateScope(newEnd)
-                            newScope.runInStateScope(stateful)
+                            newScope.stateful()
                         }
                     }
                 }
             }
         val operatorName = "applyLatestStatefulForKey"
         val name = operatorName
-        val changes: TFlow<Map<K, Maybe<A>>> = TFlowInit(constInit(name, changesNode.cached()))
-        return changes to FrpDeferredValue(initOut)
+        val changes: Events<Map<K, Maybe<A>>> = EventsInit(constInit(name, changesNode.cached()))
+        return changes to DeferredValue(initOut)
     }
 
-    private fun <A> TFlow<FrpStateful<A>>.observeStatefulsInternal(): TFlow<A> {
-        val operatorName = "observeStatefuls"
+    override fun <A> Events<Stateful<A>>.applyStatefuls(): Events<A> {
+        val operatorName = "applyStatefuls"
         val name = operatorName
-        return TFlowInit(
+        return EventsInit(
             constInit(
                 name,
-                mapImpl(
-                        upstream = { this@observeStatefulsInternal.init.connect(evalScope = this) }
-                    ) { stateful ->
-                        reenterStateScope(outerScope = this@StateScopeImpl)
-                            .runInStateScope(stateful)
+                mapImpl(upstream = { this@applyStatefuls.init.connect(evalScope = this) }) {
+                        stateful,
+                        _ ->
+                        reenterStateScope(outerScope = this@StateScopeImpl).stateful()
                     }
                     .cached(),
             )
         )
     }
 
-    override val frpScope: FrpStateScope = FrpStateScopeImpl()
+    override fun childStateScope(newEnd: Events<Any>) =
+        StateScopeImpl(evalScope, lazy { merge(newEnd, endSignal) })
 
-    private inner class FrpStateScopeImpl :
-        FrpStateScope, FrpTransactionScope by evalScope.frpScope {
-
-        override fun <A> deferredStateScope(
-            block: suspend FrpStateScope.() -> A
-        ): FrpDeferredValue<A> = deferredInternal(block)
-
-        override fun <A> TFlow<A>.holdDeferred(initialValue: FrpDeferredValue<A>): TState<A> =
-            toTStateDeferredInternal(initialValue)
-
-        override fun <K : Any, V> TFlow<Map<K, Maybe<TFlow<V>>>>.mergeIncrementally(
-            initialTFlows: FrpDeferredValue<Map<K, TFlow<V>>>
-        ): TFlow<Map<K, V>> {
-            val storage: TState<Map<K, TFlow<V>>> = foldMapIncrementally(initialTFlows)
-            return mergeIncrementallyInternal(storage)
+    private fun <A> Events<A>.truncateToScope(operatorName: String): Events<A> =
+        if (endSignalOnce === emptyEvents) {
+            this
+        } else {
+            endSignalOnce
+                .mapCheap { emptyEvents }
+                .holdStateInternal(operatorName, this)
+                .switchEvents()
         }
 
-        override fun <K : Any, V> TFlow<Map<K, Maybe<TFlow<V>>>>.mergeIncrementallyPromptly(
-            initialTFlows: FrpDeferredValue<Map<K, TFlow<V>>>
-        ): TFlow<Map<K, V>> {
-            val storage: TState<Map<K, TFlow<V>>> = foldMapIncrementally(initialTFlows)
-            return mergeIncrementallyPromptInternal(storage)
+    private fun <A> Events<A>.nextOnlyInternal(operatorName: String): Events<A> =
+        if (this === emptyEvents) {
+            this
+        } else {
+            EventsLoop<A>().apply {
+                loopback =
+                    mapCheap { emptyEvents }
+                        .holdStateInternal(operatorName, this@nextOnlyInternal)
+                        .switchEvents()
+            }
         }
 
-        override fun <K, A, B> TFlow<Map<K, Maybe<FrpStateful<A>>>>.applyLatestStatefulForKey(
-            init: FrpDeferredValue<Map<K, FrpStateful<B>>>,
-            numKeys: Int?,
-        ): Pair<TFlow<Map<K, Maybe<A>>>, FrpDeferredValue<Map<K, B>>> =
-            applyLatestStatefulForKeyInternal(init, numKeys)
+    private fun <A> Events<A>.holdStateInternal(operatorName: String, init: A): State<A> =
+        holdStateInternalDeferred(operatorName, CompletableLazy(init))
 
-        override fun <A> TFlow<FrpStateful<A>>.applyStatefuls(): TFlow<A> =
-            observeStatefulsInternal()
+    private fun <A> Events<A>.holdStateInternalDeferred(
+        operatorName: String,
+        init: Lazy<A>,
+    ): State<A> {
+        val changes = this@holdStateInternalDeferred
+        val name = operatorName
+        val impl =
+            activatedStateSource(
+                name,
+                operatorName,
+                evalScope,
+                { changes.init.connect(evalScope = this) },
+                init,
+            )
+        return StateInit(constInit(name, impl))
     }
-
-    override suspend fun <R> runInStateScope(block: suspend FrpStateScope.() -> R): R {
-        val complete = CompletableDeferred<R>(parent = coroutineContext.job)
-        block.startCoroutine(
-            frpScope,
-            object : Continuation<R> {
-                override val context: CoroutineContext
-                    get() = EmptyCoroutineContext
-
-                override fun resumeWith(result: Result<R>) {
-                    complete.completeWith(result)
-                }
-            },
-        )
-        return complete.await()
-    }
-
-    override fun childStateScope(newEnd: TFlow<Any>) =
-        StateScopeImpl(evalScope, merge(newEnd, endSignal))
 }
 
 private fun EvalScope.reenterStateScope(outerScope: StateScopeImpl) =
-    StateScopeImpl(evalScope = this, endSignal = outerScope.endSignal)
+    StateScopeImpl(evalScope = this, endSignalLazy = outerScope.endSignalLazy)
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/TFlowImpl.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/TFlowImpl.kt
deleted file mode 100644
index b904b48..0000000
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/TFlowImpl.kt
+++ /dev/null
@@ -1,62 +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.kairos.internal
-
-import com.android.systemui.kairos.util.Maybe
-
-/* Initialized TFlow */
-internal fun interface TFlowImpl<out A> {
-    suspend fun activate(evalScope: EvalScope, downstream: Schedulable): ActivationResult<A>?
-}
-
-internal data class ActivationResult<out A>(
-    val connection: NodeConnection<A>,
-    val needsEval: Boolean,
-)
-
-internal inline fun <A> TFlowCheap(crossinline cheap: CheapNodeSubscribe<A>) =
-    TFlowImpl { scope, ds ->
-        scope.cheap(ds)
-    }
-
-internal typealias CheapNodeSubscribe<A> =
-    suspend EvalScope.(downstream: Schedulable) -> ActivationResult<A>?
-
-internal data class NodeConnection<out A>(
-    val directUpstream: PullNode<A>,
-    val schedulerUpstream: PushNode<*>,
-)
-
-internal suspend fun <A> NodeConnection<A>.hasCurrentValue(
-    transactionStore: TransactionStore
-): Boolean = schedulerUpstream.hasCurrentValue(transactionStore)
-
-internal suspend fun <A> NodeConnection<A>.removeDownstreamAndDeactivateIfNeeded(
-    downstream: Schedulable
-) = schedulerUpstream.removeDownstreamAndDeactivateIfNeeded(downstream)
-
-internal suspend fun <A> NodeConnection<A>.scheduleDeactivationIfNeeded(evalScope: EvalScope) =
-    schedulerUpstream.scheduleDeactivationIfNeeded(evalScope)
-
-internal suspend fun <A> NodeConnection<A>.removeDownstream(downstream: Schedulable) =
-    schedulerUpstream.removeDownstream(downstream)
-
-internal suspend fun <A> NodeConnection<A>.getPushEvent(evalScope: EvalScope): Maybe<A> =
-    directUpstream.getPushEvent(evalScope)
-
-internal val <A> NodeConnection<A>.depthTracker: DepthTracker
-    get() = schedulerUpstream.depthTracker
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/TStateImpl.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/TStateImpl.kt
deleted file mode 100644
index c68b4c3..0000000
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/TStateImpl.kt
+++ /dev/null
@@ -1,399 +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.kairos.internal
-
-import com.android.systemui.kairos.internal.util.Key
-import com.android.systemui.kairos.internal.util.associateByIndex
-import com.android.systemui.kairos.internal.util.hashString
-import com.android.systemui.kairos.internal.util.mapValuesParallel
-import com.android.systemui.kairos.util.Just
-import com.android.systemui.kairos.util.Maybe
-import com.android.systemui.kairos.util.just
-import com.android.systemui.kairos.util.none
-import java.util.concurrent.atomic.AtomicLong
-import kotlinx.coroutines.CompletableDeferred
-import kotlinx.coroutines.CoroutineStart
-import kotlinx.coroutines.Deferred
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-
-internal sealed interface TStateImpl<out A> {
-    val name: String?
-    val operatorName: String
-    val changes: TFlowImpl<A>
-
-    suspend fun getCurrentWithEpoch(evalScope: EvalScope): Pair<A, Long>
-}
-
-internal sealed class TStateDerived<A>(override val changes: TFlowImpl<A>) :
-    TStateImpl<A>, Key<Deferred<Pair<A, Long>>> {
-
-    @Volatile
-    var invalidatedEpoch = Long.MIN_VALUE
-        private set
-
-    @Volatile
-    protected var cache: Any? = EmptyCache
-        private set
-
-    override suspend fun getCurrentWithEpoch(evalScope: EvalScope): Pair<A, Long> =
-        evalScope.transactionStore
-            .getOrPut(this) { evalScope.deferAsync(CoroutineStart.LAZY) { pull(evalScope) } }
-            .await()
-
-    suspend fun pull(evalScope: EvalScope): Pair<A, Long> {
-        @Suppress("UNCHECKED_CAST")
-        return recalc(evalScope)?.also { (a, epoch) -> setCache(a, epoch) }
-            ?: ((cache as A) to invalidatedEpoch)
-    }
-
-    fun setCache(value: A, epoch: Long) {
-        if (epoch > invalidatedEpoch) {
-            cache = value
-            invalidatedEpoch = epoch
-        }
-    }
-
-    fun getCachedUnsafe(): Maybe<A> {
-        @Suppress("UNCHECKED_CAST")
-        return if (cache == EmptyCache) none else just(cache as A)
-    }
-
-    protected abstract suspend fun recalc(evalScope: EvalScope): Pair<A, Long>?
-
-    private data object EmptyCache
-}
-
-internal class TStateSource<A>(
-    override val name: String?,
-    override val operatorName: String,
-    init: Deferred<A>,
-    override val changes: TFlowImpl<A>,
-) : TStateImpl<A> {
-    constructor(
-        name: String?,
-        operatorName: String,
-        init: A,
-        changes: TFlowImpl<A>,
-    ) : this(name, operatorName, CompletableDeferred(init), changes)
-
-    lateinit var upstreamConnection: NodeConnection<A>
-
-    // Note: Don't need to synchronize; we will never interleave reads and writes, since all writes
-    // are performed at the end of a network step, after any reads would have taken place.
-
-    @Volatile private var _current: Deferred<A> = init
-    @Volatile
-    var writeEpoch = 0L
-        private set
-
-    override suspend fun getCurrentWithEpoch(evalScope: EvalScope): Pair<A, Long> =
-        _current.await() to writeEpoch
-
-    /** called by network after eval phase has completed */
-    suspend fun updateState(evalScope: EvalScope) {
-        // write the latch
-        val eventResult = upstreamConnection.getPushEvent(evalScope)
-        if (eventResult is Just) {
-            _current = CompletableDeferred(eventResult.value)
-            writeEpoch = evalScope.epoch
-        }
-    }
-
-    override fun toString(): String = "TStateImpl(changes=$changes, current=$_current)"
-
-    @OptIn(ExperimentalCoroutinesApi::class)
-    fun getStorageUnsafe(): Maybe<A> =
-        if (_current.isCompleted) just(_current.getCompleted()) else none
-}
-
-internal fun <A> constS(name: String?, operatorName: String, init: A): TStateImpl<A> =
-    TStateSource(name, operatorName, init, neverImpl)
-
-internal inline fun <A> mkState(
-    name: String?,
-    operatorName: String,
-    evalScope: EvalScope,
-    crossinline getChanges: suspend EvalScope.() -> TFlowImpl<A>,
-    init: Deferred<A>,
-): TStateImpl<A> {
-    lateinit var state: TStateSource<A>
-    val calm: TFlowImpl<A> =
-        filterNode(getChanges) { new -> new != state.getCurrentWithEpoch(evalScope = this).first }
-            .cached()
-    return TStateSource(name, operatorName, init, calm).also {
-        state = it
-        evalScope.scheduleOutput(
-            OneShot {
-                calm.activate(evalScope = this, downstream = Schedulable.S(state))?.let {
-                    (connection, needsEval) ->
-                    state.upstreamConnection = connection
-                    if (needsEval) {
-                        schedule(state)
-                    }
-                }
-            }
-        )
-    }
-}
-
-private inline fun <A> TFlowImpl<A>.calm(
-    crossinline getState: () -> TStateDerived<A>
-): TFlowImpl<A> =
-    filterNode({ this@calm }) { new ->
-            val state = getState()
-            val (current, _) = state.getCurrentWithEpoch(evalScope = this)
-            if (new != current) {
-                state.setCache(new, epoch)
-                true
-            } else {
-                false
-            }
-        }
-        .cached()
-
-internal fun <A, B> TStateImpl<A>.mapCheap(
-    name: String?,
-    operatorName: String,
-    transform: suspend EvalScope.(A) -> B,
-): TStateImpl<B> =
-    DerivedMapCheap(name, operatorName, this, mapImpl({ changes }) { transform(it) }, transform)
-
-internal class DerivedMapCheap<A, B>(
-    override val name: String?,
-    override val operatorName: String,
-    val upstream: TStateImpl<A>,
-    override val changes: TFlowImpl<B>,
-    private val transform: suspend EvalScope.(A) -> B,
-) : TStateImpl<B> {
-
-    override suspend fun getCurrentWithEpoch(evalScope: EvalScope): Pair<B, Long> {
-        val (a, epoch) = upstream.getCurrentWithEpoch(evalScope)
-        return evalScope.transform(a) to epoch
-    }
-
-    override fun toString(): String = "${this::class.simpleName}@$hashString"
-}
-
-internal fun <A, B> TStateImpl<A>.map(
-    name: String?,
-    operatorName: String,
-    transform: suspend EvalScope.(A) -> B,
-): TStateImpl<B> {
-    lateinit var state: TStateDerived<B>
-    val mappedChanges = mapImpl({ changes }) { transform(it) }.cached().calm { state }
-    state = DerivedMap(name, operatorName, transform, this, mappedChanges)
-    return state
-}
-
-internal class DerivedMap<A, B>(
-    override val name: String?,
-    override val operatorName: String,
-    private val transform: suspend EvalScope.(A) -> B,
-    val upstream: TStateImpl<A>,
-    changes: TFlowImpl<B>,
-) : TStateDerived<B>(changes) {
-    override fun toString(): String = "${this::class.simpleName}@$hashString"
-
-    override suspend fun recalc(evalScope: EvalScope): Pair<B, Long>? {
-        val (a, epoch) = upstream.getCurrentWithEpoch(evalScope)
-        return if (epoch > invalidatedEpoch) {
-            evalScope.transform(a) to epoch
-        } else {
-            null
-        }
-    }
-}
-
-internal fun <A> TStateImpl<TStateImpl<A>>.flatten(name: String?, operator: String): TStateImpl<A> {
-    // emits the current value of the new inner state, when that state is emitted
-    val switchEvents = mapImpl({ changes }) { newInner -> newInner.getCurrentWithEpoch(this).first }
-    // emits the new value of the new inner state when that state is emitted, or
-    // falls back to the current value if a new state is *not* being emitted this
-    // transaction
-    val innerChanges =
-        mapImpl({ changes }) { newInner ->
-            mergeNodes({ switchEvents }, { newInner.changes }) { _, new -> new }
-        }
-    val switchedChanges: TFlowImpl<A> =
-        mapImpl({
-            switchPromptImpl(
-                getStorage = {
-                    mapOf(Unit to this@flatten.getCurrentWithEpoch(evalScope = this).first.changes)
-                },
-                getPatches = { mapImpl({ innerChanges }) { new -> mapOf(Unit to just(new)) } },
-            )
-        }) { map ->
-            map.getValue(Unit)
-        }
-    lateinit var state: DerivedFlatten<A>
-    state = DerivedFlatten(name, operator, this, switchedChanges.calm { state })
-    return state
-}
-
-internal class DerivedFlatten<A>(
-    override val name: String?,
-    override val operatorName: String,
-    val upstream: TStateImpl<TStateImpl<A>>,
-    changes: TFlowImpl<A>,
-) : TStateDerived<A>(changes) {
-    override suspend fun recalc(evalScope: EvalScope): Pair<A, Long> {
-        val (inner, epoch0) = upstream.getCurrentWithEpoch(evalScope)
-        val (a, epoch1) = inner.getCurrentWithEpoch(evalScope)
-        return a to maxOf(epoch0, epoch1)
-    }
-
-    override fun toString(): String = "${this::class.simpleName}@$hashString"
-}
-
-@Suppress("NOTHING_TO_INLINE")
-internal inline fun <A, B> TStateImpl<A>.flatMap(
-    name: String?,
-    operatorName: String,
-    noinline transform: suspend EvalScope.(A) -> TStateImpl<B>,
-): TStateImpl<B> = map(null, operatorName, transform).flatten(name, operatorName)
-
-internal fun <A, B, Z> zipStates(
-    name: String?,
-    operatorName: String,
-    l1: TStateImpl<A>,
-    l2: TStateImpl<B>,
-    transform: suspend EvalScope.(A, B) -> Z,
-): TStateImpl<Z> =
-    zipStates(null, operatorName, mapOf(0 to l1, 1 to l2)).map(name, operatorName) {
-        val a = it.getValue(0)
-        val b = it.getValue(1)
-        @Suppress("UNCHECKED_CAST") transform(a as A, b as B)
-    }
-
-internal fun <A, B, C, Z> zipStates(
-    name: String?,
-    operatorName: String,
-    l1: TStateImpl<A>,
-    l2: TStateImpl<B>,
-    l3: TStateImpl<C>,
-    transform: suspend EvalScope.(A, B, C) -> Z,
-): TStateImpl<Z> =
-    zipStates(null, operatorName, mapOf(0 to l1, 1 to l2, 2 to l3)).map(name, operatorName) {
-        val a = it.getValue(0)
-        val b = it.getValue(1)
-        val c = it.getValue(2)
-        @Suppress("UNCHECKED_CAST") transform(a as A, b as B, c as C)
-    }
-
-internal fun <A, B, C, D, Z> zipStates(
-    name: String?,
-    operatorName: String,
-    l1: TStateImpl<A>,
-    l2: TStateImpl<B>,
-    l3: TStateImpl<C>,
-    l4: TStateImpl<D>,
-    transform: suspend EvalScope.(A, B, C, D) -> Z,
-): TStateImpl<Z> =
-    zipStates(null, operatorName, mapOf(0 to l1, 1 to l2, 2 to l3, 3 to l4)).map(
-        name,
-        operatorName,
-    ) {
-        val a = it.getValue(0)
-        val b = it.getValue(1)
-        val c = it.getValue(2)
-        val d = it.getValue(3)
-        @Suppress("UNCHECKED_CAST") transform(a as A, b as B, c as C, d as D)
-    }
-
-internal fun <A, B, C, D, E, Z> zipStates(
-    name: String?,
-    operatorName: String,
-    l1: TStateImpl<A>,
-    l2: TStateImpl<B>,
-    l3: TStateImpl<C>,
-    l4: TStateImpl<D>,
-    l5: TStateImpl<E>,
-    transform: suspend EvalScope.(A, B, C, D, E) -> Z,
-): TStateImpl<Z> =
-    zipStates(null, operatorName, mapOf(0 to l1, 1 to l2, 2 to l3, 3 to l4, 4 to l5)).map(
-        name,
-        operatorName,
-    ) {
-        val a = it.getValue(0)
-        val b = it.getValue(1)
-        val c = it.getValue(2)
-        val d = it.getValue(3)
-        val e = it.getValue(4)
-        @Suppress("UNCHECKED_CAST") transform(a as A, b as B, c as C, d as D, e as E)
-    }
-
-internal fun <K : Any, A> zipStates(
-    name: String?,
-    operatorName: String,
-    states: Map<K, TStateImpl<A>>,
-): TStateImpl<Map<K, A>> {
-    if (states.isEmpty()) return constS(name, operatorName, emptyMap())
-    val stateChanges: Map<K, TFlowImpl<A>> = states.mapValues { it.value.changes }
-    lateinit var state: DerivedZipped<K, A>
-    // No need for calm; invariant ensures that changes will only emit when there's a difference
-    val changes: TFlowImpl<Map<K, A>> =
-        mapImpl({
-            switchDeferredImpl(getStorage = { stateChanges }, getPatches = { neverImpl })
-        }) { patch ->
-            states
-                .mapValues { (k, v) ->
-                    if (k in patch) {
-                        patch.getValue(k)
-                    } else {
-                        v.getCurrentWithEpoch(evalScope = this).first
-                    }
-                }
-                .also { state.setCache(it, epoch) }
-        }
-    state = DerivedZipped(name, operatorName, states, changes)
-    return state
-}
-
-internal class DerivedZipped<K : Any, A>(
-    override val name: String?,
-    override val operatorName: String,
-    val upstream: Map<K, TStateImpl<A>>,
-    changes: TFlowImpl<Map<K, A>>,
-) : TStateDerived<Map<K, A>>(changes) {
-    override suspend fun recalc(evalScope: EvalScope): Pair<Map<K, A>, Long> {
-        val newEpoch = AtomicLong()
-        return upstream.mapValuesParallel {
-            val (a, epoch) = it.value.getCurrentWithEpoch(evalScope)
-            newEpoch.accumulateAndGet(epoch, ::maxOf)
-            a
-        } to newEpoch.get()
-    }
-
-    override fun toString(): String = "${this::class.simpleName}@$hashString"
-}
-
-@Suppress("NOTHING_TO_INLINE")
-internal inline fun <A> zipStates(
-    name: String?,
-    operatorName: String,
-    states: List<TStateImpl<A>>,
-): TStateImpl<List<A>> =
-    if (states.isEmpty()) {
-        constS(name, operatorName, emptyList())
-    } else {
-        zipStates(null, operatorName, states.asIterable().associateByIndex()).mapCheap(
-            name,
-            operatorName,
-        ) {
-            it.values.toList()
-        }
-    }
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/TransactionalImpl.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/TransactionalImpl.kt
index 8647bdd..13bd3b0 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/TransactionalImpl.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/TransactionalImpl.kt
@@ -16,31 +16,25 @@
 
 package com.android.systemui.kairos.internal
 
-import com.android.systemui.kairos.internal.util.Key
 import com.android.systemui.kairos.internal.util.hashString
-import kotlinx.coroutines.CoroutineStart
-import kotlinx.coroutines.Deferred
 
 internal sealed class TransactionalImpl<out A> {
-    data class Const<out A>(val value: Deferred<A>) : TransactionalImpl<A>()
+    data class Const<out A>(val value: Lazy<A>) : TransactionalImpl<A>()
 
-    class Impl<A>(val block: suspend EvalScope.() -> A) : TransactionalImpl<A>(), Key<Deferred<A>> {
+    class Impl<A>(val block: EvalScope.() -> A) : TransactionalImpl<A>() {
+        val cache = TransactionCache<Lazy<A>>()
+
         override fun toString(): String = "${this::class.simpleName}@$hashString"
     }
 }
 
 @Suppress("NOTHING_TO_INLINE")
-internal inline fun <A> transactionalImpl(
-    noinline block: suspend EvalScope.() -> A
-): TransactionalImpl<A> = TransactionalImpl.Impl(block)
+internal inline fun <A> transactionalImpl(noinline block: EvalScope.() -> A): TransactionalImpl<A> =
+    TransactionalImpl.Impl(block)
 
-internal fun <A> TransactionalImpl<A>.sample(evalScope: EvalScope): Deferred<A> =
+internal fun <A> TransactionalImpl<A>.sample(evalScope: EvalScope): Lazy<A> =
     when (this) {
         is TransactionalImpl.Const -> value
         is TransactionalImpl.Impl ->
-            evalScope.transactionStore
-                .getOrPut(this) {
-                    evalScope.deferAsync(start = CoroutineStart.LAZY) { evalScope.block() }
-                }
-                .also { it.start() }
+            cache.getOrPut(evalScope) { evalScope.deferAsync { evalScope.block() } }
     }
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/store/ArrayMapK.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/store/ArrayMapK.kt
new file mode 100644
index 0000000..f0c2f34
--- /dev/null
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/store/ArrayMapK.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.kairos.internal.store
+
+import java.util.concurrent.atomic.AtomicReferenceArray
+
+/** A [Map] backed by a flat array. */
+internal class ArrayMapK<V>(
+    val unwrapped: List<MutableMap.MutableEntry<Int, V>>,
+    val originalCapacity: Int,
+) : MapK<ArrayMapK.W, Int, V>, AbstractMap<Int, V>() {
+    object W
+
+    override val entries: Set<Map.Entry<Int, V>> =
+        object : AbstractSet<Map.Entry<Int, V>>() {
+            override val size: Int
+                get() = unwrapped.size
+
+            override fun iterator(): Iterator<Map.Entry<Int, V>> = unwrapped.iterator()
+        }
+}
+
+@Suppress("NOTHING_TO_INLINE")
+internal inline fun <V> MapK<ArrayMapK.W, Int, V>.asArrayHolder(): ArrayMapK<V> =
+    this as ArrayMapK<V>
+
+internal class MutableArrayMapK<V>
+private constructor(private val storage: AtomicReferenceArray<MutableMap.MutableEntry<Int, V>?>) :
+    MutableMapK<ArrayMapK.W, Int, V>, AbstractMutableMap<Int, V>() {
+
+    constructor(length: Int) : this(AtomicReferenceArray<MutableMap.MutableEntry<Int, V>?>(length))
+
+    override fun readOnlyCopy(): ArrayMapK<V> {
+        val size1 = storage.length()
+        return ArrayMapK(
+            buildList {
+                for (i in 0 until size1) {
+                    storage.get(i)?.let { entry -> add(StoreEntry(entry.key, entry.value)) }
+                }
+            },
+            size1,
+        )
+    }
+
+    override fun asReadOnly(): MapK<ArrayMapK.W, Int, V> = readOnlyCopy()
+
+    private fun getNumEntries(): Int {
+        val capacity = storage.length()
+        var total = 0
+        for (i in 0 until capacity) {
+            storage.get(i)?.let { total++ }
+        }
+        return total
+    }
+
+    override fun put(key: Int, value: V): V? =
+        storage.get(key)?.value.also { storage.set(key, StoreEntry(key, value)) }
+
+    override val entries: MutableSet<MutableMap.MutableEntry<Int, V>> =
+        object : AbstractMutableSet<MutableMap.MutableEntry<Int, V>>() {
+            override val size: Int
+                get() = getNumEntries()
+
+            override fun add(element: MutableMap.MutableEntry<Int, V>): Boolean =
+                (storage.get(element.key) is MutableMap.MutableEntry<*, *>).also {
+                    storage.set(element.key, element)
+                }
+
+            override fun iterator(): MutableIterator<MutableMap.MutableEntry<Int, V>> =
+                object : MutableIterator<MutableMap.MutableEntry<Int, V>> {
+
+                    var cursor = -1
+                    var nextIndex = -1
+
+                    override fun hasNext(): Boolean {
+                        val capacity = storage.length()
+                        if (nextIndex >= capacity) return false
+                        if (nextIndex != cursor) return true
+                        while (++nextIndex < capacity) {
+                            if (storage.get(nextIndex) != null) {
+                                return true
+                            }
+                        }
+                        return false
+                    }
+
+                    override fun next(): MutableMap.MutableEntry<Int, V> {
+                        if (!hasNext()) throw NoSuchElementException()
+                        cursor = nextIndex
+                        return storage.get(cursor)!!
+                    }
+
+                    override fun remove() {
+                        check(
+                            cursor >= 0 &&
+                                cursor < storage.length() &&
+                                storage.getAndSet(cursor, null) != null
+                        )
+                    }
+                }
+        }
+
+    class Factory : MutableMapK.Factory<ArrayMapK.W, Int> {
+        override fun <V> create(capacity: Int?) =
+            MutableArrayMapK<V>(checkNotNull(capacity) { "Cannot use ArrayMapK with null capacity." })
+
+        override fun <V> create(input: MapK<ArrayMapK.W, Int, V>): MutableArrayMapK<V> {
+            val holder = input.asArrayHolder()
+            return MutableArrayMapK(
+                AtomicReferenceArray<MutableMap.MutableEntry<Int, V>?>(holder.originalCapacity)
+                    .apply { holder.unwrapped.forEach { set(it.key, it) } }
+            )
+        }
+    }
+}
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/store/MapHolder.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/store/MapHolder.kt
new file mode 100644
index 0000000..db2dde0
--- /dev/null
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/store/MapHolder.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.kairos.internal.store
+
+import com.android.systemui.kairos.internal.util.ConcurrentNullableHashMap
+
+@JvmInline
+internal value class MapHolder<K, V>(val unwrapped: Map<K, V>) :
+    MapK<MapHolder.W, K, V>, Map<K, V> by unwrapped {
+    object W
+}
+
+@Suppress("NOTHING_TO_INLINE")
+internal inline fun <K, V> MapK<MapHolder.W, K, V>.asMapHolder(): MapHolder<K, V> =
+    this as MapHolder<K, V>
+
+// TODO: preserve insertion order?
+internal class ConcurrentHashMapK<K, V>(private val storage: ConcurrentNullableHashMap<K, V>) :
+    MutableMapK<MapHolder.W, K, V>, MutableMap<K, V> by storage {
+
+    override fun readOnlyCopy() = MapHolder(storage.toMap())
+
+    override fun asReadOnly(): MapK<MapHolder.W, K, V> = MapHolder(storage)
+
+    class Factory<K> : MutableMapK.Factory<MapHolder.W, K> {
+        override fun <V> create(capacity: Int?) =
+            ConcurrentHashMapK<K, V>(
+                capacity?.let { ConcurrentNullableHashMap(capacity) } ?: ConcurrentNullableHashMap()
+            )
+
+        override fun <V> create(input: MapK<MapHolder.W, K, V>) =
+            ConcurrentHashMapK(
+                ConcurrentNullableHashMap<K, V>().apply {
+                    input.asMapHolder().unwrapped.forEach { (k, v) -> set(k, v) }
+                }
+            )
+    }
+}
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/store/MapK.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/store/MapK.kt
new file mode 100644
index 0000000..e193a49
--- /dev/null
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/store/MapK.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.kairos.internal.store
+
+/**
+ * Higher-kinded encoding for [Map].
+ *
+ * Let's say you want to write a class that is generic over both a map, and the type of data within
+ * the map:
+ * ``` kotlin
+ *   class Foo<TMap, TKey, TValue> {
+ *     val container: TMap<TKey, TElement> // disallowed!
+ *   }
+ * ```
+ *
+ * You can use `MapK` to represent the "higher-kinded" type variable `TMap`:
+ * ``` kotlin
+ *   class Foo<TMap, TKey, TValue> {
+ *      val container: MapK<TMap, TKey, TValue> // OK!
+ *   }
+ * ```
+ *
+ * Note that Kotlin will not let you use the generic type without parameters as `TMap`:
+ * ``` kotlin
+ *   val fooHk: MapK<HashMap, Int, String> // not allowed: HashMap requires two type parameters
+ * ```
+ *
+ * To work around this, you need to declare a special type-witness object. This object is only used
+ * at compile time and can be stripped out by a minifier because it's never used at runtime.
+ *
+ * ``` kotlin
+ *   class Foo<A, B> : MapK<FooWitness, A, B> { ... }
+ *   object FooWitness
+ *
+ *   // safe, as long as Foo is the only implementor of MapK<FooWitness, *, *>
+ *   fun <A, B> MapK<FooWitness, A, B>.asFoo(): Foo<A, B> = this as Foo<A, B>
+ *
+ *   val fooStore: MapK<FooWitness, Int, String> = Foo()
+ *   val foo: Foo<Int, String> = fooStore.asFoo()
+ * ```
+ */
+internal interface MapK<W, K, V> : Map<K, V>
+
+internal interface MutableMapK<W, K, V> : MutableMap<K, V> {
+
+    fun readOnlyCopy(): MapK<W, K, V>
+
+    fun asReadOnly(): MapK<W, K, V>
+
+    interface Factory<W, K> {
+        fun <V> create(capacity: Int?): MutableMapK<W, K, V>
+
+        fun <V> create(input: MapK<W, K, V>): MutableMapK<W, K, V>
+    }
+}
+
+internal object NoValue
+
+internal data class StoreEntry<K, V>(override var key: K, override var value: V) :
+    MutableMap.MutableEntry<K, V> {
+    override fun setValue(newValue: V): V = value.also { value = newValue }
+}
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/store/Single.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/store/Single.kt
new file mode 100644
index 0000000..2d08948
--- /dev/null
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/store/Single.kt
@@ -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.systemui.kairos.internal.store
+
+@Suppress("NOTHING_TO_INLINE") internal inline fun <V> singleOf(value: V) = Single<V>(value)
+
+/** A [Map] with a single element that has key [Unit]. */
+internal class Single<V>(val unwrapped: Any?) : MapK<Single.W, Unit, V>, AbstractMap<Unit, V>() {
+
+    constructor() : this(NoValue)
+
+    @Suppress("UNCHECKED_CAST")
+    override val entries: Set<Map.Entry<Unit, V>> =
+        if (unwrapped === NoValue) emptySet() else setOf(StoreEntry(Unit, unwrapped as V))
+
+    object W
+}
+
+@Suppress("NOTHING_TO_INLINE")
+internal inline fun <V> MapK<Single.W, Unit, V>.asSingle(): Single<V> = this as Single<V>
+
+internal class SingletonMapK<V>(@Volatile private var value: Any?) :
+    MutableMapK<Single.W, Unit, V>, AbstractMutableMap<Unit, V>() {
+
+    constructor() : this(NoValue)
+
+    override fun readOnlyCopy() =
+        Single<V>(if (value === NoValue) value else (value as MutableMap.MutableEntry<*, *>).value)
+
+    override fun asReadOnly(): MapK<Single.W, Unit, V> = readOnlyCopy()
+
+    @Suppress("UNCHECKED_CAST")
+    override fun put(key: Unit, value: V): V? =
+        (this.value as? MutableMap.MutableEntry<Unit, V>)?.value.also {
+            this.value = StoreEntry(Unit, value)
+        }
+
+    override val entries: MutableSet<MutableMap.MutableEntry<Unit, V>> =
+        object : AbstractMutableSet<MutableMap.MutableEntry<Unit, V>>() {
+            override fun add(element: MutableMap.MutableEntry<Unit, V>): Boolean =
+                (value !== NoValue).also { value = element }
+
+            override val size: Int
+                get() = if (value === NoValue) 0 else 1
+
+            override fun iterator(): MutableIterator<MutableMap.MutableEntry<Unit, V>> {
+                return object : MutableIterator<MutableMap.MutableEntry<Unit, V>> {
+
+                    var done = false
+
+                    override fun hasNext(): Boolean = value !== NoValue && !done
+
+                    override fun next(): MutableMap.MutableEntry<Unit, V> {
+                        if (!hasNext()) throw NoSuchElementException()
+                        done = true
+                        @Suppress("UNCHECKED_CAST")
+                        return value as MutableMap.MutableEntry<Unit, V>
+                    }
+
+                    override fun remove() {
+                        if (!done || value === NoValue) throw IllegalStateException()
+                        value = NoValue
+                    }
+                }
+            }
+        }
+
+    internal class Factory : MutableMapK.Factory<Single.W, Unit> {
+        override fun <V> create(capacity: Int?): SingletonMapK<V> {
+            check(capacity == null || capacity == 0 || capacity == 1) {
+                "Can't use singleton store with capacity > 1. Got: $capacity"
+            }
+            return SingletonMapK()
+        }
+
+        override fun <V> create(input: MapK<Single.W, Unit, V>): SingletonMapK<V> =
+            SingletonMapK(input.asSingle().unwrapped)
+    }
+}
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/util/ConcurrentNullableHashMap.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/util/ConcurrentNullableHashMap.kt
index 6c8ae7c..afeb067 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/util/ConcurrentNullableHashMap.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/util/ConcurrentNullableHashMap.kt
@@ -16,31 +16,114 @@
 
 package com.android.systemui.kairos.internal.util
 
+import com.android.systemui.kairos.internal.store.NoValue
 import java.util.concurrent.ConcurrentHashMap
+import java.util.concurrent.ConcurrentMap
 
-internal class ConcurrentNullableHashMap<K : Any, V>
-private constructor(private val inner: ConcurrentHashMap<K, Any>) {
+internal class ConcurrentNullableHashMap<K, V>
+private constructor(private val inner: ConcurrentHashMap<Any, Any>) :
+    ConcurrentMap<K, V>, AbstractMutableMap<K, V>() {
+
     constructor() : this(ConcurrentHashMap())
 
-    @Suppress("UNCHECKED_CAST")
-    operator fun get(key: K): V? = inner[key]?.takeIf { it !== NullValue } as V?
+    constructor(capacity: Int) : this(ConcurrentHashMap(capacity))
+
+    override fun get(key: K): V? = inner[key ?: NullValue]?.let { toNullable<V>(it) }
+
+    fun getValue(key: K): V = toNullable(inner.getValue(key ?: NullValue))
 
     @Suppress("UNCHECKED_CAST")
-    fun put(key: K, value: V?): V? =
-        inner.put(key, value ?: NullValue)?.takeIf { it !== NullValue } as V?
+    override fun put(key: K, value: V): V? =
+        inner.put(key ?: NullValue, value ?: NullValue)?.takeIf { it !== NullValue } as V?
 
-    operator fun set(key: K, value: V?) {
+    operator fun set(key: K, value: V) {
         put(key, value)
     }
 
-    @Suppress("UNCHECKED_CAST")
-    fun toMap(): Map<K, V> = inner.mapValues { (_, v) -> v.takeIf { it !== NullValue } as V }
+    fun toMap(): Map<K, V> =
+        inner.asSequence().associate { (k, v) -> toNullable<K>(k) to toNullable(v) }
 
-    fun clear() {
+    override fun clear() {
         inner.clear()
     }
 
+    override fun remove(key: K, value: V): Boolean = inner.remove(key ?: NoValue, value ?: NoValue)
+
+    override val entries: MutableSet<MutableMap.MutableEntry<K, V>> =
+        object : AbstractMutableSet<MutableMap.MutableEntry<K, V>>() {
+            val wrapped = inner.entries
+
+            override fun add(element: MutableMap.MutableEntry<K, V>): Boolean {
+                val e =
+                    object : MutableMap.MutableEntry<Any, Any> {
+                        override val key: Any
+                            get() = element.key ?: NullValue
+
+                        override val value: Any
+                            get() = element.value ?: NullValue
+
+                        override fun setValue(newValue: Any): Any =
+                            element.setValue(toNullable(newValue)) ?: NullValue
+                    }
+                return wrapped.add(e)
+            }
+
+            override val size: Int
+                get() = wrapped.size
+
+            override fun iterator(): MutableIterator<MutableMap.MutableEntry<K, V>> {
+                val iter = wrapped.iterator()
+                return object : MutableIterator<MutableMap.MutableEntry<K, V>> {
+                    override fun hasNext(): Boolean = iter.hasNext()
+
+                    override fun next(): MutableMap.MutableEntry<K, V> {
+                        val element = iter.next()
+                        return object : MutableMap.MutableEntry<K, V> {
+                            override val key: K
+                                get() = toNullable(element.key)
+
+                            override val value: V
+                                get() = toNullable(element.value)
+
+                            override fun setValue(newValue: V): V =
+                                toNullable(element.setValue(newValue ?: NullValue))
+                        }
+                    }
+
+                    override fun remove() {
+                        iter.remove()
+                    }
+                }
+            }
+        }
+
+    override fun replace(key: K, oldValue: V, newValue: V): Boolean =
+        inner.replace(key ?: NullValue, oldValue ?: NullValue, newValue ?: NullValue)
+
+    override fun replace(key: K, value: V): V? =
+        inner.replace(key ?: NullValue, value ?: NullValue)?.let { toNullable<V>(it) }
+
+    override fun putIfAbsent(key: K, value: V): V? =
+        inner.putIfAbsent(key ?: NullValue, value ?: NullValue)?.let { toNullable<V>(it) }
+
+    @Suppress("UNCHECKED_CAST", "NOTHING_TO_INLINE")
+    private inline fun <T> toNullable(value: Any): T = value.takeIf { it !== NullValue } as T
+
     fun isNotEmpty(): Boolean = inner.isNotEmpty()
+
+    @Suppress("UNCHECKED_CAST")
+    override fun remove(key: K): V? =
+        inner.remove(key ?: NullValue)?.takeIf { it !== NullValue } as V?
+
+    fun asSequence(): Sequence<Pair<K, V>> =
+        inner.asSequence().map { (key, value) -> toNullable<K>(key) to toNullable(value) }
+
+    override fun isEmpty(): Boolean = inner.isEmpty()
+
+    override fun containsKey(key: K): Boolean = inner.containsKey(key ?: NullValue)
+
+    fun getOrPut(key: K, defaultValue: () -> V): V =
+        toNullable(inner.getOrPut(key) { defaultValue() ?: NullValue })
 }
 
 private object NullValue
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/util/HeteroMap.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/util/HeteroMap.kt
index 5cee2dd..c34e67e 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/util/HeteroMap.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/util/HeteroMap.kt
@@ -17,26 +17,37 @@
 package com.android.systemui.kairos.internal.util
 
 import com.android.systemui.kairos.util.Maybe
-import com.android.systemui.kairos.util.None
-import com.android.systemui.kairos.util.just
+import com.android.systemui.kairos.util.Maybe.Absent
 import java.util.concurrent.ConcurrentHashMap
 
-internal interface Key<A>
-
 private object NULL
 
-internal class HeteroMap {
+internal class HeteroMap private constructor(private val store: ConcurrentHashMap<Key<*>, Any>) {
+    interface Key<A> {}
 
-    private val store = ConcurrentHashMap<Key<*>, Any>()
+    constructor() : this(ConcurrentHashMap())
+
+    constructor(capacity: Int) : this(ConcurrentHashMap(capacity))
 
     @Suppress("UNCHECKED_CAST")
     operator fun <A> get(key: Key<A>): Maybe<A> =
-        store[key]?.let { just((if (it === NULL) null else it) as A) } ?: None
+        store[key]?.let { Maybe.present((if (it === NULL) null else it) as A) } ?: Absent
 
     operator fun <A> set(key: Key<A>, value: A) {
         store[key] = value ?: NULL
     }
 
+    @Suppress("UNCHECKED_CAST")
+    fun <A : Any> getOrNull(key: Key<A>): A? =
+        store[key]?.let { (if (it === NULL) null else it) as A }
+
+    @Suppress("UNCHECKED_CAST")
+    fun <A> getOrError(key: Key<A>, block: () -> String): A {
+        store[key]?.let {
+            return (if (it === NULL) null else it) as A
+        } ?: error(block())
+    }
+
     operator fun contains(key: Key<*>): Boolean = store.containsKey(key)
 
     fun clear() {
@@ -45,7 +56,7 @@
 
     @Suppress("UNCHECKED_CAST")
     fun <A> remove(key: Key<A>): Maybe<A> =
-        store.remove(key)?.let { just((if (it === NULL) null else it) as A) } ?: None
+        store.remove(key)?.let { Maybe.present((if (it === NULL) null else it) as A) } ?: Absent
 
     @Suppress("UNCHECKED_CAST")
     fun <A> getOrPut(key: Key<A>, defaultValue: () -> A): A =
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/util/MapUtils.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/util/MapUtils.kt
index ebf9a66..6786259 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/util/MapUtils.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/util/MapUtils.kt
@@ -16,8 +16,6 @@
 
 package com.android.systemui.kairos.internal.util
 
-import kotlinx.coroutines.CoroutineStart
-import kotlinx.coroutines.async
 import kotlinx.coroutines.awaitAll
 import kotlinx.coroutines.coroutineScope
 import kotlinx.coroutines.yield
@@ -32,7 +30,7 @@
     destination.also {
         coroutineScope {
                 mapValues {
-                    async {
+                    asyncImmediate {
                         yield()
                         block(it)
                     }
@@ -41,7 +39,7 @@
             .mapValuesNotNullTo(it) { (_, deferred) -> deferred.await() }
     }
 
-internal inline fun <K, A, B : Any, M : MutableMap<K, B>> Map<K, A>.mapValuesNotNullTo(
+internal inline fun <K, A, B, M : MutableMap<K, B>> Map<K, A>.mapValuesNotNullTo(
     destination: M,
     block: (Map.Entry<K, A>) -> B?,
 ): M =
@@ -51,9 +49,13 @@
         }
     }
 
+internal inline fun <K, A, B> Map<K, A>.mapValuesNotNull(
+    block: (Map.Entry<K, A>) -> B?
+): Map<K, B> = mapValuesNotNullTo(mutableMapOf(), block)
+
 internal suspend fun <A, B> Iterable<A>.mapParallel(transform: suspend (A) -> B): List<B> =
     coroutineScope {
-        map { async(start = CoroutineStart.LAZY) { transform(it) } }.awaitAll()
+        map { asyncImmediate { transform(it) } }.awaitAll()
     }
 
 internal suspend fun <K, A, B, M : MutableMap<K, B>> Map<K, A>.mapValuesParallelTo(
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/util/Util.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/util/Util.kt
index 6bb7f9f..d2a169c 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/util/Util.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/internal/util/Util.kt
@@ -18,8 +18,13 @@
 
 package com.android.systemui.kairos.internal.util
 
+import kotlin.contracts.ExperimentalContracts
+import kotlin.contracts.InvocationKind
+import kotlin.contracts.contract
 import kotlin.coroutines.CoroutineContext
 import kotlin.coroutines.EmptyCoroutineContext
+import kotlin.time.DurationUnit
+import kotlin.time.measureTimedValue
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.CoroutineStart
 import kotlinx.coroutines.Deferred
@@ -31,6 +36,62 @@
 import kotlinx.coroutines.launch
 import kotlinx.coroutines.newCoroutineContext
 
+private const val LogEnabled = false
+
+@Suppress("NOTHING_TO_INLINE")
+internal inline fun logLn(indent: Int = 0, message: Any?) {
+    if (!LogEnabled) return
+    log(indent, message)
+    println()
+}
+
+@Suppress("NOTHING_TO_INLINE")
+internal inline fun log(indent: Int = 0, message: Any?) {
+    if (!LogEnabled) return
+    printIndent(indent)
+    print(message)
+}
+
+@JvmInline
+internal value class LogIndent(val currentLogIndent: Int) {
+    @OptIn(ExperimentalContracts::class)
+    inline fun <R> logDuration(prefix: String, start: Boolean = true, block: LogIndent.() -> R): R {
+        contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
+        return logDuration(currentLogIndent, prefix, start, block)
+    }
+
+    @Suppress("NOTHING_TO_INLINE")
+    inline fun logLn(message: Any?) = logLn(currentLogIndent, message)
+}
+
+@OptIn(ExperimentalContracts::class)
+internal inline fun <R> logDuration(
+    indent: Int,
+    prefix: String,
+    start: Boolean = true,
+    block: LogIndent.() -> R,
+): R {
+    contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
+    if (!LogEnabled) return LogIndent(0).block()
+    if (start) {
+        logLn(indent, prefix)
+    }
+    val (result, duration) = measureTimedValue { LogIndent(indent + 1).block() }
+
+    printIndent(indent)
+    print(prefix)
+    print(": ")
+    println(duration.toString(DurationUnit.MICROSECONDS))
+    return result
+}
+
+@Suppress("NOTHING_TO_INLINE")
+private inline fun printIndent(indent: Int) {
+    for (i in 0 until indent) {
+        print("  ")
+    }
+}
+
 internal fun <A> CoroutineScope.asyncImmediate(
     start: CoroutineStart = CoroutineStart.UNDISPATCHED,
     context: CoroutineContext = EmptyCoroutineContext,
@@ -51,7 +112,7 @@
     }
 }
 
-internal fun CoroutineScope.launchOnCancel(
+internal fun CoroutineScope.invokeOnCancel(
     context: CoroutineContext = EmptyCoroutineContext,
     block: () -> Unit,
 ): Job =
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/util/Either.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/util/Either.kt
index ad9f7d7..9f17d56 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/util/Either.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/util/Either.kt
@@ -18,97 +18,118 @@
 
 package com.android.systemui.kairos.util
 
+import com.android.systemui.kairos.util.Either.First
+import com.android.systemui.kairos.util.Either.Second
+
 /**
- * Contains a value of two possibilities: `Left<A>` or `Right<B>`
+ * Contains a value of two possibilities: `First<A>` or `Second<B>`
  *
  * [Either] generalizes sealed classes the same way that [Pair] generalizes data classes; if a
  * [Pair] is effectively an anonymous grouping of two instances, then an [Either] is an anonymous
  * set of two options.
  */
-sealed class Either<out A, out B>
+sealed interface Either<out A, out B> {
+    /** An [Either] that contains a [First] value. */
+    @JvmInline value class First<out A>(val value: A) : Either<A, Nothing>
 
-/** An [Either] that contains a [Left] value. */
-data class Left<out A>(val value: A) : Either<A, Nothing>()
+    /** An [Either] that contains a [Second] value. */
+    @JvmInline value class Second<out B>(val value: B) : Either<Nothing, B>
 
-/** An [Either] that contains a [Right] value. */
-data class Right<out B>(val value: B) : Either<Nothing, B>()
+    companion object {
+        /** Constructs an [Either] containing the first possibility. */
+        fun <A> first(value: A): Either<A, Nothing> = First(value)
 
-/**
- * Returns an [Either] containing the result of applying [transform] to the [Left] value, or the
- * [Right] value unchanged.
- */
-inline fun <A, B, C> Either<A, C>.mapLeft(transform: (A) -> B): Either<B, C> =
-    when (this) {
-        is Left -> Left(transform(value))
-        is Right -> this
+        /** Constructs a [Either] containing the second possibility. */
+        fun <B> second(value: B): Either<Nothing, B> = Second(value)
     }
-
-/**
- * Returns an [Either] containing the result of applying [transform] to the [Right] value, or the
- * [Left] value unchanged.
- */
-inline fun <A, B, C> Either<A, B>.mapRight(transform: (B) -> C): Either<A, C> =
-    when (this) {
-        is Left -> this
-        is Right -> Right(transform(value))
-    }
-
-/** Returns a [Maybe] containing the [Left] value held by this [Either], if present. */
-inline fun <A> Either<A, *>.leftMaybe(): Maybe<A> =
-    when (this) {
-        is Left -> just(value)
-        else -> None
-    }
-
-/** Returns the [Left] value held by this [Either], or `null` if this is a [Right] value. */
-inline fun <A> Either<A, *>.leftOrNull(): A? =
-    when (this) {
-        is Left -> value
-        else -> null
-    }
-
-/** Returns a [Maybe] containing the [Right] value held by this [Either], if present. */
-inline fun <B> Either<*, B>.rightMaybe(): Maybe<B> =
-    when (this) {
-        is Right -> just(value)
-        else -> None
-    }
-
-/** Returns the [Right] value held by this [Either], or `null` if this is a [Left] value. */
-inline fun <B> Either<*, B>.rightOrNull(): B? =
-    when (this) {
-        is Right -> value
-        else -> null
-    }
-
-/**
- * Partitions this sequence of [Either] into two lists; [Pair.first] contains all [Left] values, and
- * [Pair.second] contains all [Right] values.
- */
-fun <A, B> Sequence<Either<A, B>>.partitionEithers(): Pair<List<A>, List<B>> {
-    val lefts = mutableListOf<A>()
-    val rights = mutableListOf<B>()
-    for (either in this) {
-        when (either) {
-            is Left -> lefts.add(either.value)
-            is Right -> rights.add(either.value)
-        }
-    }
-    return lefts to rights
 }
 
 /**
- * Partitions this map of [Either] values into two maps; [Pair.first] contains all [Left] values,
- * and [Pair.second] contains all [Right] values.
+ * Returns an [Either] containing the result of applying [transform] to the [First] value, or the
+ * [Second] value unchanged.
  */
-fun <K, A, B> Map<K, Either<A, B>>.partitionEithers(): Pair<Map<K, A>, Map<K, B>> {
-    val lefts = mutableMapOf<K, A>()
-    val rights = mutableMapOf<K, B>()
-    for ((k, e) in this) {
-        when (e) {
-            is Left -> lefts[k] = e.value
-            is Right -> rights[k] = e.value
+inline fun <A, B, C> Either<A, C>.mapFirst(transform: (A) -> B): Either<B, C> =
+    when (this) {
+        is First -> First(transform(value))
+        is Second -> this
+    }
+
+/**
+ * Returns an [Either] containing the result of applying [transform] to the [Second] value, or the
+ * [First] value unchanged.
+ */
+inline fun <A, B, C> Either<A, B>.mapSecond(transform: (B) -> C): Either<A, C> =
+    when (this) {
+        is First -> this
+        is Second -> Second(transform(value))
+    }
+
+/** Returns a [Maybe] containing the [First] value held by this [Either], if present. */
+inline fun <A> Either<A, *>.firstMaybe(): Maybe<A> =
+    when (this) {
+        is First -> Maybe.present(value)
+        else -> Maybe.absent
+    }
+
+/** Returns the [First] value held by this [Either], or `null` if this is a [Second] value. */
+inline fun <A> Either<A, *>.firstOrNull(): A? =
+    when (this) {
+        is First -> value
+        else -> null
+    }
+
+/** Returns a [Maybe] containing the [Second] value held by this [Either], if present. */
+inline fun <B> Either<*, B>.secondMaybe(): Maybe<B> =
+    when (this) {
+        is Second -> Maybe.present(value)
+        else -> Maybe.absent
+    }
+
+/** Returns the [Second] value held by this [Either], or `null` if this is a [First] value. */
+inline fun <B> Either<*, B>.secondOrNull(): B? =
+    when (this) {
+        is Second -> value
+        else -> null
+    }
+
+/**
+ * Returns a [These] containing either the [First] value as [These.first], or the [Second] value as
+ * [These.second]. Will never return a [These.both].
+ */
+fun <A, B> Either<A, B>.asThese(): These<A, B> =
+    when (this) {
+        is Second -> These.second(value)
+        is First -> These.first(value)
+    }
+
+/**
+ * Partitions this sequence of [Either] into two lists; [Pair.first] contains all [First] values,
+ * and [Pair.second] contains all [Second] values.
+ */
+fun <A, B> Sequence<Either<A, B>>.partitionEithers(): Pair<List<A>, List<B>> {
+    val firsts = mutableListOf<A>()
+    val seconds = mutableListOf<B>()
+    for (either in this) {
+        when (either) {
+            is First -> firsts.add(either.value)
+            is Second -> seconds.add(either.value)
         }
     }
-    return lefts to rights
+    return firsts to seconds
+}
+
+/**
+ * Partitions this map of [Either] values into two maps; [Pair.first] contains all [First] values,
+ * and [Pair.second] contains all [Second] values.
+ */
+fun <K, A, B> Map<K, Either<A, B>>.partitionEithers(): Pair<Map<K, A>, Map<K, B>> {
+    val firsts = mutableMapOf<K, A>()
+    val seconds = mutableMapOf<K, B>()
+    for ((k, e) in this) {
+        when (e) {
+            is First -> firsts[k] = e.value
+            is Second -> seconds[k] = e.value
+        }
+    }
+    return firsts to seconds
 }
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/util/MapPatch.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/util/MapPatch.kt
new file mode 100644
index 0000000..8fe41bc
--- /dev/null
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/util/MapPatch.kt
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.kairos.util
+
+import com.android.systemui.kairos.util.Either.First
+import com.android.systemui.kairos.util.Either.Second
+import com.android.systemui.kairos.util.Maybe.Present
+
+/** A "patch" that can be used to batch-update a [Map], via [applyPatch]. */
+typealias MapPatch<K, V> = Map<K, Maybe<V>>
+
+/**
+ * Returns a new [Map] that has [patch] applied to the original map.
+ *
+ * For each entry in [patch]:
+ * * a [Present] value will be included in the new map, replacing the entry in the original map with
+ *   the same key, if present.
+ * * a [Maybe.Absent] value will be omitted from the new map, excluding the entry in the original
+ *   map with the same key, if present.
+ */
+fun <K, V> Map<K, V>.applyPatch(patch: MapPatch<K, V>): Map<K, V> {
+    val (adds: List<Pair<K, V>>, removes: List<K>) =
+        patch
+            .asSequence()
+            .map { (k, v) -> if (v is Present) First(k to v.value) else Second(k) }
+            .partitionEithers()
+    val removed: Map<K, V> = this - removes.toSet()
+    val updated: Map<K, V> = removed + adds
+    return updated
+}
+
+/**
+ * Returns a [MapPatch] that, when applied, includes all of the values from the original [Map].
+ *
+ * Shorthand for:
+ * ``` kotlin
+ *   mapValues { (key, value) -> Maybe.present(value) }
+ * ```
+ */
+fun <K, V> Map<K, V>.toMapPatch(): MapPatch<K, V> = mapValues { Maybe.present(it.value) }
+
+/**
+ * Returns a [MapPatch] that, when applied, includes all of the entries from [new] whose keys are
+ * not present in [old], and excludes all entries with keys present in [old] that are not also
+ * present in [new].
+ *
+ * Note that, unlike [mapPatchFromFullDiff], only keys are taken into account. If the same key is
+ * present in both [old] and [new], but the associated values are not equal, then the returned
+ * [MapPatch] will *not* include any update to that key.
+ */
+fun <K, V> mapPatchFromKeyDiff(old: Map<K, V>, new: Map<K, V>): MapPatch<K, V> {
+    val removes = old.keys - new.keys
+    val adds = new - old.keys
+    return buildMap {
+        for (removed in removes) {
+            put(removed, Maybe.absent)
+        }
+        for ((newKey, newValue) in adds) {
+            put(newKey, Maybe.present(newValue))
+        }
+    }
+}
+
+/**
+ * Returns a [MapPatch] that, when applied, includes all of the entries from [new] that are not
+ * present in [old], and excludes all entries with keys present in [old] that are not also present
+ * in [new].
+ *
+ * Note that, unlike [mapPatchFromKeyDiff], both keys and values are taken into account. If the same
+ * key is present in both [old] and [new], but the associated values are not equal, then the
+ * returned [MapPatch] will include the entry from [new].
+ */
+fun <K, V> mapPatchFromFullDiff(old: Map<K, V>, new: Map<K, V>): MapPatch<K, V> {
+    val removes = old.keys - new.keys
+    val adds =
+        new.mapMaybeValues { (k, v) ->
+            if (k in old && v == old[k]) Maybe.absent else Maybe.present(v)
+        }
+    return hashMapOf<K, Maybe<V>>().apply {
+        for (removed in removes) {
+            put(removed, Maybe.absent)
+        }
+        for ((newKey, newValue) in adds) {
+            put(newKey, Maybe.present(newValue))
+        }
+    }
+}
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/util/Maybe.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/util/Maybe.kt
index c3cae38..4754bc4 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/util/Maybe.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/util/Maybe.kt
@@ -18,6 +18,8 @@
 
 package com.android.systemui.kairos.util
 
+import com.android.systemui.kairos.util.Maybe.Absent
+import com.android.systemui.kairos.util.Maybe.Present
 import kotlin.coroutines.Continuation
 import kotlin.coroutines.CoroutineContext
 import kotlin.coroutines.EmptyCoroutineContext
@@ -27,19 +29,30 @@
 import kotlin.coroutines.suspendCoroutine
 
 /** Represents a value that may or may not be present. */
-sealed class Maybe<out A>
+sealed interface Maybe<out A> {
+    /** A [Maybe] value that is present. */
+    @JvmInline value class Present<out A> internal constructor(val value: A) : Maybe<A>
 
-/** A [Maybe] value that is present. */
-data class Just<out A> internal constructor(val value: A) : Maybe<A>()
+    /** A [Maybe] value that is not present. */
+    data object Absent : Maybe<Nothing>
 
-/** A [Maybe] value that is not present. */
-data object None : Maybe<Nothing>()
+    companion object {
+        /** Returns a [Maybe] containing [value]. */
+        fun <A> present(value: A): Maybe<A> = Present(value)
+
+        /** A [Maybe] that is not present. */
+        val absent: Maybe<Nothing> = Absent
+
+        /** A [Maybe] that is not present. */
+        inline fun <A> absent(): Maybe<A> = Absent
+    }
+}
 
 /** Utilities to query [Maybe] instances from within a [maybe] block. */
 @RestrictsSuspension
 object MaybeScope {
     suspend operator fun <A> Maybe<A>.not(): A = suspendCoroutine { k ->
-        if (this is Just) k.resume(value)
+        if (this is Present) k.resume(value)
     }
 
     suspend inline fun guard(crossinline block: () -> Boolean): Unit = suspendCoroutine { k ->
@@ -51,7 +64,8 @@
  * Returns a [Maybe] value produced by evaluating [block].
  *
  * [block] can use its [MaybeScope] receiver to query other [Maybe] values, automatically cancelling
- * execution of [block] and producing [None] when attempting to query a [Maybe] that is not present.
+ * execution of [block] and producing [Absent] when attempting to query a [Maybe] that is not
+ * present.
  *
  * This can be used instead of Kotlin's built-in nullability (`?.` and `?:`) operators when dealing
  * with complex combinations of nullables:
@@ -66,33 +80,30 @@
  * ```
  */
 fun <A> maybe(block: suspend MaybeScope.() -> A): Maybe<A> {
-    var maybeResult: Maybe<A> = None
+    var maybeResult: Maybe<A> = Absent
     val k =
         object : Continuation<A> {
             override val context: CoroutineContext = EmptyCoroutineContext
 
             override fun resumeWith(result: Result<A>) {
-                maybeResult = result.getOrNull()?.let { just(it) } ?: None
+                maybeResult = result.getOrNull()?.let { Maybe.present(it) } ?: Absent
             }
         }
     block.startCoroutine(MaybeScope, k)
     return maybeResult
 }
 
-/** Returns a [Just] containing this value, or [None] if `null`. */
+/** Returns a [Maybe] containing this value if it is not `null`. */
 inline fun <A> (A?).toMaybe(): Maybe<A> = maybe(this)
 
-/** Returns a [Just] containing a non-null [value], or [None] if `null`. */
-inline fun <A> maybe(value: A?): Maybe<A> = value?.let(::just) ?: None
+/** Returns a [Maybe] containing [value] if it is not `null`. */
+inline fun <A> maybe(value: A?): Maybe<A> = value?.let { Maybe.present(it) } ?: Absent
 
-/** Returns a [Just] containing [value]. */
-fun <A> just(value: A): Maybe<A> = Just(value)
+/** Returns a [Maybe] that is absent. */
+fun <A> maybeOf(): Maybe<A> = Absent
 
-/** A [Maybe] that is not present. */
-val none: Maybe<Nothing> = None
-
-/** A [Maybe] that is not present. */
-inline fun <A> none(): Maybe<A> = None
+/** Returns a [Maybe] containing [value]. */
+fun <A> maybeOf(value: A): Maybe<A> = Present(value)
 
 /** Returns the value present in this [Maybe], or `null` if not present. */
 inline fun <A> Maybe<A>.orNull(): A? = orElse(null)
@@ -103,22 +114,22 @@
  */
 inline fun <A, B> Maybe<A>.map(transform: (A) -> B): Maybe<B> =
     when (this) {
-        is Just -> just(transform(value))
-        is None -> None
+        is Present -> Maybe.present(transform(value))
+        is Absent -> Absent
     }
 
 /** Returns the result of applying [transform] to the value in the original [Maybe]. */
 inline fun <A, B> Maybe<A>.flatMap(transform: (A) -> Maybe<B>): Maybe<B> =
     when (this) {
-        is Just -> transform(value)
-        is None -> None
+        is Present -> transform(value)
+        is Absent -> Absent
     }
 
 /** Returns the value present in this [Maybe], or the result of [defaultValue] if not present. */
 inline fun <A> Maybe<A>.orElseGet(defaultValue: () -> A): A =
     when (this) {
-        is Just -> value
-        is None -> defaultValue()
+        is Present -> value
+        is Absent -> defaultValue()
     }
 
 /**
@@ -130,8 +141,8 @@
 /** Returns the value present in this [Maybe], or [defaultValue] if not present. */
 inline fun <A> Maybe<A>.orElse(defaultValue: A): A =
     when (this) {
-        is Just -> value
-        is None -> defaultValue
+        is Present -> value
+        is Absent -> defaultValue
     }
 
 /**
@@ -140,15 +151,16 @@
  */
 inline fun <A> Maybe<A>.filter(predicate: (A) -> Boolean): Maybe<A> =
     when (this) {
-        is Just -> if (predicate(value)) this else None
+        is Present -> if (predicate(value)) this else Absent
         else -> this
     }
 
 /** Returns a [List] containing all values that are present in this [Iterable]. */
-fun <A> Iterable<Maybe<A>>.filterJust(): List<A> = asSequence().filterJust().toList()
+fun <A> Iterable<Maybe<A>>.filterPresent(): List<A> = asSequence().filterPresent().toList()
 
 /** Returns a [List] containing all values that are present in this [Sequence]. */
-fun <A> Sequence<Maybe<A>>.filterJust(): Sequence<A> = filterIsInstance<Just<A>>().map { it.value }
+fun <A> Sequence<Maybe<A>>.filterPresent(): Sequence<A> =
+    filterIsInstance<Present<A>>().map { it.value }
 
 // Align
 
@@ -158,23 +170,25 @@
  */
 inline fun <A, B, C> Maybe<A>.alignWith(other: Maybe<B>, transform: (These<A, B>) -> C): Maybe<C> =
     when (this) {
-        is Just -> {
+        is Present -> {
             val a = value
             when (other) {
-                is Just -> {
+                is Present -> {
                     val b = other.value
-                    just(transform(These.both(a, b)))
+                    Maybe.present(transform(These.both(a, b)))
                 }
-                None -> just(transform(These.thiz(a)))
+
+                Absent -> Maybe.present(transform(These.first(a)))
             }
         }
-        None ->
+        Absent ->
             when (other) {
-                is Just -> {
+                is Present -> {
                     val b = other.value
-                    just(transform(These.that(b)))
+                    Maybe.present(transform(These.second(b)))
                 }
-                None -> none
+
+                Absent -> Maybe.absent
             }
     }
 
@@ -188,7 +202,7 @@
  */
 inline fun <A> Maybe<A>.orElseGetMaybe(other: () -> Maybe<A>): Maybe<A> =
     when (this) {
-        is Just -> this
+        is Present -> this
         else -> other()
     }
 
@@ -230,26 +244,38 @@
  * Returns a list containing only the present results of applying [transform] to each element in the
  * original iterable.
  */
-fun <A, B> Iterable<A>.mapMaybe(transform: (A) -> Maybe<B>): List<B> =
-    asSequence().mapMaybe(transform).toList()
+inline fun <A, B> Iterable<A>.mapMaybe(transform: (A) -> Maybe<B>): List<B> = buildList {
+    for (a in this@mapMaybe) {
+        val result = transform(a)
+        if (result is Present) {
+            add(result.value)
+        }
+    }
+}
 
 /**
  * Returns a sequence containing only the present results of applying [transform] to each element in
  * the original sequence.
  */
 fun <A, B> Sequence<A>.mapMaybe(transform: (A) -> Maybe<B>): Sequence<B> =
-    map(transform).filterIsInstance<Just<B>>().map { it.value }
+    map(transform).filterIsInstance<Present<B>>().map { it.value }
 
 /**
  * Returns a map with values of only the present results of applying [transform] to each entry in
  * the original map.
  */
-inline fun <K, A, B> Map<K, A>.mapMaybeValues(
-    crossinline p: (Map.Entry<K, A>) -> Maybe<B>
-): Map<K, B> = asSequence().mapMaybe { entry -> p(entry).map { entry.key to it } }.toMap()
+inline fun <K, A, B> Map<K, A>.mapMaybeValues(transform: (Map.Entry<K, A>) -> Maybe<B>): Map<K, B> =
+    buildMap {
+        for (entry in this@mapMaybeValues) {
+            val result = transform(entry)
+            if (result is Present) {
+                put(entry.key, result.value)
+            }
+        }
+    }
 
 /** Returns a map with all non-present values filtered out. */
-fun <K, A> Map<K, Maybe<A>>.filterJustValues(): Map<K, A> =
+fun <K, A> Map<K, Maybe<A>>.filterPresentValues(): Map<K, A> =
     asSequence().mapMaybe { (key, mValue) -> mValue.map { key to it } }.toMap()
 
 /**
@@ -263,9 +289,9 @@
 fun <K, V> Map<K, V>.getMaybe(key: K): Maybe<V> {
     val value = get(key)
     if (value == null && !containsKey(key)) {
-        return none
+        return Maybe.absent
     } else {
         @Suppress("UNCHECKED_CAST")
-        return just(value as V)
+        return Maybe.present(value as V)
     }
 }
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/util/These.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/util/These.kt
index aa95e0d..fc7b1e0 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/util/These.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/util/These.kt
@@ -16,26 +16,28 @@
 
 package com.android.systemui.kairos.util
 
+import com.android.systemui.kairos.util.Maybe.Present
+
 /** Contains at least one of two potential values. */
-sealed class These<A, B> {
-    /** Contains a single potential value. */
-    class This<A, B> internal constructor(val thiz: A) : These<A, B>()
+sealed class These<out A, out B> {
+    /** A [These] that contains a [First] value. */
+    class First<A, B> internal constructor(val value: A) : These<A, B>()
 
-    /** Contains a single potential value. */
-    class That<A, B> internal constructor(val that: B) : These<A, B>()
+    /** A [These] that contains a [Second] value. */
+    class Second<A, B> internal constructor(val value: B) : These<A, B>()
 
-    /** Contains both potential values. */
-    class Both<A, B> internal constructor(val thiz: A, val that: B) : These<A, B>()
+    /** A [These] that contains [Both] a [first] and [second] value. */
+    class Both<A, B> internal constructor(val first: A, val second: B) : These<A, B>()
 
     companion object {
-        /** Constructs a [These] containing only [thiz]. */
-        fun <A, B> thiz(thiz: A): These<A, B> = This(thiz)
+        /** Constructs a [These] containing the first possibility. */
+        fun <A> first(value: A): These<A, Nothing> = First(value)
 
-        /** Constructs a [These] containing only [that]. */
-        fun <A, B> that(that: B): These<A, B> = That(that)
+        /** Constructs a [These] containing the second possibility. */
+        fun <B> second(value: B): These<Nothing, B> = Second(value)
 
-        /** Constructs a [These] containing both [thiz] and [that]. */
-        fun <A, B> both(thiz: A, that: B): These<A, B> = Both(thiz, that)
+        /** Constructs a [These] containing both possibilities. */
+        fun <A, B> both(first: A, second: B): These<A, B> = Both(first, second)
     }
 }
 
@@ -45,87 +47,88 @@
  */
 inline fun <A> These<A, A>.merge(f: (A, A) -> A): A =
     when (this) {
-        is These.This -> thiz
-        is These.That -> that
-        is These.Both -> f(thiz, that)
+        is These.First -> value
+        is These.Second -> value
+        is These.Both -> f(first, second)
     }
 
-/** Returns the [These.This] [value][These.This.thiz] present in this [These] as a [Maybe]. */
-fun <A> These<A, *>.maybeThis(): Maybe<A> =
+/** Returns the [These.First] [value][These.First.value] present in this [These] as a [Maybe]. */
+fun <A> These<A, *>.maybeFirst(): Maybe<A> =
     when (this) {
-        is These.Both -> just(thiz)
-        is These.That -> None
-        is These.This -> just(thiz)
+        is These.Both -> Maybe.present(first)
+        is These.Second -> Maybe.absent
+        is These.First -> Maybe.present(value)
     }
 
 /**
- * Returns the [These.This] [value][These.This.thiz] present in this [These], or `null` if not
+ * Returns the [These.First] [value][These.First.value] present in this [These], or `null` if not
  * present.
  */
-fun <A : Any> These<A, *>.thisOrNull(): A? =
+fun <A : Any> These<A, *>.firstOrNull(): A? =
     when (this) {
-        is These.Both -> thiz
-        is These.That -> null
-        is These.This -> thiz
+        is These.Both -> first
+        is These.Second -> null
+        is These.First -> value
     }
 
-/** Returns the [These.That] [value][These.That.that] present in this [These] as a [Maybe]. */
-fun <A> These<*, A>.maybeThat(): Maybe<A> =
+/** Returns the [These.Second] [value][These.Second.value] present in this [These] as a [Maybe]. */
+fun <A> These<*, A>.maybeSecond(): Maybe<A> =
     when (this) {
-        is These.Both -> just(that)
-        is These.That -> just(that)
-        is These.This -> None
+        is These.Both -> Maybe.present(second)
+        is These.Second -> Maybe.present(value)
+        is These.First -> Maybe.absent
     }
 
 /**
- * Returns the [These.That] [value][These.That.that] present in this [These], or `null` if not
+ * Returns the [These.Second] [value][These.Second.value] present in this [These], or `null` if not
  * present.
  */
-fun <A : Any> These<*, A>.thatOrNull(): A? =
+fun <A : Any> These<*, A>.secondOrNull(): A? =
     when (this) {
-        is These.Both -> that
-        is These.That -> that
-        is These.This -> null
+        is These.Both -> second
+        is These.Second -> value
+        is These.First -> null
     }
 
 /** Returns [These.Both] values present in this [These] as a [Maybe]. */
 fun <A, B> These<A, B>.maybeBoth(): Maybe<Pair<A, B>> =
     when (this) {
-        is These.Both -> just(thiz to that)
-        else -> None
+        is These.Both -> Maybe.present(first to second)
+        else -> Maybe.absent
     }
 
-/** Returns a [These] containing [thiz] and/or [that] if they are present. */
-fun <A, B> these(thiz: Maybe<A>, that: Maybe<B>): Maybe<These<A, B>> =
-    when (thiz) {
-        is Just ->
-            just(
-                when (that) {
-                    is Just -> These.both(thiz.value, that.value)
-                    else -> These.thiz(thiz.value)
+/** Returns a [These] containing [first] and/or [second] if they are present. */
+fun <A, B> these(first: Maybe<A>, second: Maybe<B>): Maybe<These<A, B>> =
+    when (first) {
+        is Present ->
+            Maybe.present(
+                when (second) {
+                    is Present -> These.both(first.value, second.value)
+                    else -> These.first(first.value)
                 }
             )
+
         else ->
-            when (that) {
-                is Just -> just(These.that(that.value))
-                else -> none
+            when (second) {
+                is Present -> Maybe.present(These.second(second.value))
+                else -> Maybe.absent
             }
     }
 
 /**
- * Returns a [These] containing [thiz] and/or [that] if they are non-null, or `null` if both are
+ * Returns a [These] containing [first] and/or [second] if they are non-null, or `null` if both are
  * `null`.
  */
-fun <A : Any, B : Any> theseNull(thiz: A?, that: B?): These<A, B>? =
-    thiz?.let { that?.let { These.both(thiz, that) } ?: These.thiz(thiz) }
-        ?: that?.let { These.that(that) }
+fun <A : Any, B : Any> theseNotNull(first: A?, second: B?): These<A, B>? =
+    first?.let { second?.let { These.both(first, second) } ?: These.first(first) }
+        ?: second?.let { These.second(second) }
 
 /**
- * Returns two maps, with [Pair.first] containing all [These.This] values and [Pair.second]
- * containing all [These.That] values.
+ * Returns two maps, with [Pair.first] containing all [These.First] values and [Pair.second]
+ * containing all [These.Second] values.
  *
  * If the value is [These.Both], then the associated key with appear in both output maps, bound to
- * [These.Both.thiz] and [These.Both.that] in each respective output.
+ * [These.Both.first] and [These.Both.second] in each respective output.
  */
 fun <K, A, B> Map<K, These<A, B>>.partitionThese(): Pair<Map<K, A>, Map<K, B>> {
     val a = mutableMapOf<K, A>()
@@ -133,14 +136,14 @@
     for ((k, t) in this) {
         when (t) {
             is These.Both -> {
-                a[k] = t.thiz
-                b[k] = t.that
+                a[k] = t.first
+                b[k] = t.second
             }
-            is These.That -> {
-                b[k] = t.that
+            is These.Second -> {
+                b[k] = t.value
             }
-            is These.This -> {
-                a[k] = t.thiz
+            is These.First -> {
+                a[k] = t.value
             }
         }
     }
diff --git a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/util/WithPrev.kt b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/util/WithPrev.kt
index 5cfaa3e..1bb97ac 100644
--- a/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/util/WithPrev.kt
+++ b/packages/SystemUI/utils/kairos/src/com/android/systemui/kairos/util/WithPrev.kt
@@ -16,5 +16,5 @@
 
 package com.android.systemui.kairos.util
 
-/** Holds a [newValue] emitted from a `TFlow`, along with the [previousValue] emitted value. */
+/** Holds a [newValue] emitted from a `Events`, along with the [previousValue] emitted value. */
 data class WithPrev<out S, out T : S>(val previousValue: S, val newValue: T)
diff --git a/packages/SystemUI/utils/kairos/test/com/android/systemui/kairos/KairosSamples.kt b/packages/SystemUI/utils/kairos/test/com/android/systemui/kairos/KairosSamples.kt
new file mode 100644
index 0000000..88a5b7a
--- /dev/null
+++ b/packages/SystemUI/utils/kairos/test/com/android/systemui/kairos/KairosSamples.kt
@@ -0,0 +1,774 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.kairos
+
+import com.android.systemui.kairos.util.MapPatch
+import com.android.systemui.kairos.util.These
+import com.android.systemui.kairos.util.maybeOf
+import com.android.systemui.kairos.util.toMaybe
+import kotlin.time.Duration.Companion.seconds
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.TestDispatcher
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
+import org.junit.Assert
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+@OptIn(ExperimentalCoroutinesApi::class)
+class KairosSamples {
+
+    @Test fun test_mapMaybe() = runSample { mapMaybe() }
+
+    fun BuildScope.mapMaybe() {
+        val emitter = MutableEvents<String>()
+        val ints = emitter.mapMaybe { it.toIntOrNull().toMaybe() }
+
+        var observedInput: String? = null
+        emitter.observe { observedInput = it }
+
+        var observedInt: Int? = null
+        ints.observe { observedInt = it }
+
+        launchEffect {
+            // parse succeeds
+            emitter.emit("6")
+            assertEquals(observedInput, "6")
+            assertEquals(observedInt, 6)
+
+            // parse fails
+            emitter.emit("foo")
+            assertEquals(observedInput, "foo")
+            assertEquals(observedInt, 6)
+
+            // parse succeeds
+            emitter.emit("500")
+            assertEquals(observedInput, "500")
+            assertEquals(observedInt, 500)
+        }
+    }
+
+    @Test fun test_mapCheap() = runSample { mapCheap() }
+
+    fun BuildScope.mapCheap() {
+        val emitter = MutableEvents<Int>()
+
+        var invocationCount = 0
+        val squared =
+            emitter.mapCheap {
+                invocationCount++
+                it * it
+            }
+
+        var observedSquare: Int? = null
+        squared.observe { observedSquare = it }
+
+        launchEffect {
+            emitter.emit(10)
+            assertTrue(invocationCount >= 1)
+            assertEquals(observedSquare, 100)
+
+            emitter.emit(2)
+            assertTrue(invocationCount >= 2)
+            assertEquals(observedSquare, 4)
+        }
+    }
+
+    @Test fun test_mapEvents() = runSample { mapEvents() }
+
+    fun BuildScope.mapEvents() {
+        val emitter = MutableEvents<Int>()
+
+        val squared = emitter.map { it * it }
+
+        var observedSquare: Int? = null
+        squared.observe { observedSquare = it }
+
+        launchEffect {
+            emitter.emit(10)
+            assertEquals(observedSquare, 100)
+
+            emitter.emit(2)
+            assertEquals(observedSquare, 4)
+        }
+    }
+
+    @Test fun test_eventsLoop() = runSample { eventsLoop() }
+
+    fun BuildScope.eventsLoop() {
+        val emitter = MutableEvents<Unit>()
+        var newCount: Events<Int> by EventsLoop()
+        val count = newCount.holdState(0)
+        newCount = emitter.map { count.sample() + 1 }
+
+        var observedCount = 0
+        count.observe { observedCount = it }
+
+        launchEffect {
+            emitter.emit(Unit)
+            assertEquals(observedCount, expected = 1)
+
+            emitter.emit(Unit)
+            assertEquals(observedCount, expected = 2)
+        }
+    }
+
+    @Test fun test_stateLoop() = runSample { stateLoop() }
+
+    fun BuildScope.stateLoop() {
+        val emitter = MutableEvents<Unit>()
+        var count: State<Int> by StateLoop()
+        count = emitter.map { count.sample() + 1 }.holdState(0)
+
+        var observedCount = 0
+        count.observe { observedCount = it }
+
+        launchEffect {
+            emitter.emit(Unit)
+            assertEquals(observedCount, expected = 1)
+
+            emitter.emit(Unit)
+            assertEquals(observedCount, expected = 2)
+        }
+    }
+
+    @Test fun test_changes() = runSample { changes() }
+
+    fun BuildScope.changes() {
+        val emitter = MutableEvents<Int>()
+        val state = emitter.holdState(0)
+
+        var numEmissions = 0
+        emitter.observe { numEmissions++ }
+
+        var observedState = 0
+        var numChangeEmissions = 0
+        state.changes.observe {
+            observedState = it
+            numChangeEmissions++
+        }
+
+        launchEffect {
+            emitter.emit(0)
+            assertEquals(numEmissions, expected = 1)
+            assertEquals(numChangeEmissions, expected = 0)
+            assertEquals(observedState, expected = 0)
+
+            emitter.emit(5)
+            assertEquals(numEmissions, expected = 2)
+            assertEquals(numChangeEmissions, expected = 1)
+            assertEquals(observedState, expected = 5)
+
+            emitter.emit(3)
+            assertEquals(numEmissions, expected = 3)
+            assertEquals(numChangeEmissions, expected = 2)
+            assertEquals(observedState, expected = 3)
+
+            emitter.emit(3)
+            assertEquals(numEmissions, expected = 4)
+            assertEquals(numChangeEmissions, expected = 2)
+            assertEquals(observedState, expected = 3)
+
+            emitter.emit(5)
+            assertEquals(numEmissions, expected = 5)
+            assertEquals(numChangeEmissions, expected = 3)
+            assertEquals(observedState, expected = 5)
+        }
+    }
+
+    @Test fun test_partitionThese() = runSample { partitionThese() }
+
+    fun BuildScope.partitionThese() {
+        val emitter = MutableEvents<These<Int, String>>()
+        val (lefts, rights) = emitter.partitionThese()
+
+        var observedLeft: Int? = null
+        lefts.observe { observedLeft = it }
+
+        var observedRight: String? = null
+        rights.observe { observedRight = it }
+
+        launchEffect {
+            emitter.emit(These.first(10))
+            assertEquals(observedLeft, 10)
+            assertEquals(observedRight, null)
+
+            emitter.emit(These.both(2, "foo"))
+            assertEquals(observedLeft, 2)
+            assertEquals(observedRight, "foo")
+
+            emitter.emit(These.second("bar"))
+            assertEquals(observedLeft, 2)
+            assertEquals(observedRight, "bar")
+        }
+    }
+
+    @Test fun test_merge() = runSample { merge() }
+
+    fun BuildScope.merge() {
+        val emitter = MutableEvents<Int>()
+        val fizz = emitter.mapNotNull { if (it % 3 == 0) "Fizz" else null }
+        val buzz = emitter.mapNotNull { if (it % 5 == 0) "Buzz" else null }
+        val fizzbuzz = fizz.mergeWith(buzz) { _, _ -> "Fizz Buzz" }
+        val output = mergeLeft(fizzbuzz, emitter.mapCheap { it.toString() })
+
+        var observedOutput: String? = null
+        output.observe { observedOutput = it }
+
+        launchEffect {
+            emitter.emit(1)
+            assertEquals(observedOutput, "1")
+            emitter.emit(2)
+            assertEquals(observedOutput, "2")
+            emitter.emit(3)
+            assertEquals(observedOutput, "Fizz")
+            emitter.emit(4)
+            assertEquals(observedOutput, "4")
+            emitter.emit(5)
+            assertEquals(observedOutput, "Buzz")
+            emitter.emit(6)
+            assertEquals(observedOutput, "Fizz")
+            emitter.emit(15)
+            assertEquals(observedOutput, "Fizz Buzz")
+        }
+    }
+
+    @Test fun test_groupByKey() = runSample { groupByKey() }
+
+    fun BuildScope.groupByKey() {
+        val emitter = MutableEvents<Map<String, Int>>()
+        val grouped = emitter.groupByKey()
+        val groupA = grouped["A"]
+        val groupB = grouped["B"]
+
+        var numEmissions = 0
+        emitter.observe { numEmissions++ }
+
+        var observedA: Int? = null
+        groupA.observe { observedA = it }
+
+        var observedB: Int? = null
+        groupB.observe { observedB = it }
+
+        launchEffect {
+            // emit to group A
+            emitter.emit(mapOf("A" to 3))
+            assertEquals(numEmissions, 1)
+            assertEquals(observedA, 3)
+            assertEquals(observedB, null)
+
+            // emit to groups B and C, even though there are no observers of C
+            emitter.emit(mapOf("B" to 9, "C" to 100))
+            assertEquals(numEmissions, 2)
+            assertEquals(observedA, 3)
+            assertEquals(observedB, 9)
+
+            // emit to groups A and B
+            emitter.emit(mapOf("B" to 6, "A" to 14))
+            assertEquals(numEmissions, 3)
+            assertEquals(observedA, 14)
+            assertEquals(observedB, 6)
+
+            // emit to group with no listeners
+            emitter.emit(mapOf("Q" to -66))
+            assertEquals(numEmissions, 4)
+            assertEquals(observedA, 14)
+            assertEquals(observedB, 6)
+
+            // no-op emission
+            emitter.emit(emptyMap())
+            assertEquals(numEmissions, 5)
+            assertEquals(observedA, 14)
+            assertEquals(observedB, 6)
+        }
+    }
+
+    @Test fun test_switchEvents() = runSample { switchEvents() }
+
+    fun BuildScope.switchEvents() {
+        val negator = MutableEvents<Unit>()
+        val emitter = MutableEvents<Int>()
+        val negate = negator.foldState(false) { _, negate -> !negate }
+        val output =
+            negate.map { negate -> if (negate) emitter.map { it * -1 } else emitter }.switchEvents()
+
+        var observed: Int? = null
+        output.observe { observed = it }
+
+        launchEffect {
+            // emit like normal
+            emitter.emit(10)
+            assertEquals(observed, 10)
+
+            // enable negation
+            observed = null
+            negator.emit(Unit)
+            assertEquals(observed, null)
+
+            emitter.emit(99)
+            assertEquals(observed, -99)
+
+            // disable negation
+            observed = null
+            negator.emit(Unit)
+            emitter.emit(7)
+            assertEquals(observed, 7)
+        }
+    }
+
+    @Test fun test_switchEventsPromptly() = runSample { switchEventsPromptly() }
+
+    fun BuildScope.switchEventsPromptly() {
+        val emitter = MutableEvents<Int>()
+        val enabled = emitter.map { it > 10 }.holdState(false)
+        val switchedIn = enabled.map { enabled -> if (enabled) emitter else emptyEvents }
+        val deferredSwitch = switchedIn.switchEvents()
+        val promptSwitch = switchedIn.switchEventsPromptly()
+
+        var observedDeferred: Int? = null
+        deferredSwitch.observe { observedDeferred = it }
+
+        var observedPrompt: Int? = null
+        promptSwitch.observe { observedPrompt = it }
+
+        launchEffect {
+            emitter.emit(3)
+            assertEquals(observedDeferred, null)
+            assertEquals(observedPrompt, null)
+
+            emitter.emit(20)
+            assertEquals(observedDeferred, null)
+            assertEquals(observedPrompt, 20)
+
+            emitter.emit(30)
+            assertEquals(observedDeferred, 30)
+            assertEquals(observedPrompt, 30)
+
+            emitter.emit(8)
+            assertEquals(observedDeferred, 8)
+            assertEquals(observedPrompt, 8)
+
+            emitter.emit(1)
+            assertEquals(observedDeferred, 8)
+            assertEquals(observedPrompt, 8)
+        }
+    }
+
+    @Test fun test_sampleTransactional() = runSample { sampleTransactional() }
+
+    fun BuildScope.sampleTransactional() {
+        var store = 0
+        val transactional = transactionally { store++ }
+
+        effect {
+            assertEquals(store, 0)
+            assertEquals(transactional.sample(), 0)
+            assertEquals(store, 1)
+            assertEquals(transactional.sample(), 0)
+            assertEquals(store, 1)
+        }
+    }
+
+    @Test fun test_states() = runSample { states() }
+
+    fun BuildScope.states() {
+        val constantState = stateOf(10)
+        effect { assertEquals(constantState.sample(), 10) }
+
+        val mappedConstantState: State<Int> = constantState.map { it * 2 }
+        effect { assertEquals(mappedConstantState.sample(), 20) }
+
+        val emitter = MutableEvents<Int>()
+        val heldState: State<Int?> = emitter.holdState(null)
+        effect { assertEquals(heldState.sample(), null) }
+
+        var observed: Int? = null
+        var wasObserved = false
+        heldState.observe {
+            observed = it
+            wasObserved = true
+        }
+        launchEffect {
+            assertTrue(wasObserved)
+            emitter.emit(4)
+            assertEquals(observed, 4)
+        }
+
+        val combinedStates: State<Pair<Int, Int?>> =
+            combine(mappedConstantState, heldState) { a, b -> Pair(a, b) }
+
+        effect { assertEquals(combinedStates.sample(), 20 to null) }
+
+        var observedPair: Pair<Int, Int?>? = null
+        combinedStates.observe { observedPair = it }
+        launchEffect {
+            emitter.emit(12)
+            assertEquals(observedPair, 20 to 12)
+        }
+    }
+
+    @Test fun test_holdState() = runSample { holdState() }
+
+    fun BuildScope.holdState() {
+        val emitter = MutableEvents<Int>()
+        val heldState: State<Int?> = emitter.holdState(null)
+        effect { assertEquals(heldState.sample(), null) }
+
+        var observed: Int? = null
+        var wasObserved = false
+        heldState.observe {
+            observed = it
+            wasObserved = true
+        }
+        launchEffect {
+            // observation of the initial state took place immediately
+            assertTrue(wasObserved)
+
+            // state changes are also observed
+            emitter.emit(4)
+            assertEquals(observed, 4)
+
+            emitter.emit(20)
+            assertEquals(observed, 20)
+        }
+    }
+
+    @Test fun test_mapState() = runSample { mapState() }
+
+    fun BuildScope.mapState() {
+        val emitter = MutableEvents<Int>()
+        val held: State<Int> = emitter.holdState(0)
+        val squared: State<Int> = held.map { it * it }
+
+        var observed: Int? = null
+        squared.observe { observed = it }
+
+        launchEffect {
+            assertEquals(observed, 0)
+
+            emitter.emit(10)
+            assertEquals(observed, 100)
+        }
+    }
+
+    @Test fun test_combineState() = runSample { combineState() }
+
+    fun BuildScope.combineState() {
+        val emitter = MutableEvents<Int>()
+        val state = emitter.holdState(0)
+        val squared = state.map { it * it }
+        val negated = state.map { -it }
+        val combined = squared.combine(negated) { a, b -> Pair(a, b) }
+
+        val observed = mutableListOf<Pair<Int, Int>>()
+        combined.observe { observed.add(it) }
+
+        launchEffect {
+            emitter.emit(10)
+            emitter.emit(20)
+            emitter.emit(3)
+
+            assertEquals(observed, listOf(0 to 0, 100 to -10, 400 to -20, 9 to -3))
+        }
+    }
+
+    @Test fun test_flatMap() = runSample { flatMap() }
+
+    fun BuildScope.flatMap() {
+        val toggler = MutableEvents<Unit>()
+        val firstEmitter = MutableEvents<Unit>()
+        val secondEmitter = MutableEvents<Unit>()
+
+        val firstCount: State<Int> = firstEmitter.foldState(0) { _, count -> count + 1 }
+        val secondCount: State<Int> = secondEmitter.foldState(0) { _, count -> count + 1 }
+        val toggleState: State<Boolean> = toggler.foldState(true) { _, state -> !state }
+
+        val activeCount: State<Int> =
+            toggleState.flatMap { b -> if (b) firstCount else secondCount }
+
+        var observed: Int? = null
+        activeCount.observe { observed = it }
+
+        launchEffect {
+            assertEquals(observed, 0)
+
+            firstEmitter.emit(Unit)
+            assertEquals(observed, 1)
+
+            secondEmitter.emit(Unit)
+            assertEquals(observed, 1)
+
+            secondEmitter.emit(Unit)
+            assertEquals(observed, 1)
+
+            toggler.emit(Unit)
+            assertEquals(observed, 2)
+
+            toggler.emit(Unit)
+            assertEquals(observed, 1)
+        }
+    }
+
+    @Test fun test_incrementals() = runSample { incrementals() }
+
+    fun BuildScope.incrementals() {
+        val patchEmitter = MutableEvents<MapPatch<String, Int>>()
+        val incremental: Incremental<String, Int> = patchEmitter.foldStateMapIncrementally()
+        val squared = incremental.mapValues { (key, value) -> value * value }
+
+        var observedUpdate: MapPatch<String, Int>? = null
+        squared.updates.observe { observedUpdate = it }
+
+        var observedState: Map<String, Int>? = null
+        squared.observe { observedState = it }
+
+        launchEffect {
+            assertEquals(observedState, emptyMap())
+            assertEquals(observedUpdate, null)
+
+            // add entry: A => 10
+            patchEmitter.emit(mapOf("A" to maybeOf(10)))
+            assertEquals(observedState, mapOf("A" to 100))
+            assertEquals(observedUpdate, mapOf("A" to maybeOf(100)))
+
+            // update entry: A => 5
+            // add entry: B => 6
+            patchEmitter.emit(mapOf("A" to maybeOf(5), "B" to maybeOf(6)))
+            assertEquals(observedState, mapOf("A" to 25, "B" to 36))
+            assertEquals(observedUpdate, mapOf("A" to maybeOf(25), "B" to maybeOf(36)))
+
+            // remove entry: A
+            // add entry: C => 9
+            // remove non-existent entry: F
+            patchEmitter.emit(mapOf("A" to maybeOf(), "C" to maybeOf(9), "F" to maybeOf()))
+            assertEquals(observedState, mapOf("B" to 36, "C" to 81))
+            // non-existent entry is filtered from the update
+            assertEquals(observedUpdate, mapOf("A" to maybeOf(), "C" to maybeOf(81)))
+        }
+    }
+
+    @Test fun test_mergeEventsIncrementally() = runSample(block = mergeEventsIncrementally())
+
+    fun mergeEventsIncrementally(): BuildSpec<Unit> = buildSpec {
+        val patchEmitter = MutableEvents<MapPatch<String, Events<Int>>>()
+        val incremental: Incremental<String, Events<Int>> = patchEmitter.foldStateMapIncrementally()
+        val merged: Events<Map<String, Int>> = incremental.mergeEventsIncrementally()
+
+        var observed: Map<String, Int>? = null
+        merged.observe { observed = it }
+
+        launchEffect {
+            // add events entry: A
+            val emitterA = MutableEvents<Int>()
+            patchEmitter.emit(mapOf("A" to maybeOf(emitterA)))
+
+            emitterA.emit(100)
+            assertEquals(observed, mapOf("A" to 100))
+
+            // add events entry: B
+            val emitterB = MutableEvents<Int>()
+            patchEmitter.emit(mapOf("B" to maybeOf(emitterB)))
+
+            // merged emits from both A and B
+            emitterB.emit(5)
+            assertEquals(observed, mapOf("B" to 5))
+
+            emitterA.emit(20)
+            assertEquals(observed, mapOf("A" to 20))
+
+            // remove entry: A
+            patchEmitter.emit(mapOf("A" to maybeOf()))
+            emitterA.emit(0)
+            // event is not emitted now that A has been removed
+            assertEquals(observed, mapOf("A" to 20))
+
+            // but B still works
+            emitterB.emit(3)
+            assertEquals(observed, mapOf("B" to 3))
+        }
+    }
+
+    @Test
+    fun test_mergeEventsIncrementallyPromptly() =
+        runSample(block = mergeEventsIncrementallyPromptly())
+
+    fun mergeEventsIncrementallyPromptly(): BuildSpec<Unit> = buildSpec {
+        val patchEmitter = MutableEvents<MapPatch<String, Events<Int>>>()
+        val incremental: Incremental<String, Events<Int>> = patchEmitter.foldStateMapIncrementally()
+        val deferredMerge: Events<Map<String, Int>> = incremental.mergeEventsIncrementally()
+        val promptMerge: Events<Map<String, Int>> = incremental.mergeEventsIncrementallyPromptly()
+
+        var observedDeferred: Map<String, Int>? = null
+        deferredMerge.observe { observedDeferred = it }
+
+        var observedPrompt: Map<String, Int>? = null
+        promptMerge.observe { observedPrompt = it }
+
+        launchEffect {
+            val emitterA = MutableEvents<Int>()
+            patchEmitter.emit(mapOf("A" to maybeOf(emitterA)))
+
+            emitterA.emit(100)
+            assertEquals(observedDeferred, mapOf("A" to 100))
+            assertEquals(observedPrompt, mapOf("A" to 100))
+
+            val emitterB = patchEmitter.map { 5 }
+            patchEmitter.emit(mapOf("B" to maybeOf(emitterB)))
+
+            assertEquals(observedDeferred, mapOf("A" to 100))
+            assertEquals(observedPrompt, mapOf("B" to 5))
+        }
+    }
+
+    @Test fun test_applyLatestStateful() = runSample(block = applyLatestStateful())
+
+    fun applyLatestStateful(): BuildSpec<Unit> = buildSpec {
+        val reset = MutableEvents<Unit>()
+        val emitter = MutableEvents<Unit>()
+        val stateEvents: Events<State<Int>> =
+            reset
+                .map { statefully { emitter.foldState(0) { _, count -> count + 1 } } }
+                .applyLatestStateful()
+        val activeState: State<State<Int>?> = stateEvents.holdState(null)
+
+        launchEffect {
+            // nothing is active yet
+            kairosNetwork.transact { assertEquals(activeState.sample(), null) }
+
+            // activate the counter
+            reset.emit(Unit)
+            val firstState =
+                kairosNetwork.transact {
+                    assertEquals(activeState.sample()?.sample(), 0)
+                    activeState.sample()!!
+                }
+
+            // emit twice
+            emitter.emit(Unit)
+            emitter.emit(Unit)
+            kairosNetwork.transact { assertEquals(firstState.sample(), 2) }
+
+            // start a new counter, disabling the old one
+            reset.emit(Unit)
+            val secondState =
+                kairosNetwork.transact {
+                    assertEquals(activeState.sample()?.sample(), 0)
+                    activeState.sample()!!
+                }
+            kairosNetwork.transact { assertEquals(firstState.sample(), 2) }
+
+            // emit: the new counter updates, but the old one does not
+            emitter.emit(Unit)
+            kairosNetwork.transact { assertEquals(secondState.sample(), 1) }
+            kairosNetwork.transact { assertEquals(firstState.sample(), 2) }
+        }
+    }
+
+    @Test fun test_applyLatestStatefulForKey() = runSample(block = applyLatestStatefulForKey())
+
+    fun applyLatestStatefulForKey(): BuildSpec<Unit> = buildSpec {
+        val reset = MutableEvents<String>()
+        val emitter = MutableEvents<String>()
+        val stateEvents: Events<MapPatch<String, State<Int>>> =
+            reset
+                .map { key ->
+                    mapOf(
+                        key to
+                            maybeOf(
+                                statefully {
+                                    emitter
+                                        .filter { it == key }
+                                        .foldState(0) { _, count -> count + 1 }
+                                }
+                            )
+                    )
+                }
+                .applyLatestStatefulForKey()
+        val activeStatesByKey: Incremental<String, State<Int>> =
+            stateEvents.foldStateMapIncrementally(emptyMap())
+
+        launchEffect {
+            // nothing is active yet
+            kairosNetwork.transact { assertEquals(activeStatesByKey.sample(), emptyMap()) }
+
+            // activate a new entry A
+            reset.emit("A")
+            val firstStateA =
+                kairosNetwork.transact {
+                    val stateMap: Map<String, State<Int>> = activeStatesByKey.sample()
+                    assertEquals(stateMap.keys, setOf("A"))
+                    stateMap.getValue("A").also { assertEquals(it.sample(), 0) }
+                }
+
+            // emit twice to A
+            emitter.emit("A")
+            emitter.emit("A")
+            kairosNetwork.transact { assertEquals(firstStateA.sample(), 2) }
+
+            // active a new entry B
+            reset.emit("B")
+            val firstStateB =
+                kairosNetwork.transact {
+                    val stateMap: Map<String, State<Int>> = activeStatesByKey.sample()
+                    assertEquals(stateMap.keys, setOf("A", "B"))
+                    stateMap.getValue("B").also {
+                        assertEquals(it.sample(), 0)
+                        assertEquals(firstStateA.sample(), 2)
+                    }
+                }
+
+            // emit once to B
+            emitter.emit("B")
+            kairosNetwork.transact {
+                assertEquals(firstStateA.sample(), 2)
+                assertEquals(firstStateB.sample(), 1)
+            }
+
+            // activate a new entry for A, disabling the old entry
+            reset.emit("A")
+            val secondStateA =
+                kairosNetwork.transact {
+                    val stateMap: Map<String, State<Int>> = activeStatesByKey.sample()
+                    assertEquals(stateMap.keys, setOf("A", "B"))
+                    stateMap.getValue("A").also {
+                        assertEquals(it.sample(), 0)
+                        assertEquals(firstStateB.sample(), 1)
+                    }
+                }
+
+            // emit to A: the new A state updates, but the old one does not
+            emitter.emit("A")
+            kairosNetwork.transact {
+                assertEquals(firstStateA.sample(), 2)
+                assertEquals(secondStateA.sample(), 1)
+            }
+        }
+    }
+
+    private fun runSample(
+        dispatcher: TestDispatcher = UnconfinedTestDispatcher(),
+        block: BuildScope.() -> Unit,
+    ) {
+        runTest(dispatcher, timeout = 1.seconds) {
+            val kairosNetwork = backgroundScope.launchKairosNetwork()
+            backgroundScope.launch { kairosNetwork.activateSpec { block() } }
+        }
+    }
+}
+
+private fun <T> assertEquals(actual: T, expected: T) = Assert.assertEquals(expected, actual)
diff --git a/packages/SystemUI/utils/kairos/test/com/android/systemui/kairos/KairosTests.kt b/packages/SystemUI/utils/kairos/test/com/android/systemui/kairos/KairosTests.kt
index 688adae..ffe6e95 100644
--- a/packages/SystemUI/utils/kairos/test/com/android/systemui/kairos/KairosTests.kt
+++ b/packages/SystemUI/utils/kairos/test/com/android/systemui/kairos/KairosTests.kt
@@ -1,32 +1,12 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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, ExperimentalFrpApi::class)
-
 package com.android.systemui.kairos
 
 import com.android.systemui.kairos.util.Either
-import com.android.systemui.kairos.util.Left
+import com.android.systemui.kairos.util.Either.First
+import com.android.systemui.kairos.util.Either.Second
 import com.android.systemui.kairos.util.Maybe
-import com.android.systemui.kairos.util.None
-import com.android.systemui.kairos.util.Right
-import com.android.systemui.kairos.util.just
+import com.android.systemui.kairos.util.Maybe.Absent
 import com.android.systemui.kairos.util.map
 import com.android.systemui.kairos.util.maybe
-import com.android.systemui.kairos.util.none
 import kotlin.time.Duration
 import kotlin.time.Duration.Companion.seconds
 import kotlin.time.DurationUnit
@@ -53,13 +33,15 @@
 import kotlinx.coroutines.test.runTest
 import org.junit.Assert.assertNull
 import org.junit.Assert.assertTrue
+import org.junit.Assert.fail
 import org.junit.Test
 
+@OptIn(ExperimentalCoroutinesApi::class)
 class KairosTests {
 
     @Test
     fun basic() = runFrpTest { network ->
-        val emitter = network.mutableTFlow<Int>()
+        val emitter = network.mutableEvents<Int>()
         var result: Int? = null
         activateSpec(network) { emitter.observe { result = it } }
         runCurrent()
@@ -70,8 +52,8 @@
     }
 
     @Test
-    fun basicTFlow() = runFrpTest { network ->
-        val emitter = network.mutableTFlow<Int>()
+    fun basicEvents() = runFrpTest { network ->
+        val emitter = network.mutableEvents<Int>()
         println("starting network")
         val result = activateSpecWithResult(network) { emitter.nextDeferred() }
         runCurrent()
@@ -84,9 +66,9 @@
     }
 
     @Test
-    fun basicTState() = runFrpTest { network ->
-        val emitter = network.mutableTFlow<Int>()
-        val result = activateSpecWithResult(network) { emitter.hold(0).stateChanges.nextDeferred() }
+    fun basicState() = runFrpTest { network ->
+        val emitter = network.mutableEvents<Int>()
+        val result = activateSpecWithResult(network) { emitter.holdState(0).changes.nextDeferred() }
         runCurrent()
 
         emitter.emit(3)
@@ -110,7 +92,7 @@
     fun basicTransactional() = runFrpTest { network ->
         var value: Int? = null
         var bSource = 1
-        val emitter = network.mutableTFlow<Unit>()
+        val emitter = network.mutableEvents<Unit>()
         // Sampling this transactional will increment the source count.
         val transactional = transactionally { bSource++ }
         measureTime {
@@ -149,24 +131,26 @@
 
     @Test
     fun diamondGraph() = runFrpTest { network ->
-        val flow = network.mutableTFlow<Int>()
-        val outFlow =
+        val flow = network.mutableEvents<Int>()
+        val ouevents =
             activateSpecWithResult(network) {
-                // map TFlow like we map Flow
+                // map Events like we map Flow
                 val left = flow.map { "left" to it }.onEach { println("left: $it") }
                 val right = flow.map { "right" to it }.onEach { println("right: $it") }
 
-                // convert TFlows to TStates so that they can be combined
+                // convert Eventss to States so that they can be combined
                 val combined =
-                    left.hold("left" to 0).combineWith(right.hold("right" to 0)) { l, r -> l to r }
-                combined.stateChanges // get TState changes
+                    left.holdState("left" to 0).combine(right.holdState("right" to 0)) { l, r ->
+                        l to r
+                    }
+                combined.changes // get State changes
                     .onEach { println("merged: $it") }
                     .toSharedFlow() // convert back to Flow
             }
         runCurrent()
 
         val results = mutableListOf<Pair<Pair<String, Int>, Pair<String, Int>>>()
-        backgroundScope.launch { outFlow.toCollection(results) }
+        backgroundScope.launch { ouevents.toCollection(results) }
         runCurrent()
 
         flow.emit(1)
@@ -185,19 +169,19 @@
     fun staticNetwork() = runFrpTest { network ->
         var finalSum: Int? = null
 
-        val intEmitter = network.mutableTFlow<Int>()
-        val sampleEmitter = network.mutableTFlow<Unit>()
+        val intEmitter = network.mutableEvents<Int>()
+        val sampleEmitter = network.mutableEvents<Unit>()
 
         activateSpecWithResult(network) {
                 val updates = intEmitter.map { a -> { b: Int -> a + b } }
 
                 val sumD =
-                    TStateLoop<Int>().apply {
+                    StateLoop<Int>().apply {
                         loopback =
                             updates
                                 .sample(this) { f, sum -> f(sum) }
                                 .onEach { println("sum update: $it") }
-                                .hold(0)
+                                .holdState(0)
                     }
                 sampleEmitter
                     .onEach { println("sampleEmitter emitted") }
@@ -227,10 +211,10 @@
         var wasSold = false
         var currentAmt: Int? = null
 
-        val coin = network.mutableTFlow<Unit>()
+        val coin = network.mutableEvents<Unit>()
         val price = 50
-        val frpSpec = frpSpec {
-            val eSold = TFlowLoop<Unit>()
+        val buildSpec = buildSpec {
+            val eSold = EventsLoop<Unit>()
 
             val eInsert =
                 coin.map {
@@ -250,10 +234,10 @@
 
             val eUpdate = eInsert.mergeWith(eReset) { f, g -> { a -> g(f(a)) } }
 
-            val dTotal = TStateLoop<Int>()
-            dTotal.loopback = eUpdate.sample(dTotal) { f, total -> f(total) }.hold(price)
+            val dTotal = StateLoop<Int>()
+            dTotal.loopback = eUpdate.sample(dTotal) { f, total -> f(total) }.holdState(price)
 
-            val eAmt = dTotal.stateChanges
+            val eAmt = dTotal.changes
             val bAmt = transactionally { dTotal.sample() }
             eSold.loopback =
                 coin
@@ -268,7 +252,7 @@
             eSold.nextDeferred()
         }
 
-        activateSpec(network) { frpSpec.applySpec() }
+        activateSpec(network) { buildSpec.applySpec() }
 
         runCurrent()
 
@@ -312,8 +296,8 @@
 
     @Test
     fun promptCleanup() = runFrpTest { network ->
-        val emitter = network.mutableTFlow<Int>()
-        val stopper = network.mutableTFlow<Unit>()
+        val emitter = network.mutableEvents<Int>()
+        val stopper = network.mutableEvents<Unit>()
 
         var result: Int? = null
 
@@ -331,19 +315,19 @@
     }
 
     @Test
-    fun switchTFlow() = runFrpTest { network ->
+    fun switchEvents() = runFrpTest { network ->
         var currentSum: Int? = null
 
-        val switchHandler = network.mutableTFlow<Pair<TFlow<Int>, String>>()
-        val aHandler = network.mutableTFlow<Int>()
-        val stopHandler = network.mutableTFlow<Unit>()
-        val bHandler = network.mutableTFlow<Int>()
+        val switchHandler = network.mutableEvents<Pair<Events<Int>, String>>()
+        val aHandler = network.mutableEvents<Int>()
+        val stopHandler = network.mutableEvents<Unit>()
+        val bHandler = network.mutableEvents<Int>()
 
         val sumFlow =
             activateSpecWithResult(network) {
-                val switchE = TFlowLoop<TFlow<Int>>()
+                val switchE = EventsLoop<Events<Int>>()
                 switchE.loopback =
-                    switchHandler.mapStateful { (intFlow, name) ->
+                    switchHandler.mapStateful { (inevents, name) ->
                         println("[onEach] Switching to: $name")
                         val nextSwitch =
                             switchE.skipNext().onEach { println("[onEach] switched-out") }
@@ -351,11 +335,11 @@
                             stopHandler
                                 .onEach { println("[onEach] stopped") }
                                 .mergeWith(nextSwitch) { _, b -> b }
-                        intFlow.takeUntil(stopEvent)
+                        inevents.takeUntil(stopEvent)
                     }
 
-                val adderE: TFlow<(Int) -> Int> =
-                    switchE.hold(emptyTFlow).switch().map { a ->
+                val adderE: Events<(Int) -> Int> =
+                    switchE.holdState(emptyEvents).switchEvents().map { a ->
                         println("[onEach] new number $a")
                         ({ sum: Int ->
                             println("$a+$sum=${a + sum}")
@@ -363,13 +347,13 @@
                         })
                     }
 
-                val sumD = TStateLoop<Int>()
+                val sumD = StateLoop<Int>()
                 sumD.loopback =
                     adderE
                         .sample(sumD) { f, sum -> f(sum) }
                         .onEach { println("[onEach] writing sum: $it") }
-                        .hold(0)
-                val sumE = sumD.stateChanges
+                        .holdState(0)
+                val sumE = sumD.changes
 
                 sumE.toSharedFlow()
             }
@@ -493,16 +477,16 @@
 
     @Test
     fun switchIndirect() = runFrpTest { network ->
-        val emitter = network.mutableTFlow<Unit>()
+        val emitter = network.mutableEvents<Unit>()
         activateSpec(network) {
-            emptyTFlow.map { emitter.map { 1 } }.flatten().map { "$it" }.observe()
+            emptyEvents.map { emitter.map { 1 } }.flatten().map { "$it" }.observe()
         }
         runCurrent()
     }
 
     @Test
     fun switchInWithResult() = runFrpTest { network ->
-        val emitter = network.mutableTFlow<Unit>()
+        val emitter = network.mutableEvents<Unit>()
         val out =
             activateSpecWithResult(network) {
                 emitter.map { emitter.map { 1 } }.flatten().toSharedFlow()
@@ -518,11 +502,11 @@
     fun switchInCompleted() = runFrpTest { network ->
         val outputs = mutableListOf<Int>()
 
-        val switchAH = network.mutableTFlow<Unit>()
-        val intAH = network.mutableTFlow<Int>()
-        val stopEmitter = network.mutableTFlow<Unit>()
+        val switchAH = network.mutableEvents<Unit>()
+        val intAH = network.mutableEvents<Int>()
+        val stopEmitter = network.mutableEvents<Unit>()
 
-        val top = frpSpec {
+        val top = buildSpec {
             val intS = intAH.takeUntil(stopEmitter)
             val switched = switchAH.map { intS }.flatten()
             switched.toSharedFlow()
@@ -554,13 +538,13 @@
     }
 
     @Test
-    fun switchTFlow_outerCompletesFirst() = runFrpTest { network ->
+    fun switchEvents_outerCompletesFirst() = runFrpTest { network ->
         var stepResult: Int? = null
 
-        val switchAH = network.mutableTFlow<Unit>()
-        val switchStopEmitter = network.mutableTFlow<Unit>()
-        val intStopEmitter = network.mutableTFlow<Unit>()
-        val intAH = network.mutableTFlow<Int>()
+        val switchAH = network.mutableEvents<Unit>()
+        val switchStopEmitter = network.mutableEvents<Unit>()
+        val intStopEmitter = network.mutableEvents<Unit>()
+        val intAH = network.mutableEvents<Int>()
         val flow =
             activateSpecWithResult(network) {
                 val intS = intAH.takeUntil(intStopEmitter)
@@ -604,12 +588,12 @@
         intStopEmitter.emit(Unit) // intAH.complete()
         runCurrent()
 
-        // assertEquals(just(10), network.await())
+        // assertEquals(present(10), network.await())
     }
 
     @Test
-    fun mapTFlow() = runFrpTest { network ->
-        val emitter = network.mutableTFlow<Int>()
+    fun mapEvents() = runFrpTest { network ->
+        val emitter = network.mutableEvents<Int>()
         var stepResult: Int? = null
 
         val flow =
@@ -643,7 +627,7 @@
         var pullValue = 0
         val a = transactionally { pullValue }
         val b = transactionally { a.sample() * 2 }
-        val emitter = network.mutableTFlow<Unit>()
+        val emitter = network.mutableEvents<Unit>()
         val flow =
             activateSpecWithResult(network) {
                 val sampleB = emitter.sample(b) { _, b -> b }
@@ -667,14 +651,14 @@
     }
 
     @Test
-    fun mapTState() = runFrpTest { network ->
-        val emitter = network.mutableTFlow<Int>()
+    fun mapState() = runFrpTest { network ->
+        val emitter = network.mutableEvents<Int>()
         var stepResult: Int? = null
         val flow =
             activateSpecWithResult(network) {
-                val state = emitter.hold(0).map { it + 2 }
+                val state = emitter.holdState(0).map { it + 2 }
                 val stateCurrent = transactionally { state.sample() }
-                val stateChanges = state.stateChanges
+                val stateChanges = state.changes
                 val sampleState = emitter.sample(stateCurrent) { _, b -> b }
                 val merge = stateChanges.mergeWith(sampleState) { a, b -> a + b }
                 merge.toSharedFlow()
@@ -695,38 +679,39 @@
 
     @Test
     fun partitionEither() = runFrpTest { network ->
-        val emitter = network.mutableTFlow<Either<Int, Int>>()
+        val emitter = network.mutableEvents<Either<Int, Int>>()
         val result =
             activateSpecWithResult(network) {
                 val (l, r) = emitter.partitionEither()
                 val pDiamond =
                     l.map { it * 2 }
                         .mergeWith(r.map { it * -1 }) { _, _ -> error("unexpected coincidence") }
-                pDiamond.hold(null).toStateFlow()
+                pDiamond.holdState(null).toStateFlow()
             }
         runCurrent()
 
-        emitter.emit(Left(10))
+        emitter.emit(First(10))
         runCurrent()
 
         assertEquals(20, result.value)
 
-        emitter.emit(Right(30))
+        emitter.emit(Second(30))
         runCurrent()
 
         assertEquals(-30, result.value)
     }
 
     @Test
-    fun accumTState() = runFrpTest { network ->
-        val emitter = network.mutableTFlow<Int>()
-        val sampler = network.mutableTFlow<Unit>()
+    fun accumState() = runFrpTest { network ->
+        val emitter = network.mutableEvents<Int>()
+        val sampler = network.mutableEvents<Unit>()
         var stepResult: Int? = null
         val flow =
             activateSpecWithResult(network) {
-                val sumState = emitter.map { a -> { b: Int -> a + b } }.fold(0) { f, a -> f(a) }
+                val sumState =
+                    emitter.map { a -> { b: Int -> a + b } }.foldState(0) { f, a -> f(a) }
 
-                sumState.stateChanges
+                sumState.changes
                     .mergeWith(sampler.sample(sumState) { _, sum -> sum }) { _, _ ->
                         error("Unexpected coincidence")
                     }
@@ -750,11 +735,11 @@
     }
 
     @Test
-    fun mergeTFlows() = runFrpTest { network ->
-        val first = network.mutableTFlow<Int>()
-        val stopFirst = network.mutableTFlow<Unit>()
-        val second = network.mutableTFlow<Int>()
-        val stopSecond = network.mutableTFlow<Unit>()
+    fun mergeEventss() = runFrpTest { network ->
+        val first = network.mutableEvents<Int>()
+        val stopFirst = network.mutableEvents<Unit>()
+        val second = network.mutableEvents<Int>()
+        val stopSecond = network.mutableEvents<Unit>()
         var stepResult: Int? = null
 
         val flow: SharedFlow<Int>
@@ -831,19 +816,19 @@
                 secondEmitDuration: ${secondEmitDuration.toString(DurationUnit.MILLISECONDS, 2)}
                 stopFirstDuration: ${stopFirstDuration.toString(DurationUnit.MILLISECONDS, 2)}
                 testDeadEmitFirstDuration: ${
-                    testDeadEmitFirstDuration.toString(
-                        DurationUnit.MILLISECONDS,
-                        2,
-                    )
-                }
+        testDeadEmitFirstDuration.toString(
+          DurationUnit.MILLISECONDS,
+          2,
+        )
+      }
                 secondEmitDuration2: ${secondEmitDuration2.toString(DurationUnit.MILLISECONDS, 2)}
                 stopSecondDuration: ${stopSecondDuration.toString(DurationUnit.MILLISECONDS, 2)}
                 testDeadEmitSecondDuration: ${
-                    testDeadEmitSecondDuration.toString(
-                        DurationUnit.MILLISECONDS,
-                        2,
-                    )
-                }
+        testDeadEmitSecondDuration.toString(
+          DurationUnit.MILLISECONDS,
+          2,
+        )
+      }
             """
                 .trimIndent()
         )
@@ -851,10 +836,10 @@
 
     @Test
     fun sampleCancel() = runFrpTest { network ->
-        val updater = network.mutableTFlow<Int>()
-        val stopUpdater = network.mutableTFlow<Unit>()
-        val sampler = network.mutableTFlow<Unit>()
-        val stopSampler = network.mutableTFlow<Unit>()
+        val updater = network.mutableEvents<Int>()
+        val stopUpdater = network.mutableEvents<Unit>()
+        val sampler = network.mutableEvents<Unit>()
+        val stopSampler = network.mutableEvents<Unit>()
         var stepResult: Int? = null
         val flow =
             activateSpecWithResult(network) {
@@ -862,7 +847,7 @@
                 val samplerS = sampler.takeUntil(stopSamplerFirst)
                 val stopUpdaterFirst = stopUpdater
                 val updaterS = updater.takeUntil(stopUpdaterFirst)
-                val sampledS = samplerS.sample(updaterS.hold(0)) { _, b -> b }
+                val sampledS = samplerS.sample(updaterS.holdState(0)) { _, b -> b }
                 sampledS.toSharedFlow()
             }
 
@@ -893,35 +878,35 @@
 
     @Test
     fun combineStates_differentUpstreams() = runFrpTest { network ->
-        val a = network.mutableTFlow<Int>()
-        val b = network.mutableTFlow<Int>()
+        val a = network.mutableEvents<Int>()
+        val b = network.mutableEvents<Int>()
         var observed: Pair<Int, Int>? = null
-        val tState =
+        val state =
             activateSpecWithResult(network) {
-                val state = combine(a.hold(0), b.hold(0)) { a, b -> Pair(a, b) }
-                state.stateChanges.observe { observed = it }
+                val state = combine(a.holdState(0), b.holdState(0)) { a, b -> Pair(a, b) }
+                state.changes.observe { observed = it }
                 state
             }
-        assertEquals(0 to 0, network.transact { tState.sample() })
+        assertEquals(0 to 0, network.transact { state.sample() })
         assertEquals(null, observed)
         a.emit(5)
         assertEquals(5 to 0, observed)
-        assertEquals(5 to 0, network.transact { tState.sample() })
+        assertEquals(5 to 0, network.transact { state.sample() })
         b.emit(3)
         assertEquals(5 to 3, observed)
-        assertEquals(5 to 3, network.transact { tState.sample() })
+        assertEquals(5 to 3, network.transact { state.sample() })
     }
 
     @Test
     fun sampleCombinedStates() = runFrpTest { network ->
-        val updater = network.mutableTFlow<Int>()
-        val emitter = network.mutableTFlow<Unit>()
+        val updater = network.mutableEvents<Int>()
+        val emitter = network.mutableEvents<Unit>()
 
         val result =
             activateSpecWithResult(network) {
-                val bA = updater.map { it * 2 }.hold(0)
-                val bB = updater.hold(0)
-                val combineD: TState<Pair<Int, Int>> = bA.combineWith(bB) { a, b -> a to b }
+                val bA = updater.map { it * 2 }.holdState(0)
+                val bB = updater.holdState(0)
+                val combineD: State<Pair<Int, Int>> = bA.combine(bB) { a, b -> a to b }
                 val sampleS = emitter.sample(combineD) { _, b -> b }
                 sampleS.nextDeferred()
             }
@@ -942,13 +927,13 @@
 
     @Test
     fun switchMapPromptly() = runFrpTest { network ->
-        val emitter = network.mutableTFlow<Unit>()
+        val emitter = network.mutableEvents<Unit>()
         val result =
             activateSpecWithResult(network) {
                 emitter
                     .map { emitter.map { 1 }.map { it + 1 }.map { it * 2 } }
-                    .hold(emptyTFlow)
-                    .switchPromptly()
+                    .holdState(emptyEvents)
+                    .switchEventsPromptly()
                     .nextDeferred()
             }
         runCurrent()
@@ -962,8 +947,8 @@
 
     @Test
     fun switchDeeper() = runFrpTest { network ->
-        val emitter = network.mutableTFlow<Unit>()
-        val e2 = network.mutableTFlow<Unit>()
+        val emitter = network.mutableEvents<Unit>()
+        val e2 = network.mutableEvents<Unit>()
         val result =
             activateSpecWithResult(network) {
                 val tres =
@@ -988,14 +973,14 @@
 
     @Test
     fun recursionBasic() = runFrpTest { network ->
-        val add1 = network.mutableTFlow<Unit>()
-        val sub1 = network.mutableTFlow<Unit>()
+        val add1 = network.mutableEvents<Unit>()
+        val sub1 = network.mutableEvents<Unit>()
         val stepResult: StateFlow<Int> =
             activateSpecWithResult(network) {
-                val dSum = TStateLoop<Int>()
+                val dSum = StateLoop<Int>()
                 val sAdd1 = add1.sample(dSum) { _, sum -> sum + 1 }
                 val sMinus1 = sub1.sample(dSum) { _, sum -> sum - 1 }
-                dSum.loopback = sAdd1.mergeWith(sMinus1) { a, _ -> a }.hold(0)
+                dSum.loopback = sAdd1.mergeWith(sMinus1) { a, _ -> a }.holdState(0)
                 dSum.toStateFlow()
             }
         runCurrent()
@@ -1017,16 +1002,17 @@
     }
 
     @Test
-    fun recursiveTState() = runFrpTest { network ->
-        val e = network.mutableTFlow<Unit>()
+    fun recursiveState() = runFrpTest { network ->
+        val e = network.mutableEvents<Unit>()
         var changes = 0
         val state =
             activateSpecWithResult(network) {
-                val s = TFlowLoop<Unit>()
-                val deferred = s.map { tStateOf(null) }
-                val e3 = e.map { tStateOf(Unit) }
-                val flattened = e3.mergeWith(deferred) { a, _ -> a }.hold(tStateOf(null)).flatten()
-                s.loopback = emptyTFlow
+                val s = EventsLoop<Unit>()
+                val deferred = s.map { stateOf(null) }
+                val e3 = e.map { stateOf(Unit) }
+                val flattened =
+                    e3.mergeWith(deferred) { a, _ -> a }.holdState(stateOf(null)).flatten()
+                s.loopback = emptyEvents
                 flattened.toStateFlow()
             }
 
@@ -1036,7 +1022,7 @@
 
     @Test
     fun fanOut() = runFrpTest { network ->
-        val e = network.mutableTFlow<Map<String, Int>>()
+        val e = network.mutableEvents<Map<String, Int>>()
         val (fooFlow, barFlow) =
             activateSpecWithResult(network) {
                 val selector = e.groupByKey()
@@ -1057,16 +1043,30 @@
     }
 
     @Test
+    fun propagateError() {
+        try {
+            runFrpTest { network ->
+                runCurrent()
+                try {
+                    network.transact<Unit> { error("message") }
+                    fail("caller did not throw exception")
+                } catch (_: IllegalStateException) {}
+            }
+            fail("scheduler did not throw exception")
+        } catch (_: IllegalStateException) {}
+    }
+
+    @Test
     fun fanOutLateSubscribe() = runFrpTest { network ->
-        val e = network.mutableTFlow<Map<String, Int>>()
+        val e = network.mutableEvents<Map<String, Int>>()
         val barFlow =
             activateSpecWithResult(network) {
                 val selector = e.groupByKey()
                 selector
                     .eventsForKey("foo")
                     .map { selector.eventsForKey("bar") }
-                    .hold(emptyTFlow)
-                    .switchPromptly()
+                    .holdState(emptyEvents)
+                    .switchEventsPromptly()
                     .toSharedFlow()
             }
         val stateFlow = barFlow.stateIn(backgroundScope, SharingStarted.Eagerly, null)
@@ -1081,9 +1081,9 @@
     }
 
     @Test
-    fun inputFlowCompleted() = runFrpTest { network ->
+    fun inputEventsCompleted() = runFrpTest { network ->
         val results = mutableListOf<Int>()
-        val e = network.mutableTFlow<Int>()
+        val e = network.mutableEvents<Int>()
         activateSpec(network) { e.nextOnly().observe { results.add(it) } }
         runCurrent()
 
@@ -1099,49 +1099,56 @@
 
     @Test
     fun fanOutThenMergeIncrementally() = runFrpTest { network ->
-        // A tflow of group updates, where a group is a tflow of child updates, where a child is a
+        // A events of group updates, where a group is a events of child updates, where a child is a
         // stateflow
-        val e = network.mutableTFlow<Map<Int, Maybe<TFlow<Map<Int, Maybe<StateFlow<String>>>>>>>()
+        val e = network.mutableEvents<Map<Int, Maybe<Events<Map<Int, Maybe<StateFlow<String>>>>>>>()
         println("fanOutMergeInc START")
         val state =
             activateSpecWithResult(network) {
-                // Convert nested Flows to nested TFlow/TState
-                val emitter: TFlow<Map<Int, Maybe<TFlow<Map<Int, Maybe<TState<String>>>>>>> =
+                // Convert nested Flows to nested Events/State
+                val emitter: Events<Map<Int, Maybe<Events<Map<Int, Maybe<State<String>>>>>>> =
                     e.mapBuild { m ->
                         m.mapValues { (_, mFlow) ->
                             mFlow.map {
                                 it.mapBuild { m2 ->
+                                    println("m2: $m2")
                                     m2.mapValues { (_, mState) ->
-                                        mState.map { stateFlow -> stateFlow.toTState() }
+                                        mState.map { stateFlow -> stateFlow.toState() }
                                     }
                                 }
                             }
                         }
                     }
-                // Accumulate all of our updates into a single TState
-                val accState: TState<Map<Int, Map<Int, String>>> =
+                // Accumulate all of our updates into a single State
+                val accState: State<Map<Int, Map<Int, String>>> =
                     emitter
                         .mapStateful {
-                            changeMap: Map<Int, Maybe<TFlow<Map<Int, Maybe<TState<String>>>>>> ->
+                            changeMap: Map<Int, Maybe<Events<Map<Int, Maybe<State<String>>>>>> ->
                             changeMap.mapValues { (groupId, mGroupChanges) ->
                                 mGroupChanges.map {
-                                    groupChanges: TFlow<Map<Int, Maybe<TState<String>>>> ->
+                                    groupChanges: Events<Map<Int, Maybe<State<String>>>> ->
                                     // New group
                                     val childChangeById = groupChanges.groupByKey()
-                                    val map: TFlow<Map<Int, Maybe<TFlow<Maybe<TState<String>>>>>> =
+                                    val map: Events<Map<Int, Maybe<Events<Maybe<State<String>>>>>> =
                                         groupChanges.mapStateful {
-                                            gChangeMap: Map<Int, Maybe<TState<String>>> ->
+                                            gChangeMap: Map<Int, Maybe<State<String>>> ->
+                                            println("gChangeMap: $gChangeMap")
                                             gChangeMap.mapValues { (childId, mChild) ->
-                                                mChild.map { child: TState<String> ->
+                                                mChild.map { child: State<String> ->
                                                     println("new child $childId in the house")
                                                     // New child
                                                     val eRemoved =
                                                         childChangeById
                                                             .eventsForKey(childId)
-                                                            .filter { it === None }
+                                                            .filter { it === Absent }
+                                                            .onEach {
+                                                                println(
+                                                                    "removing? (groupId=$groupId, childId=$childId)"
+                                                                )
+                                                            }
                                                             .nextOnly()
 
-                                                    val addChild: TFlow<Maybe<TState<String>>> =
+                                                    val addChild: Events<Maybe<State<String>>> =
                                                         now.map { mChild }
                                                             .onEach {
                                                                 println(
@@ -1149,14 +1156,14 @@
                                                                 )
                                                             }
 
-                                                    val removeChild: TFlow<Maybe<TState<String>>> =
+                                                    val removeChild: Events<Maybe<State<String>>> =
                                                         eRemoved
                                                             .onEach {
                                                                 println(
                                                                     "removeChild (groupId=$groupId, childId=$childId)"
                                                                 )
                                                             }
-                                                            .map { none() }
+                                                            .map { Maybe.absent() }
 
                                                     addChild.mergeWith(removeChild) { _, _ ->
                                                         error("unexpected coincidence")
@@ -1164,17 +1171,18 @@
                                                 }
                                             }
                                         }
-                                    val mergeIncrementally: TFlow<Map<Int, Maybe<TState<String>>>> =
+                                    val mergeIncrementally: Events<Map<Int, Maybe<State<String>>>> =
                                         map.onEach { println("merge patch: $it") }
-                                            .mergeIncrementallyPromptly()
+                                            .mergeEventsIncrementallyPromptly()
                                     mergeIncrementally
-                                        .onEach { println("patch: $it") }
-                                        .foldMapIncrementally()
+                                        .onEach { println("foldmap patch: $it") }
+                                        .foldStateMapIncrementally()
                                         .flatMap { it.combine() }
                                 }
                             }
                         }
-                        .foldMapIncrementally()
+                        .onEach { println("fold patch: $it") }
+                        .foldStateMapIncrementally()
                         .flatMap { it.combine() }
 
                 accState.toStateFlow()
@@ -1183,17 +1191,17 @@
 
         assertEquals(emptyMap(), state.value)
 
-        val emitter2 = network.mutableTFlow<Map<Int, Maybe<StateFlow<String>>>>()
+        val emitter2 = network.mutableEvents<Map<Int, Maybe<StateFlow<String>>>>()
         println()
         println("init outer 0")
-        e.emit(mapOf(0 to just(emitter2.onEach { println("emitter2 emit: $it") })))
+        e.emit(mapOf(0 to Maybe.present(emitter2.onEach { println("emitter2 emit: $it") })))
         runCurrent()
 
         assertEquals(mapOf(0 to emptyMap()), state.value)
 
         println()
         println("init inner 10")
-        emitter2.emit(mapOf(10 to just(MutableStateFlow("(0, 10)"))))
+        emitter2.emit(mapOf(10 to Maybe.present(MutableStateFlow("(0, 10)"))))
         runCurrent()
 
         assertEquals(mapOf(0 to mapOf(10 to "(0, 10)")), state.value)
@@ -1201,29 +1209,33 @@
         // replace
         println()
         println("replace inner 10")
-        emitter2.emit(mapOf(10 to just(MutableStateFlow("(1, 10)"))))
+        emitter2.emit(mapOf(10 to Maybe.present(MutableStateFlow("(1, 10)"))))
         runCurrent()
 
         assertEquals(mapOf(0 to mapOf(10 to "(1, 10)")), state.value)
 
         // remove
-        emitter2.emit(mapOf(10 to none()))
+        emitter2.emit(mapOf(10 to Maybe.absent()))
         runCurrent()
 
         assertEquals(mapOf(0 to emptyMap()), state.value)
 
         // add again
-        emitter2.emit(mapOf(10 to just(MutableStateFlow("(2, 10)"))))
+        emitter2.emit(mapOf(10 to Maybe.present(MutableStateFlow("(2, 10)"))))
         runCurrent()
 
         assertEquals(mapOf(0 to mapOf(10 to "(2, 10)")), state.value)
 
+        // LogEnabled = true
+
+        println("batch update")
+
         // batch update
         emitter2.emit(
             mapOf(
-                10 to none(),
-                11 to just(MutableStateFlow("(0, 11)")),
-                12 to just(MutableStateFlow("(0, 12)")),
+                10 to Maybe.absent(),
+                11 to Maybe.present(MutableStateFlow("(0, 11)")),
+                12 to Maybe.present(MutableStateFlow("(0, 12)")),
             )
         )
         runCurrent()
@@ -1233,13 +1245,13 @@
 
     @Test
     fun applyLatestNetworkChanges() = runFrpTest { network ->
-        val newCount = network.mutableTFlow<FrpSpec<Flow<Int>>>()
+        val newCount = network.mutableEvents<BuildSpec<Flow<Int>>>()
         val flowOfFlows: Flow<Flow<Int>> =
             activateSpecWithResult(network) { newCount.applyLatestSpec().toSharedFlow() }
         runCurrent()
 
-        val incCount = network.mutableTFlow<Unit>()
-        fun newFlow(): FrpSpec<SharedFlow<Int>> = frpSpec {
+        val incCount = network.mutableEvents<Unit>()
+        fun newFlow(): BuildSpec<SharedFlow<Int>> = buildSpec {
             launchEffect {
                 try {
                     println("new flow!")
@@ -1248,16 +1260,16 @@
                     println("cancelling old flow")
                 }
             }
-            lateinit var count: TState<Int>
+            lateinit var count: State<Int>
             count =
                 incCount
                     .onEach { println("incrementing ${count.sample()}") }
-                    .fold(0) { _, c -> c + 1 }
-            count.stateChanges.toSharedFlow()
+                    .foldState(0) { _, c -> c + 1 }
+            count.changes.toSharedFlow()
         }
 
         var outerCount = 0
-        val lastFlows: StateFlow<Pair<StateFlow<Int?>, StateFlow<Int?>>> =
+        val lastEvent: StateFlow<Pair<StateFlow<Int?>, StateFlow<Int?>>> =
             flowOfFlows
                 .map { it.stateIn(backgroundScope, SharingStarted.Eagerly, null) }
                 .pairwise(MutableStateFlow(null))
@@ -1275,18 +1287,18 @@
 
         assertEquals(1, outerCount)
         //        assertEquals(1, incCount.subscriptionCount)
-        assertNull(lastFlows.value.second.value)
+        assertNull(lastEvent.value.second.value)
 
         incCount.emit(Unit)
         runCurrent()
 
         println("checking")
-        assertEquals(1, lastFlows.value.second.value)
+        assertEquals(1, lastEvent.value.second.value)
 
         incCount.emit(Unit)
         runCurrent()
 
-        assertEquals(2, lastFlows.value.second.value)
+        assertEquals(2, lastEvent.value.second.value)
 
         newCount.emit(newFlow())
         runCurrent()
@@ -1294,18 +1306,18 @@
         runCurrent()
 
         // verify old flow is not getting updates
-        assertEquals(2, lastFlows.value.first.value)
+        assertEquals(2, lastEvent.value.first.value)
         // but the new one is
-        assertEquals(1, lastFlows.value.second.value)
+        assertEquals(1, lastEvent.value.second.value)
     }
 
     @Test
     fun buildScope_stateAccumulation() = runFrpTest { network ->
-        val input = network.mutableTFlow<Unit>()
+        val input = network.mutableEvents<Unit>()
         var observedCount: Int? = null
         activateSpec(network) {
-            val (c, j) = asyncScope { input.fold(0) { _, x -> x + 1 } }
-            deferredBuildScopeAction { c.get().observe { observedCount = it } }
+            val (c, j) = asyncScope { input.foldState(0) { _, x -> x + 1 } }
+            deferredBuildScopeAction { c.value.observe { observedCount = it } }
         }
         runCurrent()
         assertEquals(0, observedCount)
@@ -1321,7 +1333,7 @@
 
     @Test
     fun effect() = runFrpTest { network ->
-        val input = network.mutableTFlow<Unit>()
+        val input = network.mutableEvents<Unit>()
         var effectRunning = false
         var count = 0
         activateSpec(network) {
@@ -1333,7 +1345,7 @@
                     effectRunning = false
                 }
             }
-            merge(emptyTFlow, input.nextOnly()).observe {
+            merge(emptyEvents, input.nextOnly()).observe {
                 count++
                 j.cancel()
             }
@@ -1355,23 +1367,93 @@
         assertEquals(1, count)
     }
 
+    @Test
+    fun observeEffect_disposeHandle() = runFrpTest { network ->
+        val input = network.mutableEvents<Unit>()
+        val stopper = network.mutableEvents<Unit>()
+        var runningCount = 0
+        val specJob =
+            activateSpec(network) {
+                val handle =
+                    input.observe {
+                        launch {
+                            runningCount++
+                            awaitClose { runningCount-- }
+                        }
+                    }
+                stopper.nextOnly().observe { handle.dispose() }
+            }
+        runCurrent()
+        assertEquals(0, runningCount)
+
+        input.emit(Unit)
+        assertEquals(1, runningCount)
+
+        input.emit(Unit)
+        assertEquals(2, runningCount)
+
+        stopper.emit(Unit)
+        assertEquals(2, runningCount)
+
+        input.emit(Unit)
+        assertEquals(2, runningCount)
+
+        specJob.cancel()
+        runCurrent()
+        assertEquals(0, runningCount)
+    }
+
+    @Test
+    fun observeEffect_takeUntil() = runFrpTest { network ->
+        val input = network.mutableEvents<Unit>()
+        val stopper = network.mutableEvents<Unit>()
+        var runningCount = 0
+        val specJob =
+            activateSpec(network) {
+                input.takeUntil(stopper).observe {
+                    launch {
+                        runningCount++
+                        awaitClose { runningCount-- }
+                    }
+                }
+            }
+        runCurrent()
+        assertEquals(0, runningCount)
+
+        input.emit(Unit)
+        assertEquals(1, runningCount)
+
+        input.emit(Unit)
+        assertEquals(2, runningCount)
+
+        stopper.emit(Unit)
+        assertEquals(2, runningCount)
+
+        input.emit(Unit)
+        assertEquals(2, runningCount)
+
+        specJob.cancel()
+        runCurrent()
+        assertEquals(0, runningCount)
+    }
+
     private fun runFrpTest(
         timeout: Duration = 3.seconds,
-        block: suspend TestScope.(FrpNetwork) -> Unit,
+        block: suspend TestScope.(KairosNetwork) -> Unit,
     ) {
         runTest(timeout = timeout) {
-            val network = backgroundScope.newFrpNetwork()
+            val network = backgroundScope.launchKairosNetwork()
             runCurrent()
             block(network)
         }
     }
 
-    private fun TestScope.activateSpec(network: FrpNetwork, spec: FrpSpec<*>) =
+    private fun TestScope.activateSpec(network: KairosNetwork, spec: BuildSpec<*>) =
         backgroundScope.launch { network.activateSpec(spec) }
 
     private suspend fun <R> TestScope.activateSpecWithResult(
-        network: FrpNetwork,
-        spec: FrpSpec<R>,
+        network: KairosNetwork,
+        spec: BuildSpec<R>,
     ): R =
         CompletableDeferred<R>()
             .apply { activateSpec(network) { complete(spec.applySpec()) } }
diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto
index 6489905..3a38152 100644
--- a/proto/src/system_messages.proto
+++ b/proto/src/system_messages.proto
@@ -420,5 +420,9 @@
     // Notify the user that accessibility floating menu is hidden.
     // Package: com.android.systemui
     NOTE_A11Y_FLOATING_MENU_HIDDEN = 1009;
+
+    // Notify the hearing aid user that input device can be changed to builtin device or hearing device.
+    // Package: android
+    NOTE_HEARING_DEVICE_INPUT_SWITCH = 1012;
   }
 }
diff --git a/ravenwood/tests/bivalenttest/Android.bp b/ravenwood/tests/bivalenttest/Android.bp
index ac545df..c4086c5 100644
--- a/ravenwood/tests/bivalenttest/Android.bp
+++ b/ravenwood/tests/bivalenttest/Android.bp
@@ -40,6 +40,7 @@
 
         "junit-params",
         "platform-parametric-runner-lib",
+        "platform-compat-test-rules",
 
         // To make sure it won't cause VerifyError (b/324063814)
         "platformprotosnano",
diff --git a/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/compat/RavenwoodCompatFrameworkTest.kt b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/compat/RavenwoodCompatFrameworkTest.kt
index 882c91c..540b082 100644
--- a/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/compat/RavenwoodCompatFrameworkTest.kt
+++ b/ravenwood/tests/bivalenttest/test/com/android/ravenwoodtest/bivalenttest/compat/RavenwoodCompatFrameworkTest.kt
@@ -16,31 +16,52 @@
 package com.android.ravenwoodtest.bivalenttest.compat
 
 import android.app.compat.CompatChanges
+import android.compat.testing.PlatformCompatChangeRule
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.android.internal.ravenwood.RavenwoodEnvironment.CompatIdsForTest
-import org.junit.Assert
+import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges
+import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
 
 @RunWith(AndroidJUnit4::class)
 class RavenwoodCompatFrameworkTest {
+
+    @get:Rule
+    val compatRule = PlatformCompatChangeRule()
+
     @Test
     fun testEnabled() {
-        Assert.assertTrue(CompatChanges.isChangeEnabled(CompatIdsForTest.TEST_COMPAT_ID_1))
+        assertTrue(CompatChanges.isChangeEnabled(CompatIdsForTest.TEST_COMPAT_ID_1))
     }
 
     @Test
     fun testDisabled() {
-        Assert.assertFalse(CompatChanges.isChangeEnabled(CompatIdsForTest.TEST_COMPAT_ID_2))
+        assertFalse(CompatChanges.isChangeEnabled(CompatIdsForTest.TEST_COMPAT_ID_2))
     }
 
     @Test
     fun testEnabledAfterSForUApps() {
-        Assert.assertTrue(CompatChanges.isChangeEnabled(CompatIdsForTest.TEST_COMPAT_ID_3))
+        assertTrue(CompatChanges.isChangeEnabled(CompatIdsForTest.TEST_COMPAT_ID_3))
     }
 
     @Test
     fun testEnabledAfterUForUApps() {
-        Assert.assertFalse(CompatChanges.isChangeEnabled(CompatIdsForTest.TEST_COMPAT_ID_4))
+        assertFalse(CompatChanges.isChangeEnabled(CompatIdsForTest.TEST_COMPAT_ID_4))
+    }
+
+    @Test
+    @EnableCompatChanges(CompatIdsForTest.TEST_COMPAT_ID_5)
+    fun testEnableCompatChanges() {
+        assertTrue(CompatChanges.isChangeEnabled(CompatIdsForTest.TEST_COMPAT_ID_5))
+    }
+
+    @Test
+    @DisableCompatChanges(CompatIdsForTest.TEST_COMPAT_ID_5)
+    fun testDisableCompatChanges() {
+        assertFalse(CompatChanges.isChangeEnabled(CompatIdsForTest.TEST_COMPAT_ID_5))
     }
 }
diff --git a/ravenwood/texts/ravenwood-annotation-allowed-classes.txt b/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
index c196a09..f8315fe 100644
--- a/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
+++ b/ravenwood/texts/ravenwood-annotation-allowed-classes.txt
@@ -30,6 +30,10 @@
 android.util.ArrayMap
 android.util.ArraySet
 android.util.AtomicFile
+android.util.AtomicFileOutputStream
+android.util.AtomicFileBufferedOutputStream
+android.util.AtomicFilePrintWriter
+android.util.AtomicFileBufferedPrintWriter
 android.util.BackupUtils
 android.util.Base64
 android.util.Base64DataException
diff --git a/services/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig
index ad87cea..7f0bf03 100644
--- a/services/accessibility/accessibility.aconfig
+++ b/services/accessibility/accessibility.aconfig
@@ -79,6 +79,13 @@
 }
 
 flag {
+    name: "enable_autoclick_indicator"
+    namespace: "accessibility"
+    description: "Whether to show autoclick indicator when autoclick feature is enabled."
+    bug: "383901288"
+}
+
+flag {
     name: "enable_a11y_checker_logging"
     namespace: "accessibility"
     description: "Whether to identify and log app a11y issues."
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 37d045b..8e037c3 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -413,6 +413,7 @@
     private SparseArray<SurfaceControl> mA11yOverlayLayers = new SparseArray<>();
 
     private final FlashNotificationsController mFlashNotificationsController;
+    private final HearingDevicePhoneCallNotificationController mHearingDeviceNotificationController;
     private final UserManagerInternal mUmi;
 
     private AccessibilityUserState getCurrentUserStateLocked() {
@@ -541,7 +542,8 @@
             MagnificationController magnificationController,
             @Nullable AccessibilityInputFilter inputFilter,
             ProxyManager proxyManager,
-            PermissionEnforcer permissionEnforcer) {
+            PermissionEnforcer permissionEnforcer,
+            HearingDevicePhoneCallNotificationController hearingDeviceNotificationController) {
         super(permissionEnforcer);
         mContext = context;
         mPowerManager =  (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
@@ -569,6 +571,11 @@
         // TODO(b/255426725): not used on tests
         mVisibleBgUserIds = null;
         mInputManager = context.getSystemService(InputManager.class);
+        if (com.android.settingslib.flags.Flags.hearingDevicesInputRoutingControl()) {
+            mHearingDeviceNotificationController = hearingDeviceNotificationController;
+        } else {
+            mHearingDeviceNotificationController = null;
+        }
 
         init();
     }
@@ -618,6 +625,12 @@
         } else {
             mVisibleBgUserIds = null;
         }
+        if (com.android.settingslib.flags.Flags.hearingDevicesInputRoutingControl()) {
+            mHearingDeviceNotificationController = new HearingDevicePhoneCallNotificationController(
+                    context);
+        } else {
+            mHearingDeviceNotificationController = null;
+        }
 
         init();
     }
@@ -630,6 +643,11 @@
         if (enableTalkbackAndMagnifierKeyGestures()) {
             mInputManager.registerKeyGestureEventHandler(mKeyGestureEventHandler);
         }
+        if (com.android.settingslib.flags.Flags.hearingDevicesInputRoutingControl()) {
+            if (mHearingDeviceNotificationController != null) {
+                mHearingDeviceNotificationController.startListenForCallState();
+            }
+        }
         disableAccessibilityMenuToMigrateIfNeeded();
     }
 
diff --git a/services/accessibility/java/com/android/server/accessibility/AutoclickController.java b/services/accessibility/java/com/android/server/accessibility/AutoclickController.java
index b095e3e..b94fa2f 100644
--- a/services/accessibility/java/com/android/server/accessibility/AutoclickController.java
+++ b/services/accessibility/java/com/android/server/accessibility/AutoclickController.java
@@ -16,11 +16,17 @@
 
 package com.android.server.accessibility;
 
+import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+
+import static com.android.server.accessibility.AutoclickIndicatorView.SHOW_INDICATOR_DELAY_TIME;
+
 import android.accessibilityservice.AccessibilityTrace;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.database.ContentObserver;
+import android.graphics.PixelFormat;
 import android.net.Uri;
 import android.os.Handler;
 import android.os.SystemClock;
@@ -30,6 +36,7 @@
 import android.view.MotionEvent;
 import android.view.MotionEvent.PointerCoords;
 import android.view.MotionEvent.PointerProperties;
+import android.view.WindowManager;
 import android.view.accessibility.AccessibilityManager;
 
 /**
@@ -63,7 +70,10 @@
 
     // Lazily created on the first mouse motion event.
     private ClickScheduler mClickScheduler;
-    private ClickDelayObserver mClickDelayObserver;
+    private AutoclickSettingsObserver mAutoclickSettingsObserver;
+    private AutoclickIndicatorScheduler mAutoclickIndicatorScheduler;
+    private AutoclickIndicatorView mAutoclickIndicatorView;
+    private WindowManager mWindowManager;
 
     public AutoclickController(Context context, int userId, AccessibilityTraceManager trace) {
         mTrace = trace;
@@ -80,10 +90,17 @@
         if (event.isFromSource(InputDevice.SOURCE_MOUSE)) {
             if (mClickScheduler == null) {
                 Handler handler = new Handler(mContext.getMainLooper());
+                if (Flags.enableAutoclickIndicator()) {
+                    initiateAutoclickIndicator(handler);
+                }
+
                 mClickScheduler =
                         new ClickScheduler(handler, AccessibilityManager.AUTOCLICK_DELAY_DEFAULT);
-                mClickDelayObserver = new ClickDelayObserver(mUserId, handler);
-                mClickDelayObserver.start(mContext.getContentResolver(), mClickScheduler);
+                mAutoclickSettingsObserver = new AutoclickSettingsObserver(mUserId, handler);
+                mAutoclickSettingsObserver.start(
+                        mContext.getContentResolver(),
+                        mClickScheduler,
+                        mAutoclickIndicatorScheduler);
             }
 
             handleMouseMotion(event, policyFlags);
@@ -94,6 +111,27 @@
         super.onMotionEvent(event, rawEvent, policyFlags);
     }
 
+    private void initiateAutoclickIndicator(Handler handler) {
+        mAutoclickIndicatorScheduler = new AutoclickIndicatorScheduler(handler);
+        mAutoclickIndicatorView = new AutoclickIndicatorView(mContext);
+
+        final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
+        layoutParams.type = WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY;
+        layoutParams.flags =
+                WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
+                        | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                        | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
+        layoutParams.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
+        layoutParams.setFitInsetsTypes(0);
+        layoutParams.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
+        layoutParams.format = PixelFormat.TRANSLUCENT;
+        layoutParams.setTitle(AutoclickIndicatorView.class.getSimpleName());
+        layoutParams.inputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL;
+
+        mWindowManager = mContext.getSystemService(WindowManager.class);
+        mWindowManager.addView(mAutoclickIndicatorView, layoutParams);
+    }
+
     @Override
     public void onKeyEvent(KeyEvent event, int policyFlags) {
         if (mTrace.isA11yTracingEnabledForTypes(AccessibilityTrace.FLAGS_INPUT_FILTER)) {
@@ -122,14 +160,20 @@
 
     @Override
     public void onDestroy() {
-        if (mClickDelayObserver != null) {
-            mClickDelayObserver.stop();
-            mClickDelayObserver = null;
+        if (mAutoclickSettingsObserver != null) {
+            mAutoclickSettingsObserver.stop();
+            mAutoclickSettingsObserver = null;
         }
         if (mClickScheduler != null) {
             mClickScheduler.cancel();
             mClickScheduler = null;
         }
+
+        if (mAutoclickIndicatorScheduler != null) {
+            mAutoclickIndicatorScheduler.cancel();
+            mAutoclickIndicatorScheduler = null;
+            mWindowManager.removeView(mAutoclickIndicatorView);
+        }
     }
 
     private void handleMouseMotion(MotionEvent event, int policyFlags) {
@@ -151,19 +195,24 @@
     }
 
     /**
-     * Observes setting value for autoclick delay, and updates ClickScheduler delay whenever the
-     * setting value changes.
+     * Observes autoclick setting values, and updates ClickScheduler delay and indicator size
+     * whenever the setting value changes.
      */
-    final private static class ClickDelayObserver extends ContentObserver {
+    final private static class AutoclickSettingsObserver extends ContentObserver {
         /** URI used to identify the autoclick delay setting with content resolver. */
         private final Uri mAutoclickDelaySettingUri = Settings.Secure.getUriFor(
                 Settings.Secure.ACCESSIBILITY_AUTOCLICK_DELAY);
 
+        /** URI used to identify the autoclick cursor area size setting with content resolver. */
+        private final Uri mAutoclickCursorAreaSizeSettingUri =
+                Settings.Secure.getUriFor(Settings.Secure.ACCESSIBILITY_AUTOCLICK_CURSOR_AREA_SIZE);
+
         private ContentResolver mContentResolver;
         private ClickScheduler mClickScheduler;
+        private AutoclickIndicatorScheduler mAutoclickIndicatorScheduler;
         private final int mUserId;
 
-        public ClickDelayObserver(int userId, Handler handler) {
+        public AutoclickSettingsObserver(int userId, Handler handler) {
             super(handler);
             mUserId = userId;
         }
@@ -176,11 +225,13 @@
          *     changes.
          * @param clickScheduler ClickScheduler that should be updated when click delay changes.
          * @throws IllegalStateException If internal state is already setup when the method is
-         *         called.
+         *     called.
          * @throws NullPointerException If any of the arguments is a null pointer.
          */
-        public void start(@NonNull ContentResolver contentResolver,
-                @NonNull ClickScheduler clickScheduler) {
+        public void start(
+                @NonNull ContentResolver contentResolver,
+                @NonNull ClickScheduler clickScheduler,
+                @Nullable AutoclickIndicatorScheduler autoclickIndicatorScheduler) {
             if (mContentResolver != null || mClickScheduler != null) {
                 throw new IllegalStateException("Observer already started.");
             }
@@ -193,11 +244,20 @@
 
             mContentResolver = contentResolver;
             mClickScheduler = clickScheduler;
+            mAutoclickIndicatorScheduler = autoclickIndicatorScheduler;
             mContentResolver.registerContentObserver(mAutoclickDelaySettingUri, false, this,
                     mUserId);
 
             // Initialize mClickScheduler's initial delay value.
             onChange(true, mAutoclickDelaySettingUri);
+
+            if (Flags.enableAutoclickIndicator()) {
+                // Register observer to listen to cursor area size setting change.
+                mContentResolver.registerContentObserver(
+                        mAutoclickCursorAreaSizeSettingUri, false, this, mUserId);
+                // Initialize mAutoclickIndicatorView's initial size.
+                onChange(true, mAutoclickCursorAreaSizeSettingUri);
+            }
         }
 
         /**
@@ -208,7 +268,7 @@
          */
         public void stop() {
             if (mContentResolver == null || mClickScheduler == null) {
-                throw new IllegalStateException("ClickDelayObserver not started.");
+                throw new IllegalStateException("AutoclickSettingsObserver not started.");
             }
 
             mContentResolver.unregisterContentObserver(this);
@@ -222,6 +282,76 @@
                         AccessibilityManager.AUTOCLICK_DELAY_DEFAULT, mUserId);
                 mClickScheduler.updateDelay(delay);
             }
+            if (Flags.enableAutoclickIndicator()
+                    && mAutoclickCursorAreaSizeSettingUri.equals(uri)) {
+                int size =
+                        Settings.Secure.getIntForUser(
+                                mContentResolver,
+                                Settings.Secure.ACCESSIBILITY_AUTOCLICK_CURSOR_AREA_SIZE,
+                                AccessibilityManager.AUTOCLICK_CURSOR_AREA_SIZE_DEFAULT,
+                                mUserId);
+                if (mAutoclickIndicatorScheduler != null) {
+                    mAutoclickIndicatorScheduler.updateCursorAreaSize(size);
+                }
+            }
+        }
+    }
+
+    private final class AutoclickIndicatorScheduler implements Runnable {
+        private final Handler mHandler;
+        private long mScheduledShowIndicatorTime;
+        private boolean mIndicatorCallbackActive = false;
+
+        public AutoclickIndicatorScheduler(Handler handler) {
+            mHandler = handler;
+        }
+
+        @Override
+        public void run() {
+            long now = SystemClock.uptimeMillis();
+            // Indicator was rescheduled after task was posted. Post new run task at updated time.
+            if (now < mScheduledShowIndicatorTime) {
+                mHandler.postDelayed(this, mScheduledShowIndicatorTime - now);
+                return;
+            }
+
+            mAutoclickIndicatorView.redrawIndicator();
+            mIndicatorCallbackActive = false;
+        }
+
+        public void update() {
+            long scheduledShowIndicatorTime =
+                    SystemClock.uptimeMillis() + SHOW_INDICATOR_DELAY_TIME;
+            // If there already is a scheduled show indicator at time before the updated time, just
+            // update scheduled time.
+            if (mIndicatorCallbackActive
+                    && scheduledShowIndicatorTime > mScheduledShowIndicatorTime) {
+                mScheduledShowIndicatorTime = scheduledShowIndicatorTime;
+                return;
+            }
+
+            if (mIndicatorCallbackActive) {
+                mHandler.removeCallbacks(this);
+            }
+
+            mIndicatorCallbackActive = true;
+            mScheduledShowIndicatorTime = scheduledShowIndicatorTime;
+
+            mHandler.postDelayed(this, SHOW_INDICATOR_DELAY_TIME);
+        }
+
+        public void cancel() {
+            if (!mIndicatorCallbackActive) {
+                return;
+            }
+
+            mIndicatorCallbackActive = false;
+            mScheduledShowIndicatorTime = -1;
+            mHandler.removeCallbacks(this);
+        }
+
+        public void updateCursorAreaSize(int size) {
+            mAutoclickIndicatorView.setRadius(size);
         }
     }
 
@@ -304,7 +434,14 @@
             cacheLastEvent(event, policyFlags, mLastMotionEvent == null || moved /* useAsAnchor */);
 
             if (moved) {
-              rescheduleClick(mDelay);
+                rescheduleClick(mDelay);
+
+                if (Flags.enableAutoclickIndicator()) {
+                    final int pointerIndex = event.getActionIndex();
+                    mAutoclickIndicatorView.setCoordination(
+                            event.getX(pointerIndex), event.getY(pointerIndex));
+                    mAutoclickIndicatorScheduler.update();
+                }
             }
         }
 
@@ -331,6 +468,10 @@
          */
         public void updateDelay(int delay) {
             mDelay = delay;
+
+            if (Flags.enableAutoclickIndicator() && mAutoclickIndicatorView != null) {
+                mAutoclickIndicatorView.setAnimationDuration(delay - SHOW_INDICATOR_DELAY_TIME);
+            }
         }
 
         /**
@@ -385,6 +526,10 @@
                 mLastMotionEvent = null;
             }
             mScheduledClickTime = -1;
+
+            if (Flags.enableAutoclickIndicator() && mAutoclickIndicatorView != null) {
+                mAutoclickIndicatorView.clearIndicator();
+            }
         }
 
         /**
diff --git a/services/accessibility/java/com/android/server/accessibility/AutoclickIndicatorView.java b/services/accessibility/java/com/android/server/accessibility/AutoclickIndicatorView.java
new file mode 100644
index 0000000..bf50151
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/AutoclickIndicatorView.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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;
+
+import static android.view.accessibility.AccessibilityManager.AUTOCLICK_CURSOR_AREA_SIZE_DEFAULT;
+
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.RectF;
+import android.util.DisplayMetrics;
+import android.view.View;
+import android.view.accessibility.AccessibilityManager;
+import android.view.animation.LinearInterpolator;
+
+// A visual indicator for the autoclick feature.
+public class AutoclickIndicatorView extends View {
+    private static final String TAG = AutoclickIndicatorView.class.getSimpleName();
+
+    // TODO(b/383901288): update delay time once determined by UX.
+    static final int SHOW_INDICATOR_DELAY_TIME = 150;
+
+    static final int MINIMAL_ANIMATION_DURATION = 50;
+
+    private float mRadius = AUTOCLICK_CURSOR_AREA_SIZE_DEFAULT;
+
+    private final Paint mPaint;
+
+    private final ValueAnimator mAnimator;
+
+    private final RectF mRingRect;
+
+    // x and y coordinates of the visual indicator.
+    private float mX;
+    private float mY;
+
+    // Current sweep angle of the animated ring.
+    private float mSweepAngle;
+
+    private int mAnimationDuration = AccessibilityManager.AUTOCLICK_DELAY_DEFAULT;
+
+    // Status of whether the visual indicator should display or not.
+    private boolean showIndicator = false;
+
+    public AutoclickIndicatorView(Context context) {
+        super(context);
+
+        mPaint = new Paint();
+        // TODO(b/383901288): update styling once determined by UX.
+        mPaint.setARGB(255, 52, 103, 235);
+        mPaint.setStyle(Paint.Style.STROKE);
+        mPaint.setStrokeWidth(10);
+
+        mAnimator = ValueAnimator.ofFloat(0, 360);
+        mAnimator.setDuration(mAnimationDuration);
+        mAnimator.setInterpolator(new LinearInterpolator());
+        mAnimator.addUpdateListener(
+                animation -> {
+                    mSweepAngle = (float) animation.getAnimatedValue();
+                    // Redraw the view with the updated angle.
+                    invalidate();
+                });
+
+        mRingRect = new RectF();
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        super.onDraw(canvas);
+
+        if (showIndicator) {
+            mRingRect.set(
+                    /* left= */ mX - mRadius,
+                    /* top= */ mY - mRadius,
+                    /* right= */ mX + mRadius,
+                    /* bottom= */ mY + mRadius);
+            canvas.drawArc(mRingRect, /* startAngle= */ -90, mSweepAngle, false, mPaint);
+        }
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        // Get the screen dimensions.
+        DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
+        int screenWidth = displayMetrics.widthPixels;
+        int screenHeight = displayMetrics.heightPixels;
+
+        setMeasuredDimension(screenWidth, screenHeight);
+    }
+
+    public void setCoordination(float x, float y) {
+        mX = x;
+        mY = y;
+    }
+
+    public void setRadius(int radius) {
+        mRadius = radius;
+    }
+
+    public void redrawIndicator() {
+        showIndicator = true;
+        invalidate();
+        mAnimator.start();
+    }
+
+    public void clearIndicator() {
+        showIndicator = false;
+        mAnimator.cancel();
+        invalidate();
+    }
+
+    public void setAnimationDuration(int duration) {
+        mAnimationDuration = Math.max(duration, MINIMAL_ANIMATION_DURATION);
+        mAnimator.setDuration(mAnimationDuration);
+    }
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/HearingDevicePhoneCallNotificationController.java b/services/accessibility/java/com/android/server/accessibility/HearingDevicePhoneCallNotificationController.java
new file mode 100644
index 0000000..d06daf5
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/HearingDevicePhoneCallNotificationController.java
@@ -0,0 +1,356 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothManager;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothUuid;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.media.AudioDeviceAttributes;
+import android.media.AudioDeviceInfo;
+import android.media.AudioManager;
+import android.media.MediaRecorder;
+import android.os.Bundle;
+import android.telephony.TelephonyCallback;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.annotation.VisibleForTesting;
+
+import com.android.internal.R;
+import com.android.internal.messages.nano.SystemMessageProto;
+import com.android.internal.notification.SystemNotificationChannels;
+import com.android.internal.util.ArrayUtils;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.stream.Collectors;
+
+/**
+ * A controller class to handle notification for hearing device during phone calls.
+ */
+public class HearingDevicePhoneCallNotificationController {
+
+    private final TelephonyManager mTelephonyManager;
+    private final TelephonyCallback mTelephonyListener;
+    private final Executor mCallbackExecutor;
+
+    public HearingDevicePhoneCallNotificationController(@NonNull Context context) {
+        mTelephonyListener = new CallStateListener(context);
+        mTelephonyManager = context.getSystemService(TelephonyManager.class);
+        mCallbackExecutor = Executors.newSingleThreadExecutor();
+    }
+
+    @VisibleForTesting
+    HearingDevicePhoneCallNotificationController(@NonNull Context context,
+            TelephonyCallback telephonyCallback) {
+        mTelephonyListener = telephonyCallback;
+        mTelephonyManager = context.getSystemService(TelephonyManager.class);
+        mCallbackExecutor = context.getMainExecutor();
+    }
+
+    /**
+     * Registers a telephony callback to listen for call state changed to handle notification for
+     * hearing device during phone calls.
+     */
+    public void startListenForCallState() {
+        mTelephonyManager.registerTelephonyCallback(mCallbackExecutor, mTelephonyListener);
+    }
+
+    /**
+     * A telephony callback listener to listen to call state changes and show/dismiss notification
+     */
+    @VisibleForTesting
+    static class CallStateListener extends TelephonyCallback implements
+            TelephonyCallback.CallStateListener {
+
+        private static final String TAG =
+                "HearingDevice_CallStateListener";
+        private static final String ACTION_SWITCH_TO_BUILTIN_MIC =
+                "com.android.server.accessibility.hearingdevice.action.SWITCH_TO_BUILTIN_MIC";
+        private static final String ACTION_SWITCH_TO_HEARING_MIC =
+                "com.android.server.accessibility.hearingdevice.action.SWITCH_TO_HEARING_MIC";
+        private static final String ACTION_BLUETOOTH_DEVICE_DETAILS =
+                "com.android.settings.BLUETOOTH_DEVICE_DETAIL_SETTINGS";
+        private static final String KEY_BLUETOOTH_ADDRESS = "device_address";
+        private static final String EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args";
+        private static final int MICROPHONE_SOURCE_VOICE_COMMUNICATION =
+                MediaRecorder.AudioSource.VOICE_COMMUNICATION;
+        private static final AudioDeviceAttributes BUILTIN_MIC = new AudioDeviceAttributes(
+                AudioDeviceAttributes.ROLE_INPUT, AudioDeviceInfo.TYPE_BUILTIN_MIC, "");
+
+        private final Context mContext;
+        private NotificationManager mNotificationManager;
+        private AudioManager mAudioManager;
+        private BroadcastReceiver mHearingDeviceActionReceiver;
+        private BluetoothDevice mHearingDevice;
+        private boolean mIsNotificationShown = false;
+
+        CallStateListener(@NonNull Context context) {
+            mContext = context;
+        }
+
+        @Override
+        @SuppressLint("AndroidFrameworkRequiresPermission")
+        public void onCallStateChanged(int state) {
+            // NotificationManagerService and AudioService are all initialized after
+            // AccessibilityManagerService.
+            // Can not get them in constructor. Need to get these services until callback is
+            // triggered.
+            mNotificationManager = mContext.getSystemService(NotificationManager.class);
+            mAudioManager = mContext.getSystemService(AudioManager.class);
+            if (mNotificationManager == null || mAudioManager == null) {
+                Log.w(TAG, "NotificationManager or AudioManager is not prepare yet.");
+                return;
+            }
+
+            if (state == TelephonyManager.CALL_STATE_IDLE) {
+                dismissNotificationIfNeeded();
+
+                if (mHearingDevice != null) {
+                    // reset to its original status
+                    setMicrophonePreferredForCalls(mHearingDevice.isMicrophonePreferredForCalls());
+                }
+                mHearingDevice = null;
+            }
+            if (state == TelephonyManager.CALL_STATE_OFFHOOK) {
+                mHearingDevice = getSupportedInputHearingDeviceInfo(
+                        mAudioManager.getAvailableCommunicationDevices());
+                if (mHearingDevice != null) {
+                    showNotificationIfNeeded();
+                }
+            }
+        }
+
+        private void showNotificationIfNeeded() {
+            if (mIsNotificationShown) {
+                return;
+            }
+
+            showNotification(mHearingDevice.isMicrophonePreferredForCalls());
+            mIsNotificationShown = true;
+        }
+
+        private void dismissNotificationIfNeeded() {
+            if (!mIsNotificationShown) {
+                return;
+            }
+
+            dismissNotification();
+            mIsNotificationShown = false;
+        }
+
+        private void showNotification(boolean useRemoteMicrophone) {
+            mNotificationManager.notify(
+                    SystemMessageProto.SystemMessage.NOTE_HEARING_DEVICE_INPUT_SWITCH,
+                    createSwitchInputNotification(useRemoteMicrophone));
+            registerReceiverIfNeeded();
+        }
+
+        private void dismissNotification() {
+            unregisterReceiverIfNeeded();
+            mNotificationManager.cancel(
+                    SystemMessageProto.SystemMessage.NOTE_HEARING_DEVICE_INPUT_SWITCH);
+        }
+
+        private BluetoothDevice getSupportedInputHearingDeviceInfo(List<AudioDeviceInfo> infoList) {
+            final BluetoothAdapter bluetoothAdapter = mContext.getSystemService(
+                    BluetoothManager.class).getAdapter();
+            if (bluetoothAdapter == null) {
+                return null;
+            }
+            if (!isHapClientSupported()) {
+                return null;
+            }
+
+            final Set<String> inputDeviceAddress = Arrays.stream(
+                    mAudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)).map(
+                    AudioDeviceInfo::getAddress).collect(Collectors.toSet());
+
+            //TODO: b/370812132 - Need to update if TYPE_LEA_HEARING_AID is added
+            final AudioDeviceInfo hearingDeviceInfo = infoList.stream()
+                    .filter(info -> info.getType() == AudioDeviceInfo.TYPE_BLE_HEADSET)
+                    .filter(info -> inputDeviceAddress.contains(info.getAddress()))
+                    .filter(info -> isHapClientDevice(bluetoothAdapter, info))
+                    .findAny()
+                    .orElse(null);
+
+            return (hearingDeviceInfo != null) ? bluetoothAdapter.getRemoteDevice(
+                    hearingDeviceInfo.getAddress()) : null;
+        }
+
+        @VisibleForTesting
+        boolean isHapClientDevice(BluetoothAdapter bluetoothAdapter, AudioDeviceInfo info) {
+            BluetoothDevice device = bluetoothAdapter.getRemoteDevice(info.getAddress());
+            return ArrayUtils.contains(device.getUuids(), BluetoothUuid.HAS);
+        }
+
+        @VisibleForTesting
+        boolean isHapClientSupported() {
+            return BluetoothAdapter.getDefaultAdapter().getSupportedProfiles().contains(
+                    BluetoothProfile.HAP_CLIENT);
+        }
+
+        private Notification createSwitchInputNotification(boolean useRemoteMicrophone) {
+            return new Notification.Builder(mContext,
+                    SystemNotificationChannels.ACCESSIBILITY_HEARING_DEVICE)
+                    .setContentTitle(getSwitchInputTitle(useRemoteMicrophone))
+                    .setContentText(getSwitchInputMessage(useRemoteMicrophone))
+                    .setSmallIcon(R.drawable.ic_settings_24dp)
+                    .setColor(mContext.getResources().getColor(
+                            com.android.internal.R.color.system_notification_accent_color))
+                    .setLocalOnly(true)
+                    .setCategory(Notification.CATEGORY_SYSTEM)
+                    .setContentIntent(createPendingIntent(ACTION_BLUETOOTH_DEVICE_DETAILS))
+                    .setActions(buildSwitchInputAction(useRemoteMicrophone),
+                            buildOpenSettingsAction())
+                    .build();
+        }
+
+        private Notification.Action buildSwitchInputAction(boolean useRemoteMicrophone) {
+            return useRemoteMicrophone
+                    ? new Notification.Action.Builder(null,
+                            mContext.getString(R.string.hearing_device_notification_switch_button),
+                            createPendingIntent(ACTION_SWITCH_TO_BUILTIN_MIC)).build()
+                    : new Notification.Action.Builder(null,
+                            mContext.getString(R.string.hearing_device_notification_switch_button),
+                            createPendingIntent(ACTION_SWITCH_TO_HEARING_MIC)).build();
+        }
+
+        private Notification.Action buildOpenSettingsAction() {
+            return new Notification.Action.Builder(null,
+                    mContext.getString(R.string.hearing_device_notification_settings_button),
+                    createPendingIntent(ACTION_BLUETOOTH_DEVICE_DETAILS)).build();
+        }
+
+        private PendingIntent createPendingIntent(String action) {
+            final Intent intent = new Intent(action);
+
+            switch (action) {
+                case ACTION_SWITCH_TO_BUILTIN_MIC, ACTION_SWITCH_TO_HEARING_MIC -> {
+                    intent.setPackage(mContext.getPackageName());
+                    return PendingIntent.getBroadcast(mContext, /* requestCode = */ 0, intent,
+                            PendingIntent.FLAG_IMMUTABLE);
+                }
+                case ACTION_BLUETOOTH_DEVICE_DETAILS -> {
+                    Bundle bundle = new Bundle();
+                    bundle.putString(KEY_BLUETOOTH_ADDRESS, mHearingDevice.getAddress());
+                    intent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, bundle);
+                    intent.addFlags(
+                            Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+                    return PendingIntent.getActivity(mContext, /* requestCode = */ 0, intent,
+                            PendingIntent.FLAG_IMMUTABLE);
+                }
+            }
+            return null;
+        }
+
+        private void setMicrophonePreferredForCalls(boolean useRemoteMicrophone) {
+            if (useRemoteMicrophone) {
+                switchToHearingMic();
+            } else {
+                switchToBuiltinMic();
+            }
+        }
+
+        @SuppressLint("AndroidFrameworkRequiresPermission")
+        private void switchToBuiltinMic() {
+            mAudioManager.clearPreferredDevicesForCapturePreset(
+                    MICROPHONE_SOURCE_VOICE_COMMUNICATION);
+            mAudioManager.setPreferredDeviceForCapturePreset(MICROPHONE_SOURCE_VOICE_COMMUNICATION,
+                    BUILTIN_MIC);
+        }
+
+        @SuppressLint("AndroidFrameworkRequiresPermission")
+        private void switchToHearingMic() {
+            // clear config to let audio manager to determine next priority device. We can assume
+            // user connects to hearing device here, so next priority device should be hearing
+            // device.
+            mAudioManager.clearPreferredDevicesForCapturePreset(
+                    MICROPHONE_SOURCE_VOICE_COMMUNICATION);
+        }
+
+        private void registerReceiverIfNeeded() {
+            if (mHearingDeviceActionReceiver != null) {
+                return;
+            }
+            mHearingDeviceActionReceiver = new HearingDeviceActionReceiver();
+            final IntentFilter intentFilter = new IntentFilter();
+            intentFilter.addAction(ACTION_SWITCH_TO_BUILTIN_MIC);
+            intentFilter.addAction(ACTION_SWITCH_TO_HEARING_MIC);
+            mContext.registerReceiver(mHearingDeviceActionReceiver, intentFilter,
+                    Manifest.permission.MANAGE_ACCESSIBILITY, null, Context.RECEIVER_NOT_EXPORTED);
+        }
+
+        private void unregisterReceiverIfNeeded() {
+            if (mHearingDeviceActionReceiver == null) {
+                return;
+            }
+            mContext.unregisterReceiver(mHearingDeviceActionReceiver);
+            mHearingDeviceActionReceiver = null;
+        }
+
+        private CharSequence getSwitchInputTitle(boolean useRemoteMicrophone) {
+            return useRemoteMicrophone
+                    ? mContext.getString(
+                            R.string.hearing_device_switch_phone_mic_notification_title)
+                    : mContext.getString(
+                            R.string.hearing_device_switch_hearing_mic_notification_title);
+        }
+
+        private CharSequence getSwitchInputMessage(boolean useRemoteMicrophone) {
+            return useRemoteMicrophone
+                    ? mContext.getString(
+                            R.string.hearing_device_switch_phone_mic_notification_text)
+                    : mContext.getString(
+                            R.string.hearing_device_switch_hearing_mic_notification_text);
+        }
+
+        private class HearingDeviceActionReceiver extends BroadcastReceiver {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                final String action = intent.getAction();
+                if (TextUtils.isEmpty(action)) {
+                    return;
+                }
+
+                if (ACTION_SWITCH_TO_BUILTIN_MIC.equals(action)) {
+                    switchToBuiltinMic();
+                    showNotification(/* useRemoteMicrophone= */ false);
+                } else if (ACTION_SWITCH_TO_HEARING_MIC.equals(action)) {
+                    switchToHearingMic();
+                    showNotification(/* useRemoteMicrophone= */ true);
+                }
+            }
+        }
+    }
+}
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 f15b8ee..cd46b38 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java
@@ -38,7 +38,7 @@
     // Pointer-related constants
     // This constant captures the current implementation detail that
     // pointer IDs are between 0 and 31 inclusive (subject to change).
-    // (See MAX_POINTER_ID in frameworks/base/include/ui/Input.h)
+    // (See MAX_POINTER_ID in frameworks/native/include/input/Input.h)
     public static final int MAX_POINTER_COUNT = 32;
     // Constant referring to the ids bits of all pointers.
     public static final int ALL_POINTER_ID_BITS = 0xFFFFFFFF;
diff --git a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerService.java b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerService.java
index c293087..dc6afe1 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerService.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerService.java
@@ -19,7 +19,9 @@
 import android.annotation.NonNull;
 import android.app.appfunctions.AppFunctionManagerConfiguration;
 import android.content.Context;
+import android.content.pm.PackageManagerInternal;
 
+import com.android.server.LocalServices;
 import com.android.server.SystemService;
 
 /** Service that manages app functions. */
@@ -28,7 +30,9 @@
 
     public AppFunctionManagerService(Context context) {
         super(context);
-        mServiceImpl = new AppFunctionManagerServiceImpl(context);
+        mServiceImpl =
+                new AppFunctionManagerServiceImpl(
+                        context, LocalServices.getService(PackageManagerInternal.class));
     }
 
     @Override
diff --git a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
index 37276dd..4376444 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/AppFunctionManagerServiceImpl.java
@@ -50,6 +50,10 @@
 import android.app.appsearch.observer.SchemaChangeInfo;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
+import android.content.pm.SigningInfo;
 import android.os.Binder;
 import android.os.CancellationSignal;
 import android.os.IBinder;
@@ -87,8 +91,10 @@
     private final Context mContext;
     private final Map<String, Object> mLocks = new WeakHashMap<>();
     private final AppFunctionsLoggerWrapper mLoggerWrapper;
+    private final PackageManagerInternal mPackageManagerInternal;
 
-    public AppFunctionManagerServiceImpl(@NonNull Context context) {
+    public AppFunctionManagerServiceImpl(
+            @NonNull Context context, @NonNull PackageManagerInternal packageManagerInternal) {
         this(
                 context,
                 new RemoteServiceCallerImpl<>(
@@ -96,7 +102,8 @@
                 new CallerValidatorImpl(context),
                 new ServiceHelperImpl(context),
                 new ServiceConfigImpl(),
-                new AppFunctionsLoggerWrapper(context));
+                new AppFunctionsLoggerWrapper(context),
+                packageManagerInternal);
     }
 
     @VisibleForTesting
@@ -106,13 +113,15 @@
             CallerValidator callerValidator,
             ServiceHelper appFunctionInternalServiceHelper,
             ServiceConfig serviceConfig,
-            AppFunctionsLoggerWrapper loggerWrapper) {
+            AppFunctionsLoggerWrapper loggerWrapper,
+            PackageManagerInternal packageManagerInternal) {
         mContext = Objects.requireNonNull(context);
         mRemoteServiceCaller = Objects.requireNonNull(remoteServiceCaller);
         mCallerValidator = Objects.requireNonNull(callerValidator);
         mInternalServiceHelper = Objects.requireNonNull(appFunctionInternalServiceHelper);
         mServiceConfig = serviceConfig;
         mLoggerWrapper = loggerWrapper;
+        mPackageManagerInternal = Objects.requireNonNull(packageManagerInternal);
     }
 
     /** Called when the user is unlocked. */
@@ -260,6 +269,24 @@
                                                 "Cannot find the target service."));
                                 return;
                             }
+                            // Grant target app implicit visibility to the caller
+                            final int grantRecipientUserId = targetUser.getIdentifier();
+                            final int grantRecipientAppId =
+                                    UserHandle.getAppId(
+                                            mPackageManagerInternal.getPackageUid(
+                                                    requestInternal
+                                                            .getClientRequest()
+                                                            .getTargetPackageName(),
+                                                    /* flags= */ 0,
+                                                    /* userId= */ grantRecipientUserId));
+                            if (grantRecipientAppId > 0) {
+                                mPackageManagerInternal.grantImplicitAccess(
+                                        grantRecipientUserId,
+                                        serviceIntent,
+                                        grantRecipientAppId,
+                                        callingUid,
+                                        /* direct= */ true);
+                            }
                             bindAppFunctionServiceUnchecked(
                                     requestInternal,
                                     serviceIntent,
@@ -268,7 +295,8 @@
                                     safeExecuteAppFunctionCallback,
                                     /* bindFlags= */ Context.BIND_AUTO_CREATE
                                             | Context.BIND_FOREGROUND_SERVICE,
-                                    callerBinder);
+                                    callerBinder,
+                                    callingUid);
                         })
                 .exceptionally(
                         ex -> {
@@ -420,7 +448,8 @@
             @NonNull ICancellationSignal cancellationSignalTransport,
             @NonNull SafeOneTimeExecuteAppFunctionCallback safeExecuteAppFunctionCallback,
             int bindFlags,
-            @NonNull IBinder callerBinder) {
+            @NonNull IBinder callerBinder,
+            int callingUid) {
         CancellationSignal cancellationSignal =
                 CancellationSignal.fromTransport(cancellationSignalTransport);
         ICancellationCallback cancellationCallback =
@@ -441,7 +470,11 @@
                         new RunAppFunctionServiceCallback(
                                 requestInternal,
                                 cancellationCallback,
-                                safeExecuteAppFunctionCallback),
+                                safeExecuteAppFunctionCallback,
+                                getPackageSigningInfo(
+                                        targetUser,
+                                        requestInternal.getCallingPackage(),
+                                        callingUid)),
                         callerBinder);
 
         if (!bindServiceResult) {
@@ -453,6 +486,23 @@
         }
     }
 
+    @NonNull
+    private SigningInfo getPackageSigningInfo(
+            @NonNull UserHandle targetUser, @NonNull String packageName, int uid) {
+        Objects.requireNonNull(packageName);
+        Objects.requireNonNull(targetUser);
+
+        PackageInfo packageInfo;
+        packageInfo =
+                Objects.requireNonNull(
+                        mPackageManagerInternal.getPackageInfo(
+                                packageName,
+                                PackageManager.GET_SIGNING_CERTIFICATES,
+                                uid,
+                                targetUser.getIdentifier()));
+        return Objects.requireNonNull(packageInfo.signingInfo);
+    }
+
     private AppSearchManager getAppSearchManagerAsUser(@NonNull UserHandle userHandle) {
         return mContext.createContextAsUser(userHandle, /* flags= */ 0)
                 .getSystemService(AppSearchManager.class);
diff --git a/services/appfunctions/java/com/android/server/appfunctions/RunAppFunctionServiceCallback.java b/services/appfunctions/java/com/android/server/appfunctions/RunAppFunctionServiceCallback.java
index 4cba8ec..0cec09d 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/RunAppFunctionServiceCallback.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/RunAppFunctionServiceCallback.java
@@ -23,6 +23,7 @@
 import android.app.appfunctions.ICancellationCallback;
 import android.app.appfunctions.IExecuteAppFunctionCallback;
 import android.app.appfunctions.SafeOneTimeExecuteAppFunctionCallback;
+import android.content.pm.SigningInfo;
 import android.os.SystemClock;
 import android.util.Slog;
 
@@ -38,14 +39,17 @@
     private final ExecuteAppFunctionAidlRequest mRequestInternal;
     private final SafeOneTimeExecuteAppFunctionCallback mSafeExecuteAppFunctionCallback;
     private final ICancellationCallback mCancellationCallback;
+    private final SigningInfo mCallerSigningInfo;
 
     public RunAppFunctionServiceCallback(
             ExecuteAppFunctionAidlRequest requestInternal,
             ICancellationCallback cancellationCallback,
-            SafeOneTimeExecuteAppFunctionCallback safeExecuteAppFunctionCallback) {
-        this.mRequestInternal = requestInternal;
-        this.mSafeExecuteAppFunctionCallback = safeExecuteAppFunctionCallback;
-        this.mCancellationCallback = cancellationCallback;
+            SafeOneTimeExecuteAppFunctionCallback safeExecuteAppFunctionCallback,
+            SigningInfo callerSigningInfo) {
+        mRequestInternal = requestInternal;
+        mSafeExecuteAppFunctionCallback = safeExecuteAppFunctionCallback;
+        mCancellationCallback = cancellationCallback;
+        mCallerSigningInfo = callerSigningInfo;
     }
 
     @Override
@@ -58,6 +62,7 @@
             service.executeAppFunction(
                     mRequestInternal.getClientRequest(),
                     mRequestInternal.getCallingPackage(),
+                    mCallerSigningInfo,
                     mCancellationCallback,
                     new IExecuteAppFunctionCallback.Stub() {
                         @Override
diff --git a/services/appfunctions/java/com/android/server/appfunctions/ServiceHelperImpl.java b/services/appfunctions/java/com/android/server/appfunctions/ServiceHelperImpl.java
index 37a3779..071fda4 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/ServiceHelperImpl.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/ServiceHelperImpl.java
@@ -31,8 +31,6 @@
 class ServiceHelperImpl implements ServiceHelper {
     private final Context mContext;
 
-    // TODO(b/357551503): Keep track of unlocked users.
-
     ServiceHelperImpl(@NonNull Context context) {
         mContext = Objects.requireNonNull(context);
     }
diff --git a/services/autofill/bugfixes.aconfig b/services/autofill/bugfixes.aconfig
index ec6c3b7..68bb224 100644
--- a/services/autofill/bugfixes.aconfig
+++ b/services/autofill/bugfixes.aconfig
@@ -64,6 +64,16 @@
 }
 
 flag {
+  name: "fix_get_autofill_component"
+  namespace: "autofill"
+  description: "Fix getAutofillComponent returning null, even when user has selected provider"
+  bug: "319503432"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
   name: "test_flag"
   namespace: "autofill"
   description: "Test flag "
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index cba8c66..c68e5495 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -18,6 +18,7 @@
 
 import static android.Manifest.permission.MANAGE_AUTO_FILL;
 import static android.content.Context.AUTOFILL_MANAGER_SERVICE;
+import static android.service.autofill.Flags.fixGetAutofillComponent;
 import static android.view.autofill.AutofillManager.MAX_TEMP_AUGMENTED_SERVICE_DURATION_MS;
 import static android.view.autofill.AutofillManager.getSmartSuggestionModeToString;
 
@@ -1920,8 +1921,12 @@
 
             try {
                 synchronized (mLock) {
-                    final AutofillManagerServiceImpl service =
-                            peekServiceForUserWithLocalBinderIdentityLocked(userId);
+                    final AutofillManagerServiceImpl service;
+                    if (fixGetAutofillComponent()) {
+                        service = getServiceForUserWithLocalBinderIdentityLocked(userId);
+                    } else {
+                        service = peekServiceForUserWithLocalBinderIdentityLocked(userId);
+                    }
                     if (service != null) {
                         componentName = service.getServiceComponentName();
                     } else if (sVerbose) {
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 418f3a1..0e2e505 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -109,6 +109,8 @@
 import java.io.PrintWriter;
 import java.util.Collection;
 import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
 
 @SuppressLint("LongLogTag")
 public class CompanionDeviceManagerService extends SystemService {
@@ -226,7 +228,8 @@
         if (associations.isEmpty()) return;
 
         mCompanionExemptionProcessor.updateAtm(userId, associations);
-        mCompanionExemptionProcessor.updateAutoRevokeExemptions();
+        ExecutorService executor = Executors.newSingleThreadExecutor();
+        executor.execute(mCompanionExemptionProcessor::updateAutoRevokeExemptions);
     }
 
     @Override
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 d3e808f..7456c50 100644
--- a/services/companion/java/com/android/server/companion/virtual/InputController.java
+++ b/services/companion/java/com/android/server/companion/virtual/InputController.java
@@ -265,8 +265,8 @@
         mInputManagerInternal.setPointerIconVisible(visible, displayId);
     }
 
-    void setMousePointerAccelerationEnabled(boolean enabled, int displayId) {
-        mInputManagerInternal.setMousePointerAccelerationEnabled(enabled, displayId);
+    void setMouseScalingEnabled(boolean enabled, int displayId) {
+        mInputManagerInternal.setMouseScalingEnabled(enabled, displayId);
     }
 
     void setDisplayEligibilityForPointerCapture(boolean isEligible, int displayId) {
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 6bf60bf..260ea75 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -1518,7 +1518,7 @@
 
         final long token = Binder.clearCallingIdentity();
         try {
-            mInputController.setMousePointerAccelerationEnabled(false, displayId);
+            mInputController.setMouseScalingEnabled(false, displayId);
             mInputController.setDisplayEligibilityForPointerCapture(/* isEligible= */ false,
                     displayId);
             if (isTrustedDisplay) {
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index 1f3b316..aeb2f5e 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -176,7 +176,7 @@
     public VirtualDeviceManagerService(Context context) {
         super(context);
         mImpl = new VirtualDeviceManagerImpl();
-        mNativeImpl = Flags.enableNativeVdm() ? new VirtualDeviceManagerNativeImpl() : null;
+        mNativeImpl = new VirtualDeviceManagerNativeImpl();
         mLocalService = new LocalService();
     }
 
@@ -208,9 +208,7 @@
     @RequiresPermission(android.Manifest.permission.MANAGE_COMPANION_DEVICES)
     public void onStart() {
         publishBinderService(Context.VIRTUAL_DEVICE_SERVICE, mImpl);
-        if (Flags.enableNativeVdm()) {
-            publishBinderService(VIRTUAL_DEVICE_NATIVE_SERVICE, mNativeImpl);
-        }
+        publishBinderService(VIRTUAL_DEVICE_NATIVE_SERVICE, mNativeImpl);
         publishLocalService(VirtualDeviceManagerInternal.class, mLocalService);
         ActivityTaskManagerInternal activityTaskManagerInternal = getLocalService(
                 ActivityTaskManagerInternal.class);
diff --git a/services/core/Android.bp b/services/core/Android.bp
index dc83064..d6bffcb 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -132,6 +132,7 @@
     srcs: [
         ":android.hardware.tv.hdmi.connection-V1-java-source",
         ":android.hardware.tv.hdmi.earc-V1-java-source",
+        ":android.hardware.tv.mediaquality-V1-java-source",
         ":statslog-art-java-gen",
         ":statslog-contexthub-java-gen",
         ":services.core-aidl-sources",
diff --git a/services/core/java/com/android/server/BinaryTransparencyService.java b/services/core/java/com/android/server/BinaryTransparencyService.java
index 778c686..31f6ef9 100644
--- a/services/core/java/com/android/server/BinaryTransparencyService.java
+++ b/services/core/java/com/android/server/BinaryTransparencyService.java
@@ -1708,7 +1708,7 @@
     private class PackageUpdatedReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context context, Intent intent) {
-            if (!intent.getAction().equals(Intent.ACTION_PACKAGE_ADDED)) {
+            if (!Intent.ACTION_PACKAGE_ADDED.equals(intent.getAction())) {
                 return;
             }
 
diff --git a/services/core/java/com/android/server/GestureLauncherService.java b/services/core/java/com/android/server/GestureLauncherService.java
index c1c03ff..6459016 100644
--- a/services/core/java/com/android/server/GestureLauncherService.java
+++ b/services/core/java/com/android/server/GestureLauncherService.java
@@ -17,6 +17,7 @@
 package com.android.server;
 
 import static android.service.quickaccesswallet.Flags.launchWalletOptionOnPowerDoubleTap;
+import static android.service.quickaccesswallet.Flags.launchWalletViaSysuiCallbacks;
 
 import static com.android.hardware.input.Flags.overridePowerKeyBehaviorInFocusedWindow;
 import static com.android.internal.R.integer.config_defaultMinEmergencyGestureTapDurationMillis;
@@ -65,8 +66,7 @@
 /**
  * The service that listens for gestures detected in sensor firmware and starts the intent
  * accordingly.
- * <p>For now, only camera launch gesture is supported, and in the future, more gestures can be
- * added.</p>
+ *
  * @hide
  */
 public class GestureLauncherService extends SystemService {
@@ -108,10 +108,22 @@
     @VisibleForTesting
     static final int EMERGENCY_GESTURE_POWER_BUTTON_COOLDOWN_PERIOD_MS_MAX = 5000;
 
-    /** Indicates camera should be launched on power double tap. */
+    /** Configuration value indicating double tap power gesture is disabled. */
+    @VisibleForTesting static final int DOUBLE_TAP_POWER_DISABLED_MODE = 0;
+
+    /** Configuration value indicating double tap power gesture should launch camera. */
+    @VisibleForTesting static final int DOUBLE_TAP_POWER_LAUNCH_CAMERA_MODE = 1;
+
+    /**
+     * Configuration value indicating double tap power gesture should launch one of many target
+     * actions.
+     */
+    @VisibleForTesting static final int DOUBLE_TAP_POWER_MULTI_TARGET_MODE = 2;
+
+    /** Indicates camera launch is selected as target action for multi target double tap power. */
     @VisibleForTesting static final int LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER = 0;
 
-    /** Indicates wallet should be launched on power double tap. */
+    /** Indicates wallet launch is selected as target action for multi target double tap power. */
     @VisibleForTesting static final int LAUNCH_WALLET_ON_DOUBLE_TAP_POWER = 1;
 
     /** Number of taps required to launch the double tap shortcut (either camera or wallet). */
@@ -227,6 +239,7 @@
             return mId;
         }
     }
+
     public GestureLauncherService(Context context) {
         this(context, new MetricsLogger(),
                 QuickAccessWalletClient.create(context), new UiEventLoggerImpl());
@@ -288,16 +301,15 @@
                     Settings.Secure.getUriFor(
                             Settings.Secure.DOUBLE_TAP_POWER_BUTTON_GESTURE),
                     false, mSettingObserver, mUserId);
-        } else {
-            mContext.getContentResolver().registerContentObserver(
-                    Settings.Secure.getUriFor(Settings.Secure.CAMERA_GESTURE_DISABLED),
-                    false, mSettingObserver, mUserId);
-            mContext.getContentResolver().registerContentObserver(
-                    Settings.Secure.getUriFor(
-                            Settings.Secure.CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED),
-                    false, mSettingObserver, mUserId);
         }
         mContext.getContentResolver().registerContentObserver(
+                Settings.Secure.getUriFor(Settings.Secure.CAMERA_GESTURE_DISABLED),
+                false, mSettingObserver, mUserId);
+        mContext.getContentResolver().registerContentObserver(
+                Settings.Secure.getUriFor(
+                        Settings.Secure.CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED),
+                false, mSettingObserver, mUserId);
+        mContext.getContentResolver().registerContentObserver(
                 Settings.Secure.getUriFor(Settings.Secure.CAMERA_LIFT_TRIGGER_ENABLED),
                 false, mSettingObserver, mUserId);
         mContext.getContentResolver().registerContentObserver(
@@ -467,23 +479,27 @@
                         Settings.Secure.CAMERA_GESTURE_DISABLED, 0, userId) == 0);
     }
 
-
     /** Checks if camera should be launched on double press of the power button. */
     public static boolean isCameraDoubleTapPowerSettingEnabled(Context context, int userId) {
-        boolean res;
-
-        if (launchWalletOptionOnPowerDoubleTap()) {
-            res = isDoubleTapPowerGestureSettingEnabled(context, userId)
-                    && getDoubleTapPowerGestureAction(context, userId)
-                    == LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER;
-        } else {
-            // These are legacy settings that will be deprecated once the option to launch both
-            // wallet and camera has been created.
-            res = isCameraDoubleTapPowerEnabled(context.getResources())
+        if (!launchWalletOptionOnPowerDoubleTap()) {
+            return isCameraDoubleTapPowerEnabled(context.getResources())
                     && (Settings.Secure.getIntForUser(context.getContentResolver(),
                     Settings.Secure.CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED, 0, userId) == 0);
         }
-        return res;
+
+        final int doubleTapPowerGestureSettingMode = getDoubleTapPowerGestureMode(
+                context.getResources());
+
+        return switch (doubleTapPowerGestureSettingMode) {
+            case DOUBLE_TAP_POWER_LAUNCH_CAMERA_MODE -> Settings.Secure.getIntForUser(
+                    context.getContentResolver(),
+                    Settings.Secure.CAMERA_DOUBLE_TAP_POWER_GESTURE_DISABLED, 0, userId) == 0;
+            case DOUBLE_TAP_POWER_MULTI_TARGET_MODE ->
+                    isMultiTargetDoubleTapPowerGestureSettingEnabled(context, userId)
+                            && getDoubleTapPowerGestureAction(context, userId)
+                            == LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER;
+            default -> false;
+        };
     }
 
     /** Checks if wallet should be launched on double tap of the power button. */
@@ -492,7 +508,9 @@
             return false;
         }
 
-        return isDoubleTapPowerGestureSettingEnabled(context, userId)
+        return getDoubleTapPowerGestureMode(context.getResources())
+                == DOUBLE_TAP_POWER_MULTI_TARGET_MODE
+                && isMultiTargetDoubleTapPowerGestureSettingEnabled(context, userId)
                 && getDoubleTapPowerGestureAction(context, userId)
                 == LAUNCH_WALLET_ON_DOUBLE_TAP_POWER;
     }
@@ -514,6 +532,34 @@
                 isDefaultEmergencyGestureEnabled(context.getResources()) ? 1 : 0, userId) != 0;
     }
 
+    /** Gets the double tap power gesture mode. */
+    private static int getDoubleTapPowerGestureMode(Resources resources) {
+        return resources.getInteger(R.integer.config_doubleTapPowerGestureMode);
+    }
+
+    /**
+     * Whether the setting for multi target double tap power gesture is enabled.
+     *
+     * <p>Multi target double tap power gesture allows the user to choose one of many target actions
+     * when double tapping the power button.
+     * </p>
+     */
+    private static boolean isMultiTargetDoubleTapPowerGestureSettingEnabled(Context context,
+            int userId) {
+        return Settings.Secure.getIntForUser(
+                context.getContentResolver(),
+                Settings.Secure.DOUBLE_TAP_POWER_BUTTON_GESTURE_ENABLED,
+                getDoubleTapPowerGestureMode(context.getResources())
+                        == DOUBLE_TAP_POWER_MULTI_TARGET_MODE ? 1 : 0,
+                userId)
+                == 1;
+    }
+
+    /** Gets the selected target action for the multi target double tap power gesture.
+     *
+     * <p>The target actions are defined in {@link Settings.Secure#DOUBLE_TAP_POWER_BUTTON_GESTURE}.
+     * </p>
+     */
     private static int getDoubleTapPowerGestureAction(Context context, int userId) {
         return Settings.Secure.getIntForUser(
                 context.getContentResolver(),
@@ -522,20 +568,6 @@
                 userId);
     }
 
-    /** Whether the shortcut to launch app on power double press is enabled. */
-    private static boolean isDoubleTapPowerGestureSettingEnabled(Context context, int userId) {
-        return Settings.Secure.getIntForUser(
-                context.getContentResolver(),
-                Settings.Secure.DOUBLE_TAP_POWER_BUTTON_GESTURE_ENABLED,
-                isDoubleTapConfigEnabled(context.getResources()) ? 1 : 0,
-                userId)
-                == 1;
-    }
-
-    private static boolean isDoubleTapConfigEnabled(Resources resources) {
-        return resources.getBoolean(R.bool.config_doubleTapPowerGestureEnabled);
-    }
-
     /**
      * Gets power button cooldown period in milliseconds after emergency gesture is triggered. The
      * value is capped at a maximum
@@ -594,7 +626,7 @@
                         || isCameraLiftTriggerEnabled(resources)
                         || isEmergencyGestureEnabled(resources);
         if (launchWalletOptionOnPowerDoubleTap()) {
-            res |= isDoubleTapConfigEnabled(resources);
+            res |= getDoubleTapPowerGestureMode(resources) != DOUBLE_TAP_POWER_DISABLED_MODE;
         } else {
             res |= isCameraDoubleTapPowerEnabled(resources);
         }
@@ -759,7 +791,8 @@
         } else if (launchWallet) {
             Slog.i(TAG, "Power button double tap gesture detected, launching wallet. Interval="
                     + powerTapInterval + "ms");
-            launchWallet = sendGestureTargetActivityPendingIntent();
+            launchWallet = launchWalletViaSysuiCallbacks() ?
+                    handleWalletGesture() : sendGestureTargetActivityPendingIntent();
         } else if (launchEmergencyGesture) {
             Slog.i(TAG, "Emergency gesture detected, launching.");
             launchEmergencyGesture = handleEmergencyGesture();
@@ -879,6 +912,31 @@
         }
     }
 
+    @VisibleForTesting
+    boolean handleWalletGesture() {
+        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+                "GestureLauncher:handleWalletGesture");
+        try {
+            boolean userSetupComplete = isUserSetupComplete();
+            if (!userSetupComplete) {
+                if (DBG) {
+                    Slog.d(TAG, "userSetupComplete = false, ignoring wallet gesture.");
+                }
+                return false;
+            }
+            if (DBG) {
+                Slog.d(TAG, "userSetupComplete = true, performing wallet gesture.");
+            }
+
+            StatusBarManagerInternal service = LocalServices.getService(
+                    StatusBarManagerInternal.class);
+            service.onWalletLaunchGestureDetected();
+            return true;
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+        }
+    }
+
     /**
      * @return true if emergency gesture UI was launched, false otherwise.
      */
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 09440e0..600b124 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -3227,6 +3227,12 @@
                                         "the type and name should not be empty");
                                 return;
                             }
+                            if (!type.equals(mAccountType)) {
+                                onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
+                                        "incorrect account type");
+                                return;
+                            }
+
                             Account resultAccount = new Account(name, type);
                             if (!customTokens) {
                                 saveAuthTokenToDatabase(
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 60516c3..6cca7d1 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -757,9 +757,6 @@
                 Message msg = obtainMessage(MSG_BG_START_TIMEOUT);
                 sendMessageAtTime(msg, when);
             }
-            if (mStartingBackground.size() < mMaxStartingBackground) {
-                mAm.backgroundServicesFinishedLocked(mUserId);
-            }
         }
     }
 
@@ -8953,18 +8950,12 @@
         if (!mAm.mConstants.mFgsStartRestrictionCheckCallerTargetSdk) {
             return true; // In this case, we only check the service's target SDK level.
         }
-        final int callingUid;
-        if (Flags.newFgsRestrictionLogic()) {
-            // We always consider SYSTEM_UID to target S+, so just enable the restrictions.
-            if (actualCallingUid == Process.SYSTEM_UID) {
-                return true;
-            }
-            callingUid = actualCallingUid;
-        } else {
-            // Legacy logic used mRecentCallingUid.
-            callingUid = r.mRecentCallingUid;
+        // We always consider SYSTEM_UID to target S+, so just enable the restrictions.
+        if (actualCallingUid == Process.SYSTEM_UID) {
+            return true;
         }
-        if (!CompatChanges.isChangeEnabled(FGS_BG_START_RESTRICTION_CHANGE_ID, callingUid)) {
+        if (!CompatChanges.isChangeEnabled(FGS_BG_START_RESTRICTION_CHANGE_ID,
+                actualCallingUid)) {
             return false; // If the caller targets < S, then we still disable the restrictions.
         }
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 50b6990..5a198a1 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -10837,8 +10837,12 @@
                     opti++;
                 }
                 synchronized (this) {
+                    // TODO: b/361161826 - Always pass in the dumpAll and let
+                    // BroadcastController decide how to treat it.
+                    final boolean requestDumpAll = "filter".equals(dumpPackage)
+                            ? dumpAll : true;
                     mBroadcastController.dumpBroadcastsLocked(fd, pw, args, opti,
-                            /* dumpAll= */ true, dumpPackage);
+                            requestDumpAll, dumpPackage);
                 }
             } else if ("broadcast-stats".equals(cmd)) {
                 if (opti < args.length) {
@@ -14370,10 +14374,6 @@
         mBroadcastController.unbroadcastIntent(caller, intent, userId);
     }
 
-    void backgroundServicesFinishedLocked(int userId) {
-        mBroadcastQueue.backgroundServicesFinishedLocked(userId);
-    }
-
     public void finishReceiver(IBinder caller, int resultCode, String resultData,
             Bundle resultExtras, boolean resultAbort, int flags) {
         mBroadcastController.finishReceiver(caller, resultCode, resultData, resultExtras,
@@ -18932,7 +18932,7 @@
                     Settings.Global.BROADCAST_BG_CONSTANTS);
             backConstants.TIMEOUT = BROADCAST_BG_TIMEOUT;
 
-            return new BroadcastQueueModernImpl(service, service.mHandler,
+            return new BroadcastQueueImpl(service, service.mHandler,
                         foreConstants, backConstants);
         }
 
@@ -19374,7 +19374,7 @@
             }
             if (preventIntentRedirectCollectNestedKeysOnServerIfNotCollected()) {
                 // this flag will be ramped to public.
-                intent.collectExtraIntentKeys();
+                intent.collectExtraIntentKeys(true);
             }
         }
 
diff --git a/services/core/java/com/android/server/am/BroadcastConstants.java b/services/core/java/com/android/server/am/BroadcastConstants.java
index e676b1f..81d34f6 100644
--- a/services/core/java/com/android/server/am/BroadcastConstants.java
+++ b/services/core/java/com/android/server/am/BroadcastConstants.java
@@ -111,7 +111,7 @@
     public long ALLOW_BG_ACTIVITY_START_TIMEOUT = DEFAULT_ALLOW_BG_ACTIVITY_START_TIMEOUT;
 
     /**
-     * For {@link BroadcastQueueModernImpl}: Maximum dispatch parallelism
+     * For {@link BroadcastQueueImpl}: Maximum dispatch parallelism
      * that we'll tolerate for ordinary broadcast dispatch.
      */
     public int MAX_RUNNING_PROCESS_QUEUES = DEFAULT_MAX_RUNNING_PROCESS_QUEUES;
@@ -120,7 +120,7 @@
             ActivityManager.isLowRamDeviceStatic() ? 2 : 4;
 
     /**
-     * For {@link BroadcastQueueModernImpl}: Additional running process queue parallelism beyond
+     * For {@link BroadcastQueueImpl}: Additional running process queue parallelism beyond
      * {@link #MAX_RUNNING_PROCESS_QUEUES} for dispatch of "urgent" broadcasts.
      */
     public int EXTRA_RUNNING_URGENT_PROCESS_QUEUES = DEFAULT_EXTRA_RUNNING_URGENT_PROCESS_QUEUES;
@@ -129,7 +129,7 @@
     private static final int DEFAULT_EXTRA_RUNNING_URGENT_PROCESS_QUEUES = 1;
 
     /**
-     * For {@link BroadcastQueueModernImpl}: Maximum number of consecutive urgent
+     * For {@link BroadcastQueueImpl}: Maximum number of consecutive urgent
      * broadcast dispatches allowed before letting broadcasts in lower priority queue
      * to be scheduled in order to avoid starvation.
      */
@@ -139,7 +139,7 @@
     private static final int DEFAULT_MAX_CONSECUTIVE_URGENT_DISPATCHES = 3;
 
     /**
-     * For {@link BroadcastQueueModernImpl}: Maximum number of consecutive normal
+     * For {@link BroadcastQueueImpl}: Maximum number of consecutive normal
      * broadcast dispatches allowed before letting broadcasts in lower priority queue
      * to be scheduled in order to avoid starvation.
      */
@@ -149,7 +149,7 @@
     private static final int DEFAULT_MAX_CONSECUTIVE_NORMAL_DISPATCHES = 10;
 
     /**
-     * For {@link BroadcastQueueModernImpl}: Maximum number of active broadcasts
+     * For {@link BroadcastQueueImpl}: Maximum number of active broadcasts
      * to dispatch to a "running" process queue before we retire them back to
      * being "runnable" to give other processes a chance to run.
      */
@@ -160,7 +160,7 @@
             ActivityManager.isLowRamDeviceStatic() ? 8 : 16;
 
     /**
-     * For {@link BroadcastQueueModernImpl}: Maximum number of active "blocking" broadcasts
+     * For {@link BroadcastQueueImpl}: Maximum number of active "blocking" broadcasts
      * to dispatch to a "running" System process queue before we retire them back to
      * being "runnable" to give other processes a chance to run. Here "blocking" refers to
      * whether or not we are going to block on the finishReceiver() to be called before moving
@@ -173,7 +173,7 @@
             ActivityManager.isLowRamDeviceStatic() ? 8 : 16;
 
     /**
-     * For {@link BroadcastQueueModernImpl}: Maximum number of active non-"blocking" broadcasts
+     * For {@link BroadcastQueueImpl}: Maximum number of active non-"blocking" broadcasts
      * to dispatch to a "running" System process queue before we retire them back to
      * being "runnable" to give other processes a chance to run. Here "blocking" refers to
      * whether or not we are going to block on the finishReceiver() to be called before moving
@@ -187,7 +187,7 @@
             ActivityManager.isLowRamDeviceStatic() ? 32 : 64;
 
     /**
-     * For {@link BroadcastQueueModernImpl}: Maximum number of pending
+     * For {@link BroadcastQueueImpl}: Maximum number of pending
      * broadcasts to hold for a process before we ignore any delays that policy
      * might have applied to that process.
      */
@@ -197,7 +197,7 @@
             ActivityManager.isLowRamDeviceStatic() ? 128 : 256;
 
     /**
-     * For {@link BroadcastQueueModernImpl}: Delay to apply to normal
+     * For {@link BroadcastQueueImpl}: Delay to apply to normal
      * broadcasts, giving a chance for debouncing of rapidly changing events.
      */
     public long DELAY_NORMAL_MILLIS = DEFAULT_DELAY_NORMAL_MILLIS;
@@ -205,7 +205,7 @@
     private static final long DEFAULT_DELAY_NORMAL_MILLIS = +500;
 
     /**
-     * For {@link BroadcastQueueModernImpl}: Delay to apply to broadcasts
+     * For {@link BroadcastQueueImpl}: Delay to apply to broadcasts
      * targeting cached applications.
      */
     public long DELAY_CACHED_MILLIS = DEFAULT_DELAY_CACHED_MILLIS;
@@ -213,7 +213,7 @@
     private static final long DEFAULT_DELAY_CACHED_MILLIS = +120_000;
 
     /**
-     * For {@link BroadcastQueueModernImpl}: Delay to apply to urgent
+     * For {@link BroadcastQueueImpl}: Delay to apply to urgent
      * broadcasts, typically a negative value to indicate they should be
      * executed before most other pending broadcasts.
      */
@@ -222,7 +222,7 @@
     private static final long DEFAULT_DELAY_URGENT_MILLIS = -120_000;
 
     /**
-     * For {@link BroadcastQueueModernImpl}: Delay to apply to broadcasts to
+     * For {@link BroadcastQueueImpl}: Delay to apply to broadcasts to
      * foreground processes, typically a negative value to indicate they should be
      * executed before most other pending broadcasts.
      */
@@ -232,7 +232,7 @@
     private static final long DEFAULT_DELAY_FOREGROUND_PROC_MILLIS = -120_000;
 
     /**
-     * For {@link BroadcastQueueModernImpl}: Delay to apply to broadcasts to
+     * For {@link BroadcastQueueImpl}: Delay to apply to broadcasts to
      * persistent processes, typically a negative value to indicate they should be
      * executed before most other pending broadcasts.
      */
@@ -242,7 +242,7 @@
     private static final long DEFAULT_DELAY_PERSISTENT_PROC_MILLIS = -120_000;
 
     /**
-     * For {@link BroadcastQueueModernImpl}: Maximum number of complete
+     * For {@link BroadcastQueueImpl}: Maximum number of complete
      * historical broadcasts to retain for debugging purposes.
      */
     public int MAX_HISTORY_COMPLETE_SIZE = DEFAULT_MAX_HISTORY_COMPLETE_SIZE;
@@ -251,7 +251,7 @@
             ActivityManager.isLowRamDeviceStatic() ? 64 : 256;
 
     /**
-     * For {@link BroadcastQueueModernImpl}: Maximum number of summarized
+     * For {@link BroadcastQueueImpl}: Maximum number of summarized
      * historical broadcasts to retain for debugging purposes.
      */
     public int MAX_HISTORY_SUMMARY_SIZE = DEFAULT_MAX_HISTORY_SUMMARY_SIZE;
@@ -268,7 +268,7 @@
     private static final boolean DEFAULT_CORE_DEFER_UNTIL_ACTIVE = true;
 
     /**
-     * For {@link BroadcastQueueModernImpl}: How frequently we should check for the pending
+     * For {@link BroadcastQueueImpl}: How frequently we should check for the pending
      * cold start validity.
      */
     public long PENDING_COLD_START_CHECK_INTERVAL_MILLIS =
@@ -278,7 +278,7 @@
     private static final long DEFAULT_PENDING_COLD_START_CHECK_INTERVAL_MILLIS = 30_000;
 
     /**
-     * For {@link BroadcastQueueModernImpl}: Maximum number of outgoing broadcasts from a
+     * For {@link BroadcastQueueImpl}: Maximum number of outgoing broadcasts from a
      * freezable process that will be allowed before killing the process.
      */
     public int MAX_FROZEN_OUTGOING_BROADCASTS = DEFAULT_MAX_FROZEN_OUTGOING_BROADCASTS;
diff --git a/services/core/java/com/android/server/am/BroadcastController.java b/services/core/java/com/android/server/am/BroadcastController.java
index aa06b7e..bfacfbb 100644
--- a/services/core/java/com/android/server/am/BroadcastController.java
+++ b/services/core/java/com/android/server/am/BroadcastController.java
@@ -47,6 +47,8 @@
 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 static com.android.server.am.BroadcastRecord.debugLog;
+import static com.android.server.am.BroadcastRecord.intentToString;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -1017,6 +1019,13 @@
                         android.Manifest.permission.ACCESS_BROADCAST_RESPONSE_STATS,
                         callingPid, callingUid, "recordResponseEventWhileInBackground");
             }
+
+            if (brOptions.isDebugLogEnabled()) {
+                if (!isShellOrRoot(callingUid)
+                        && (callerApp == null || !callerApp.hasActiveInstrumentation())) {
+                    brOptions.setDebugLogEnabled(false);
+                }
+            }
         }
 
         // Verify that protected broadcasts are only being sent by system code,
@@ -1622,6 +1631,10 @@
             }
         }
         while (ir < NR) {
+            // Instant Apps cannot use FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS
+            if (callerInstantApp) {
+                intent.setFlags(intent.getFlags() & ~Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
+            }
             if (receivers == null) {
                 receivers = new ArrayList();
             }
@@ -1647,7 +1660,9 @@
                     callerAppProcessState, mService.mPlatformCompat);
             broadcastSentEventRecord.setBroadcastRecord(r);
 
-            if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing ordered broadcast " + r);
+            if (DEBUG_BROADCAST || r.debugLog()) {
+                Slog.v(TAG_BROADCAST, "Enqueueing broadcast " + r);
+            }
             queue.enqueueBroadcastLocked(r);
         } else {
             // There was nobody interested in the broadcast, but we still want to record
@@ -1657,11 +1672,19 @@
                 // This was an implicit broadcast... let's record it for posterity.
                 addBroadcastStatLocked(intent.getAction(), callerPackage, 0, 0, 0);
             }
+            if (DEBUG_BROADCAST || debugLog(brOptions)) {
+                Slog.v(TAG_BROADCAST, "Skipping broadcast " + intentToString(intent)
+                        + " due to no receivers");
+            }
         }
 
         return ActivityManager.BROADCAST_SUCCESS;
     }
 
+    private boolean isShellOrRoot(int uid) {
+        return uid == SHELL_UID || uid == ROOT_UID;
+    }
+
     @GuardedBy("mService")
     private void scheduleCanceledResultTo(ProcessRecord resultToApp, IIntentReceiver resultTo,
             Intent intent, int userId, BroadcastOptions options, int callingUid,
@@ -2178,6 +2201,8 @@
         boolean printedAnything = false;
         boolean onlyReceivers = false;
         int filteredUid = Process.INVALID_UID;
+        boolean onlyFilter = false;
+        String dumpIntentAction = null;
 
         if ("history".equals(dumpPackage)) {
             if (opti < args.length && "-s".equals(args[opti])) {
@@ -2185,8 +2210,7 @@
             }
             onlyHistory = true;
             dumpPackage = null;
-        }
-        if ("receivers".equals(dumpPackage)) {
+        } else if ("receivers".equals(dumpPackage)) {
             onlyReceivers = true;
             dumpPackage = null;
             if (opti + 2 <= args.length) {
@@ -2205,7 +2229,23 @@
                     }
                 }
             }
+        } else if ("filter".equals(dumpPackage)) {
+            onlyFilter = true;
+            dumpPackage = null;
+            if (opti + 2 <= args.length) {
+                if ("--action".equals(args[opti++])) {
+                    dumpIntentAction = args[opti++];
+                    if (dumpIntentAction == null) {
+                        pw.printf("Missing argument for --action option\n");
+                        return;
+                    }
+                } else {
+                    pw.printf("Unknown argument: %s\n", args[opti]);
+                    return;
+                }
+            }
         }
+
         if (DEBUG_BROADCAST) {
             Slogf.d(TAG_BROADCAST, "dumpBroadcastsLocked(): dumpPackage=%s, onlyHistory=%b, "
                             + "onlyReceivers=%b, filteredUid=%d", dumpPackage, onlyHistory,
@@ -2213,7 +2253,7 @@
         }
 
         pw.println("ACTIVITY MANAGER BROADCAST STATE (dumpsys activity broadcasts)");
-        if (!onlyHistory && dumpAll) {
+        if (!onlyHistory && !onlyFilter && dumpAll) {
             if (mRegisteredReceivers.size() > 0) {
                 boolean printed = false;
                 Iterator it = mRegisteredReceivers.values().iterator();
@@ -2257,14 +2297,14 @@
 
         if (!onlyReceivers) {
             needSep = mBroadcastQueue.dumpLocked(fd, pw, args, opti,
-                    dumpConstants, dumpHistory, dumpAll, dumpPackage, needSep);
+                    dumpConstants, dumpHistory, dumpAll, dumpPackage, dumpIntentAction, needSep);
             printedAnything |= needSep;
         }
 
         needSep = true;
 
         synchronized (mStickyBroadcasts) {
-            if (!onlyHistory && !onlyReceivers && mStickyBroadcasts != null
+            if (!onlyHistory && !onlyReceivers && !onlyFilter && mStickyBroadcasts != null
                     && dumpPackage == null) {
                 for (int user = 0; user < mStickyBroadcasts.size(); user++) {
                     if (needSep) {
@@ -2312,13 +2352,12 @@
             }
         }
 
-        if (!onlyHistory && !onlyReceivers && dumpAll) {
+        if (!onlyHistory && !onlyReceivers && !onlyFilter && dumpAll) {
             pw.println();
-            pw.println("  Queue " + mBroadcastQueue.toString() + ": "
+            pw.println("  Queue " + mBroadcastQueue + ": "
                     + mBroadcastQueue.describeStateLocked());
             pw.println("  mHandler:");
             mService.mHandler.dump(new PrintWriterPrinter(pw), "    ");
-            needSep = true;
             printedAnything = true;
         }
 
diff --git a/services/core/java/com/android/server/am/BroadcastHistory.java b/services/core/java/com/android/server/am/BroadcastHistory.java
index d6e3d43..700cf9c 100644
--- a/services/core/java/com/android/server/am/BroadcastHistory.java
+++ b/services/core/java/com/android/server/am/BroadcastHistory.java
@@ -29,6 +29,7 @@
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Date;
+import java.util.Objects;
 
 /**
  * Collection of recent historical broadcasts that are available to be dumped
@@ -163,10 +164,11 @@
 
     @NeverCompile
     public boolean dumpLocked(@NonNull PrintWriter pw, @Nullable String dumpPackage,
-            @NonNull String queueName, @NonNull SimpleDateFormat sdf,
-            boolean dumpAll, boolean needSep) {
-        dumpBroadcastList(pw, sdf, mFrozenBroadcasts, "Frozen");
-        dumpBroadcastList(pw, sdf, mPendingBroadcasts, "Pending");
+            @Nullable String dumpIntentAction,
+            @NonNull SimpleDateFormat sdf, boolean dumpAll) {
+        boolean needSep = true;
+        dumpBroadcastList(pw, sdf, mFrozenBroadcasts, dumpIntentAction, dumpAll, "Frozen");
+        dumpBroadcastList(pw, sdf, mPendingBroadcasts, dumpIntentAction, dumpAll, "Pending");
 
         int i;
         boolean printed = false;
@@ -187,17 +189,28 @@
             if (dumpPackage != null && !dumpPackage.equals(r.callerPackage)) {
                 continue;
             }
+            if (dumpIntentAction != null && !Objects.equals(dumpIntentAction,
+                    r.intent.getAction())) {
+                continue;
+            }
             if (!printed) {
                 if (needSep) {
                     pw.println();
                 }
                 needSep = true;
-                pw.println("  Historical broadcasts [" + queueName + "]:");
+                pw.println("  Historical broadcasts:");
                 printed = true;
             }
-            if (dumpAll) {
-                pw.print("  Historical Broadcast " + queueName + " #");
-                        pw.print(i); pw.println(":");
+            if (dumpIntentAction != null) {
+                pw.print("  Historical Broadcast #");
+                pw.print(i); pw.println(":");
+                r.dump(pw, "    ", sdf);
+                if (!dumpAll) {
+                    break;
+                }
+            } else if (dumpAll) {
+                pw.print("  Historical Broadcast #");
+                pw.print(i); pw.println(":");
                 r.dump(pw, "    ", sdf);
             } else {
                 pw.print("  #"); pw.print(i); pw.print(": "); pw.println(r);
@@ -213,7 +226,7 @@
             }
         } while (ringIndex != lastIndex);
 
-        if (dumpPackage == null) {
+        if (dumpPackage == null && dumpIntentAction == null) {
             lastIndex = ringIndex = mSummaryHistoryNext;
             if (dumpAll) {
                 printed = false;
@@ -243,7 +256,7 @@
                         pw.println();
                     }
                     needSep = true;
-                    pw.println("  Historical broadcasts summary [" + queueName + "]:");
+                    pw.println("  Historical broadcasts summary:");
                     printed = true;
                 }
                 if (!dumpAll && i >= 50) {
@@ -276,15 +289,28 @@
     }
 
     private void dumpBroadcastList(@NonNull PrintWriter pw, @NonNull SimpleDateFormat sdf,
-            @NonNull ArrayList<BroadcastRecord> broadcasts, @NonNull String flavor) {
+            @NonNull ArrayList<BroadcastRecord> broadcasts, @Nullable String dumpIntentAction,
+            boolean dumpAll, @NonNull String flavor) {
         pw.print("  "); pw.print(flavor); pw.println(" broadcasts:");
         if (broadcasts.isEmpty()) {
             pw.println("    <empty>");
         } else {
+            boolean printedAnything = false;
             for (int idx = broadcasts.size() - 1; idx >= 0; --idx) {
                 final BroadcastRecord r = broadcasts.get(idx);
+                if (dumpIntentAction != null && !Objects.equals(dumpIntentAction,
+                        r.intent.getAction())) {
+                    continue;
+                }
                 pw.print(flavor); pw.print("  broadcast #"); pw.print(idx); pw.println(":");
                 r.dump(pw, "    ", sdf);
+                printedAnything = true;
+                if (dumpIntentAction != null && !dumpAll) {
+                    break;
+                }
+            }
+            if (!printedAnything) {
+                pw.println("    <no-matches>");
             }
         }
     }
diff --git a/services/core/java/com/android/server/am/BroadcastProcessQueue.java b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
index ed3cd1e..db0562f 100644
--- a/services/core/java/com/android/server/am/BroadcastProcessQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
@@ -453,7 +453,7 @@
      *
      * @return if this operation may have changed internal state, indicating
      *         that the caller is responsible for invoking
-     *         {@link BroadcastQueueModernImpl#updateRunnableList}
+     *         {@link BroadcastQueueImpl#updateRunnableList}
      */
     @CheckResult
     public boolean forEachMatchingBroadcast(@NonNull BroadcastPredicate predicate,
@@ -502,7 +502,7 @@
      *
      * @return if this operation may have changed internal state, indicating
      *         that the caller is responsible for invoking
-     *         {@link BroadcastQueueModernImpl#updateRunnableList}
+     *         {@link BroadcastQueueImpl#updateRunnableList}
      */
     @CheckResult
     public boolean setProcessAndUidState(@Nullable ProcessRecord app, boolean uidForeground,
@@ -837,7 +837,7 @@
     /**
      * @return if this operation may have changed internal state, indicating
      *         that the caller is responsible for invoking
-     *         {@link BroadcastQueueModernImpl#updateRunnableList}
+     *         {@link BroadcastQueueImpl#updateRunnableList}
      */
     @CheckResult
     boolean forceDelayBroadcastDelivery(long delayedDurationMs) {
@@ -921,7 +921,7 @@
      *
      * @return if this operation may have changed internal state, indicating
      *         that the caller is responsible for invoking
-     *         {@link BroadcastQueueModernImpl#updateRunnableList}
+     *         {@link BroadcastQueueImpl#updateRunnableList}
      */
     @CheckResult
     @VisibleForTesting
@@ -945,7 +945,7 @@
      *
      * @return if this operation may have changed internal state, indicating
      *         that the caller is responsible for invoking
-     *         {@link BroadcastQueueModernImpl#updateRunnableList}
+     *         {@link BroadcastQueueImpl#updateRunnableList}
      */
     @CheckResult
     boolean removePrioritizeEarliestRequest() {
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 6386af6..5b5ceaa 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -49,14 +49,12 @@
     final @NonNull Handler mHandler;
     final @NonNull BroadcastSkipPolicy mSkipPolicy;
     final @NonNull BroadcastHistory mHistory;
-    final @NonNull String mQueueName;
 
     BroadcastQueue(@NonNull ActivityManagerService service, @NonNull Handler handler,
-            @NonNull String name, @NonNull BroadcastSkipPolicy skipPolicy,
+            @NonNull BroadcastSkipPolicy skipPolicy,
             @NonNull BroadcastHistory history) {
         mService = Objects.requireNonNull(service);
         mHandler = Objects.requireNonNull(handler);
-        mQueueName = Objects.requireNonNull(name);
         mSkipPolicy = Objects.requireNonNull(skipPolicy);
         mHistory = Objects.requireNonNull(history);
     }
@@ -87,11 +85,6 @@
                 TAG, cookie);
     }
 
-    @Override
-    public String toString() {
-        return mQueueName;
-    }
-
     public abstract void start(@NonNull ContentResolver resolver);
 
     /**
@@ -129,9 +122,6 @@
             @Nullable String resultData, @Nullable Bundle resultExtras, boolean resultAbort,
             boolean waitForServices);
 
-    @GuardedBy("mService")
-    public abstract void backgroundServicesFinishedLocked(int userId);
-
     /**
      * Signal from OS internals that the given process has just been actively
      * attached, and is ready to begin receiving broadcasts.
@@ -244,8 +234,6 @@
 
     /**
      * Delays delivering broadcasts to the specified package.
-     *
-     * <p> Note that this is only valid for modern queue.
      */
     public void forceDelayBroadcastDelivery(@NonNull String targetPackage,
             long delayedDurationMs) {
@@ -264,7 +252,8 @@
     @GuardedBy("mService")
     public abstract boolean dumpLocked(@NonNull FileDescriptor fd, @NonNull PrintWriter pw,
             @NonNull String[] args, int opti, boolean dumpConstants, boolean dumpHistory,
-            boolean dumpAll, @Nullable String dumpPackage, boolean needSep);
+            boolean dumpAll, @Nullable String dumpPackage, @Nullable String dumpIntentAction,
+            boolean needSep);
 
     /**
      * Execute {@link #dumpLocked} and store the output into
@@ -276,7 +265,7 @@
                     PrintWriter pw = new PrintWriter(out)) {
                 pw.print("Message: ");
                 pw.println(msg);
-                dumpLocked(fd, pw, null, 0, false, false, false, null, false);
+                dumpLocked(fd, pw, null, 0, false, false, false, null, null, false);
                 pw.flush();
             }
         }, DropBoxManager.IS_TEXT);
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.md b/services/core/java/com/android/server/am/BroadcastQueue.md
index 8131793..16c4028 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.md
+++ b/services/core/java/com/android/server/am/BroadcastQueue.md
@@ -26,7 +26,7 @@
 
 ## Per-process queues
 
-The design of `BroadcastQueueModernImpl` is centered around maintaining a
+The design of `BroadcastQueueImpl` is centered around maintaining a
 separate `BroadcastProcessQueue` instance for each potential process on the
 device. At this level, a process refers to the `android:process` attributes
 defined in `AndroidManifest.xml` files, which means it can be defined and
@@ -57,7 +57,7 @@
 ## Parallel dispatch
 
 Given a collection of per-process queues with valid _runnable at_ timestamps,
-BroadcastQueueModernImpl is then willing to promote those _runnable_ queues
+BroadcastQueueImpl is then willing to promote those _runnable_ queues
 into a _running_ state. We choose the next per-process queue to promote based
 on the sorted ordering of the _runnable at_ timestamps, selecting the
 longest-waiting process first, which aims to reduce overall broadcast dispatch
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueImpl.java
similarity index 97%
rename from services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
rename to services/core/java/com/android/server/am/BroadcastQueueImpl.java
index 9e4666c..36035bd 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueImpl.java
@@ -137,17 +137,17 @@
  * {@link #finishReceiverLocked}
  * </ol>
  */
-class BroadcastQueueModernImpl extends BroadcastQueue {
-    BroadcastQueueModernImpl(ActivityManagerService service, Handler handler,
+class BroadcastQueueImpl extends BroadcastQueue {
+    BroadcastQueueImpl(ActivityManagerService service, Handler handler,
             BroadcastConstants fgConstants, BroadcastConstants bgConstants) {
         this(service, handler, fgConstants, bgConstants, new BroadcastSkipPolicy(service),
                 new BroadcastHistory(fgConstants));
     }
 
-    BroadcastQueueModernImpl(ActivityManagerService service, Handler handler,
+    BroadcastQueueImpl(ActivityManagerService service, Handler handler,
             BroadcastConstants fgConstants, BroadcastConstants bgConstants,
             BroadcastSkipPolicy skipPolicy, BroadcastHistory history) {
-        super(service, handler, "modern", skipPolicy, history);
+        super(service, handler, skipPolicy, history);
 
         // For the moment, read agnostic constants from foreground
         mConstants = Objects.requireNonNull(fgConstants);
@@ -545,8 +545,9 @@
                 }
             }
 
-            if (DEBUG_BROADCAST) logv("Promoting " + queue
-                    + " from runnable to running; process is " + queue.app);
+            if (DEBUG_BROADCAST) {
+                logv("Promoting " + queue + " from runnable to running; process is " + queue.app);
+            }
             promoteToRunningLocked(queue);
             boolean completed;
             if (processWarm) {
@@ -798,7 +799,9 @@
             mService.mOomAdjuster.mCachedAppOptimizer.freezeAppAsyncImmediateLSP(r.callerApp);
             return;
         }
-        if (DEBUG_BROADCAST) logv("Enqueuing " + r + " for " + r.receivers.size() + " receivers");
+        if (DEBUG_BROADCAST || r.debugLog()) {
+            logv("Enqueuing " + r + " for " + r.receivers.size() + " receivers");
+        }
 
         final int cookie = traceBegin("enqueueBroadcast");
         r.applySingletonPolicy(mService);
@@ -1019,7 +1022,9 @@
                 & Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0;
 
         long startTimeNs = SystemClock.uptimeNanos();
-        if (DEBUG_BROADCAST) logv("Scheduling " + r + " to cold " + queue);
+        if (DEBUG_BROADCAST || r.debugLog()) {
+            logv("Scheduling " + r + " to cold " + queue);
+        }
         queue.app = mService.startProcessLocked(queue.processName, info, true, intentFlags,
                 hostingRecord, zygotePolicyFlags, allowWhileBooting, false);
         if (queue.app == null) {
@@ -1176,7 +1181,9 @@
             }
         }
 
-        if (DEBUG_BROADCAST) logv("Scheduling " + r + " to warm " + app);
+        if (DEBUG_BROADCAST || r.debugLog()) {
+            logv("Scheduling " + r + " to warm " + app);
+        }
         setDeliveryState(queue, app, r, index, receiver, BroadcastRecord.DELIVERY_SCHEDULED,
                 "scheduleReceiverWarmLocked");
 
@@ -1192,12 +1199,12 @@
                 if (receiver instanceof BroadcastFilter) {
                     notifyScheduleRegisteredReceiver(app, r, (BroadcastFilter) receiver);
                     thread.scheduleRegisteredReceiver(
-                        ((BroadcastFilter) receiver).receiverList.receiver,
-                        receiverIntent, r.resultCode, r.resultData, r.resultExtras,
-                        r.ordered, r.initialSticky, assumeDelivered, r.userId,
-                        app.mState.getReportedProcState(),
-                        r.shareIdentity ? r.callingUid : Process.INVALID_UID,
-                        r.shareIdentity ? r.callerPackage : null);
+                            ((BroadcastFilter) receiver).receiverList.receiver,
+                            receiverIntent, r.resultCode, r.resultData, r.resultExtras,
+                            r.ordered, r.initialSticky, assumeDelivered, r.userId,
+                            app.mState.getReportedProcState(),
+                            r.shareIdentity ? r.callingUid : Process.INVALID_UID,
+                            r.shareIdentity ? r.callerPackage : null);
                     // TODO: consider making registered receivers of unordered
                     // broadcasts report results to detect ANRs
                     if (assumeDelivered) {
@@ -1562,12 +1569,17 @@
         // bookkeeping to update for ordered broadcasts
         if (!isDeliveryStateTerminal(oldDeliveryState)
                 && isDeliveryStateTerminal(newDeliveryState)) {
-            if (DEBUG_BROADCAST
-                    && newDeliveryState != BroadcastRecord.DELIVERY_DELIVERED) {
-                logw("Delivery state of " + r + " to " + receiver
+            if ((DEBUG_BROADCAST && newDeliveryState != BroadcastRecord.DELIVERY_DELIVERED)
+                    || r.debugLog()) {
+                final String msg = "Delivery state of " + r + " to " + receiver
                         + " via " + app + " changed from "
                         + deliveryStateToString(oldDeliveryState) + " to "
-                        + deliveryStateToString(newDeliveryState) + " because " + reason);
+                        + deliveryStateToString(newDeliveryState) + " because " + reason;
+                if (newDeliveryState == BroadcastRecord.DELIVERY_DELIVERED) {
+                    logv(msg);
+                } else {
+                    logw(msg);
+                }
             }
 
             notifyFinishReceiver(queue, app, r, index, receiver);
@@ -1911,13 +1923,6 @@
         return getRunningSize() + " running";
     }
 
-    @GuardedBy("mService")
-    @Override
-    public void backgroundServicesFinishedLocked(int userId) {
-        // Modern queue does not alter the broadcasts delivery behavior based on background
-        // services, so ignore.
-    }
-
     private void checkHealth() {
         synchronized (mService) {
             checkHealthLocked();
@@ -2407,7 +2412,6 @@
     @GuardedBy("mService")
     public void dumpDebug(@NonNull ProtoOutputStream proto, long fieldId) {
         long token = proto.start(fieldId);
-        proto.write(BroadcastQueueProto.QUEUE_NAME, mQueueName);
         mHistory.dumpDebug(proto);
         proto.end(token);
     }
@@ -2417,12 +2421,33 @@
     @GuardedBy("mService")
     public boolean dumpLocked(@NonNull FileDescriptor fd, @NonNull PrintWriter pw,
             @NonNull String[] args, int opti, boolean dumpConstants, boolean dumpHistory,
-            boolean dumpAll, @Nullable String dumpPackage, boolean needSep) {
+            boolean dumpAll, @Nullable String dumpPackage, @Nullable String dumpIntentAction,
+            boolean needSep) {
         final long now = SystemClock.uptimeMillis();
         final IndentingPrintWriter ipw = new IndentingPrintWriter(pw);
         ipw.increaseIndent();
         ipw.println();
 
+        if (dumpIntentAction == null) {
+            dumpProcessQueues(ipw, now);
+            dumpBroadcastsWithIgnoredPolicies(ipw);
+            dumpForegroundUids(ipw);
+
+            if (dumpConstants) {
+                mFgConstants.dump(ipw);
+                mBgConstants.dump(ipw);
+            }
+        }
+
+        if (dumpHistory) {
+            final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
+            needSep = mHistory.dumpLocked(ipw, dumpPackage, dumpIntentAction,
+                    sdf, dumpAll);
+        }
+        return needSep;
+    }
+
+    private void dumpProcessQueues(@NonNull IndentingPrintWriter ipw, @UptimeMillisLong long now) {
         ipw.println("📋 Per-process queues:");
         ipw.increaseIndent();
         for (int i = 0; i < mProcessQueues.size(); i++) {
@@ -2470,28 +2495,21 @@
         }
         ipw.decreaseIndent();
         ipw.println();
+    }
 
+    private void dumpBroadcastsWithIgnoredPolicies(@NonNull IndentingPrintWriter ipw) {
         ipw.println("Broadcasts with ignored delivery group policies:");
         ipw.increaseIndent();
         mService.dumpDeliveryGroupPolicyIgnoredActions(ipw);
         ipw.decreaseIndent();
         ipw.println();
+    }
 
+    private void dumpForegroundUids(@NonNull IndentingPrintWriter ipw) {
         ipw.println("Foreground UIDs:");
         ipw.increaseIndent();
         ipw.println(mUidForeground);
         ipw.decreaseIndent();
         ipw.println();
-
-        if (dumpConstants) {
-            mFgConstants.dump(ipw);
-            mBgConstants.dump(ipw);
-        }
-
-        if (dumpHistory) {
-            final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
-            needSep = mHistory.dumpLocked(ipw, dumpPackage, mQueueName, sdf, dumpAll, needSep);
-        }
-        return needSep;
     }
 }
diff --git a/services/core/java/com/android/server/am/BroadcastRecord.java b/services/core/java/com/android/server/am/BroadcastRecord.java
index 8d0805d..c1b0a76 100644
--- a/services/core/java/com/android/server/am/BroadcastRecord.java
+++ b/services/core/java/com/android/server/am/BroadcastRecord.java
@@ -1285,31 +1285,43 @@
     }
 
     @Override
+    @NonNull
     public String toString() {
         if (mCachedToString == null) {
-            String label = intent.getAction();
-            if (label == null) {
-                label = intent.toString();
-            }
             mCachedToString = "BroadcastRecord{" + toShortString() + "}";
         }
         return mCachedToString;
     }
 
+    @NonNull
     public String toShortString() {
         if (mCachedToShortString == null) {
-            String label = intent.getAction();
-            if (label == null) {
-                label = intent.toString();
-            }
+            final String label = intentToString(intent);
             mCachedToShortString = Integer.toHexString(System.identityHashCode(this))
                     + " " + label + "/u" + userId;
         }
         return mCachedToShortString;
     }
 
+    @NonNull
+    public static String intentToString(@NonNull Intent intent) {
+        String label = intent.getAction();
+        if (label == null) {
+            label = intent.toString();
+        }
+        return label;
+    }
+
+    public boolean debugLog() {
+        return debugLog(options);
+    }
+
+    public static boolean debugLog(@Nullable BroadcastOptions options) {
+        return options != null && options.isDebugLogEnabled();
+    }
+
     @NeverCompile
-    public void dumpDebug(ProtoOutputStream proto, long fieldId) {
+    public void dumpDebug(@NonNull ProtoOutputStream proto, long fieldId) {
         long token = proto.start(fieldId);
         proto.write(BroadcastRecordProto.USER_ID, userId);
         proto.write(BroadcastRecordProto.INTENT_ACTION, intent.getAction());
diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java
index 6e09a84..e4e53f4 100644
--- a/services/core/java/com/android/server/am/ContentProviderHelper.java
+++ b/services/core/java/com/android/server/am/ContentProviderHelper.java
@@ -1434,7 +1434,7 @@
                                     }
                                 }
                             }
-                        } catch (RemoteException ignored) {
+                        } catch (RemoteException|SecurityException ignored) {
                         }
                     });
                 }
diff --git a/services/core/java/com/android/server/am/OWNERS b/services/core/java/com/android/server/am/OWNERS
index 1a6051b..cc6fabc 100644
--- a/services/core/java/com/android/server/am/OWNERS
+++ b/services/core/java/com/android/server/am/OWNERS
@@ -21,6 +21,7 @@
 per-file HostingRecord.java = file:/ACTIVITY_MANAGER_OWNERS
 per-file App*ExitInfo* = file:/ACTIVITY_MANAGER_OWNERS
 per-file appexitinfo.proto = file:/ACTIVITY_MANAGER_OWNERS
+per-file UidObserverController* = file:/ACTIVITY_MANAGER_OWNERS
 per-file App*StartInfo* = file:/PERFORMANCE_OWNERS
 per-file appstartinfo.proto = file:/PERFORMANCE_OWNERS
 
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 3817ba1..0b78901 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -305,6 +305,10 @@
         this.stringName = null;
     }
 
+    @VisibleForTesting TempAllowListDuration getAllowlistDurationLocked(IBinder allowlistToken) {
+        return mAllowlistDuration.get(allowlistToken);
+    }
+
     void setAllowBgActivityStarts(IBinder token, int flags) {
         if (token == null) return;
         if ((flags & FLAG_ACTIVITY_SENDER) != 0) {
@@ -323,6 +327,12 @@
         mAllowBgActivityStartsForActivitySender.remove(token);
         mAllowBgActivityStartsForBroadcastSender.remove(token);
         mAllowBgActivityStartsForServiceSender.remove(token);
+        if (mAllowlistDuration != null) {
+            mAllowlistDuration.remove(token);
+            if (mAllowlistDuration.isEmpty()) {
+                mAllowlistDuration = null;
+            }
+        }
     }
 
     public void registerCancelListenerLocked(IResultReceiver receiver) {
@@ -703,7 +713,7 @@
         return res;
     }
 
-    private BackgroundStartPrivileges getBackgroundStartPrivilegesForActivitySender(
+    @VisibleForTesting BackgroundStartPrivileges getBackgroundStartPrivilegesForActivitySender(
             IBinder allowlistToken) {
         return mAllowBgActivityStartsForActivitySender.contains(allowlistToken)
                 ? BackgroundStartPrivileges.allowBackgroundActivityStarts(allowlistToken)
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index bddde9d..2216f27 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -3212,7 +3212,6 @@
         if ((pid > 0 && pid != ActivityManagerService.MY_PID)
                 || (pid == 0 && app.isPendingStart())) {
             if (pid > 0) {
-                mService.removePidLocked(pid, app);
                 app.setBindMountPending(false);
                 mService.mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
                 mService.mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
@@ -3230,6 +3229,12 @@
                 }
             }
             app.killLocked(reason, reasonCode, subReason, true, async);
+            if (pid > 0) {
+                // Remove pid record mapping after killing the process, so there won't be a short
+                // period that the app is still alive but its access to system may be illegal due
+                // to no existing record for its pid.
+                mService.removePidLocked(pid, app);
+            }
             mService.handleAppDiedLocked(app, pid, willRestart, allowRestart,
                     false /* fromBinderDied */);
             if (willRestart) {
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 49149e1..1503d88 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -1005,6 +1005,11 @@
     }
 
     @GuardedBy(anyOf = {"mService", "mProcLock"})
+    boolean hasActiveInstrumentation() {
+        return mInstr != null;
+    }
+
+    @GuardedBy(anyOf = {"mService", "mProcLock"})
     boolean isKilledByAm() {
         return mKilledByAm;
     }
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 92d33c9..ca34a13 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -278,24 +278,21 @@
      * Whether to use the new "while-in-use permission" logic for FGS start
      */
     private boolean useNewWiuLogic_forStart() {
-        return Flags.newFgsRestrictionLogic() // This flag should only be set on V+
-                && CompatChanges.isChangeEnabled(USE_NEW_WIU_LOGIC_FOR_START, appInfo.uid);
+        return CompatChanges.isChangeEnabled(USE_NEW_WIU_LOGIC_FOR_START, appInfo.uid);
     }
 
     /**
      * Whether to use the new "while-in-use permission" logic for capabilities
      */
     private boolean useNewWiuLogic_forCapabilities() {
-        return Flags.newFgsRestrictionLogic() // This flag should only be set on V+
-                && CompatChanges.isChangeEnabled(USE_NEW_WIU_LOGIC_FOR_CAPABILITIES, appInfo.uid);
+        return CompatChanges.isChangeEnabled(USE_NEW_WIU_LOGIC_FOR_CAPABILITIES, appInfo.uid);
     }
 
     /**
      * Whether to use the new "FGS BG start exemption" logic.
      */
     private boolean useNewBfslLogic() {
-        return Flags.newFgsRestrictionLogic() // This flag should only be set on V+
-                && CompatChanges.isChangeEnabled(USE_NEW_BFSL_LOGIC, appInfo.uid);
+        return CompatChanges.isChangeEnabled(USE_NEW_BFSL_LOGIC, appInfo.uid);
     }
 
 
diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
index c82933c..c99e8c8 100644
--- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
+++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java
@@ -382,6 +382,12 @@
             newSingleThreadScheduledExecutor(),
             (DeviceConfig.Properties properties) -> {
 
+              // send prop stage request to new storage
+              if (enableAconfigStorageDaemon()) {
+                  stageFlagsInNewStorage(properties);
+                  return;
+              }
+
               for (String flagName : properties.getKeyset()) {
                   String flagValue = properties.getString(flagName, null);
                   if (flagName == null || flagValue == null) {
@@ -409,11 +415,6 @@
                   setProperty(propertyName, flagValue);
               }
 
-              // send prop stage request to new storage
-              if (enableAconfigStorageDaemon()) {
-                  stageFlagsInNewStorage(properties);
-              }
-
         });
 
         // add prop sync callback for flag local overrides
@@ -423,6 +424,7 @@
             (DeviceConfig.Properties properties) -> {
                 if (enableAconfigStorageDaemon()) {
                     setLocalOverridesInNewStorage(properties);
+                    return;
                 }
 
                 if (Flags.supportLocalOverridesSysprops()) {
diff --git a/services/core/java/com/android/server/am/UidObserverController.java b/services/core/java/com/android/server/am/UidObserverController.java
index 7eeec32..f58e3f8 100644
--- a/services/core/java/com/android/server/am/UidObserverController.java
+++ b/services/core/java/com/android/server/am/UidObserverController.java
@@ -31,6 +31,7 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Message;
+import android.os.Process;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.os.SystemClock;
@@ -90,10 +91,12 @@
                 + UUID.randomUUID().toString());
 
         synchronized (mLock) {
+            final boolean canInteractAcrossUsers = ActivityManager.checkComponentPermission(
+                    INTERACT_ACROSS_USERS_FULL, callingUid, Process.INVALID_UID, true)
+                            == PackageManager.PERMISSION_GRANTED;
             mUidObservers.register(observer, new UidObserverRegistration(callingUid,
                     callingPackage, which, cutpoint,
-                    ActivityManager.checkUidPermission(INTERACT_ACROSS_USERS_FULL, callingUid)
-                    == PackageManager.PERMISSION_GRANTED, uids, token));
+                    canInteractAcrossUsers, uids, token));
         }
 
         return token;
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index c31b9ef..ec74f60 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -160,6 +160,7 @@
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.BiConsumer;
 import java.util.function.Consumer;
 
 /**
@@ -1920,8 +1921,14 @@
                 return false;
             }
 
-            mHandler.post(() -> startUserInternalOnHandler(userId, oldUserId, userStartMode,
-                    unlockListener, callingUid, callingPid));
+            final Runnable continueStartUserInternal = () -> continueStartUserInternal(userInfo,
+                    oldUserId, userStartMode, unlockListener, callingUid, callingPid);
+            if (foreground) {
+                mHandler.post(() -> dispatchOnBeforeUserSwitching(userId, () ->
+                        mHandler.post(continueStartUserInternal)));
+            } else {
+                continueStartUserInternal.run();
+            }
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
@@ -1929,11 +1936,11 @@
         return true;
     }
 
-    private void startUserInternalOnHandler(int userId, int oldUserId, int userStartMode,
+    private void continueStartUserInternal(UserInfo userInfo, int oldUserId, int userStartMode,
             IProgressListener unlockListener, int callingUid, int callingPid) {
         final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
         final boolean foreground = userStartMode == USER_START_MODE_FOREGROUND;
-        final UserInfo userInfo = getUserInfo(userId);
+        final int userId = userInfo.id;
 
         boolean needStart = false;
         boolean updateUmState = false;
@@ -1995,7 +2002,6 @@
             // it should be moved outside, but for now it's not as there are many calls to
             // external components here afterwards
             updateProfileRelatedCaches();
-            dispatchOnBeforeUserSwitching(userId);
             mInjector.getWindowManager().setCurrentUser(userId);
             mInjector.reportCurWakefulnessUsageEvent();
             // Once the internal notion of the active user has switched, we lock the device
@@ -2296,25 +2302,42 @@
         mUserSwitchObservers.finishBroadcast();
     }
 
-    private void dispatchOnBeforeUserSwitching(@UserIdInt int newUserId) {
+    private void dispatchOnBeforeUserSwitching(@UserIdInt int newUserId, Runnable onComplete) {
         final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
         t.traceBegin("dispatchOnBeforeUserSwitching-" + newUserId);
-        final int observerCount = mUserSwitchObservers.beginBroadcast();
-        for (int i = 0; i < observerCount; i++) {
-            final String name = "#" + i + " " + mUserSwitchObservers.getBroadcastCookie(i);
-            t.traceBegin("onBeforeUserSwitching-" + name);
+        final AtomicBoolean isFirst = new AtomicBoolean(true);
+        startTimeoutForOnBeforeUserSwitching(isFirst, onComplete);
+        informUserSwitchObservers((observer, callback) -> {
             try {
-                mUserSwitchObservers.getBroadcastItem(i).onBeforeUserSwitching(newUserId);
+                observer.onBeforeUserSwitching(newUserId, callback);
             } catch (RemoteException e) {
-                // Ignore
-            } finally {
-                t.traceEnd();
+                // ignore
             }
-        }
-        mUserSwitchObservers.finishBroadcast();
+        }, () -> {
+            if (isFirst.getAndSet(false)) {
+                onComplete.run();
+            }
+        }, "onBeforeUserSwitching");
         t.traceEnd();
     }
 
+    private void startTimeoutForOnBeforeUserSwitching(AtomicBoolean isFirst,
+            Runnable onComplete) {
+        final long timeout = getUserSwitchTimeoutMs();
+        mHandler.postDelayed(() -> {
+            if (isFirst.getAndSet(false)) {
+                String unresponsiveObservers;
+                synchronized (mLock) {
+                    unresponsiveObservers = String.join(", ", mCurWaitingUserSwitchCallbacks);
+                }
+                Slogf.e(TAG, "Timeout on dispatchOnBeforeUserSwitching. These UserSwitchObservers "
+                        + "did not respond in " + timeout + "ms: " + unresponsiveObservers + ".");
+                onComplete.run();
+            }
+        }, timeout);
+    }
+
+
     /** Called on handler thread */
     @VisibleForTesting
     void dispatchUserSwitchComplete(@UserIdInt int oldUserId, @UserIdInt int newUserId) {
@@ -2527,70 +2550,76 @@
         t.traceBegin("dispatchUserSwitch-" + oldUserId + "-to-" + newUserId);
 
         EventLog.writeEvent(EventLogTags.UC_DISPATCH_USER_SWITCH, oldUserId, newUserId);
-
-        final int observerCount = mUserSwitchObservers.beginBroadcast();
-        if (observerCount > 0) {
-            final ArraySet<String> curWaitingUserSwitchCallbacks = new ArraySet<>();
-            synchronized (mLock) {
-                uss.switching = true;
-                mCurWaitingUserSwitchCallbacks = curWaitingUserSwitchCallbacks;
+        uss.switching = true;
+        informUserSwitchObservers((observer, callback) -> {
+            try {
+                observer.onUserSwitching(newUserId, callback);
+            } catch (RemoteException e) {
+                // ignore
             }
-            final AtomicInteger waitingCallbacksCount = new AtomicInteger(observerCount);
-            final long userSwitchTimeoutMs = getUserSwitchTimeoutMs();
-            final long dispatchStartedTime = SystemClock.elapsedRealtime();
-            for (int i = 0; i < observerCount; i++) {
-                final long dispatchStartedTimeForObserver = SystemClock.elapsedRealtime();
-                try {
-                    // Prepend with unique prefix to guarantee that keys are unique
-                    final String name = "#" + i + " " + mUserSwitchObservers.getBroadcastCookie(i);
-                    synchronized (mLock) {
-                        curWaitingUserSwitchCallbacks.add(name);
-                    }
-                    final IRemoteCallback callback = new IRemoteCallback.Stub() {
-                        @Override
-                        public void sendResult(Bundle data) throws RemoteException {
-                            asyncTraceEnd("onUserSwitching-" + name, newUserId);
-                            synchronized (mLock) {
-                                long delayForObserver = SystemClock.elapsedRealtime()
-                                        - dispatchStartedTimeForObserver;
-                                if (delayForObserver > LONG_USER_SWITCH_OBSERVER_WARNING_TIME_MS) {
-                                    Slogf.w(TAG, "User switch slowed down by observer " + name
-                                            + ": result took " + delayForObserver
-                                            + " ms to process.");
-                                }
-
-                                long totalDelay = SystemClock.elapsedRealtime()
-                                        - dispatchStartedTime;
-                                if (totalDelay > userSwitchTimeoutMs) {
-                                    Slogf.e(TAG, "User switch timeout: observer " + name
-                                            + "'s result was received " + totalDelay
-                                            + " ms after dispatchUserSwitch.");
-                                }
-
-                                curWaitingUserSwitchCallbacks.remove(name);
-                                // Continue switching if all callbacks have been notified and
-                                // user switching session is still valid
-                                if (waitingCallbacksCount.decrementAndGet() == 0
-                                        && (curWaitingUserSwitchCallbacks
-                                        == mCurWaitingUserSwitchCallbacks)) {
-                                    sendContinueUserSwitchLU(uss, oldUserId, newUserId);
-                                }
-                            }
-                        }
-                    };
-                    asyncTraceBegin("onUserSwitching-" + name, newUserId);
-                    mUserSwitchObservers.getBroadcastItem(i).onUserSwitching(newUserId, callback);
-                } catch (RemoteException e) {
-                    // Ignore
-                }
-            }
-        } else {
+        }, () -> {
             synchronized (mLock) {
                 sendContinueUserSwitchLU(uss, oldUserId, newUserId);
             }
+        }, "onUserSwitching");
+        t.traceEnd();
+    }
+
+    void informUserSwitchObservers(BiConsumer<IUserSwitchObserver, IRemoteCallback> consumer,
+            final Runnable onComplete, String trace) {
+        final int observerCount = mUserSwitchObservers.beginBroadcast();
+        if (observerCount == 0) {
+            onComplete.run();
+            mUserSwitchObservers.finishBroadcast();
+            return;
+        }
+        final ArraySet<String> curWaitingUserSwitchCallbacks = new ArraySet<>();
+        synchronized (mLock) {
+            mCurWaitingUserSwitchCallbacks = curWaitingUserSwitchCallbacks;
+        }
+        final AtomicInteger waitingCallbacksCount = new AtomicInteger(observerCount);
+        final long userSwitchTimeoutMs = getUserSwitchTimeoutMs();
+        final long dispatchStartedTime = SystemClock.elapsedRealtime();
+        for (int i = 0; i < observerCount; i++) {
+            final long dispatchStartedTimeForObserver = SystemClock.elapsedRealtime();
+            // Prepend with unique prefix to guarantee that keys are unique
+            final String name = "#" + i + " " + mUserSwitchObservers.getBroadcastCookie(i);
+            synchronized (mLock) {
+                curWaitingUserSwitchCallbacks.add(name);
+            }
+            final IRemoteCallback callback = new IRemoteCallback.Stub() {
+                @Override
+                public void sendResult(Bundle data) throws RemoteException {
+                    asyncTraceEnd(trace + "-" + name, 0);
+                    synchronized (mLock) {
+                        long delayForObserver = SystemClock.elapsedRealtime()
+                                - dispatchStartedTimeForObserver;
+                        if (delayForObserver > LONG_USER_SWITCH_OBSERVER_WARNING_TIME_MS) {
+                            Slogf.w(TAG, "User switch slowed down by observer " + name
+                                    + ": result took " + delayForObserver
+                                    + " ms to process. " + trace);
+                        }
+                        long totalDelay = SystemClock.elapsedRealtime() - dispatchStartedTime;
+                        if (totalDelay > userSwitchTimeoutMs) {
+                            Slogf.e(TAG, "User switch timeout: observer " + name
+                                    + "'s result was received " + totalDelay
+                                    + " ms after dispatchUserSwitch. " + trace);
+                        }
+                        curWaitingUserSwitchCallbacks.remove(name);
+                        // Continue switching if all callbacks have been notified and
+                        // user switching session is still valid
+                        if (waitingCallbacksCount.decrementAndGet() == 0
+                                && (curWaitingUserSwitchCallbacks
+                                == mCurWaitingUserSwitchCallbacks)) {
+                            onComplete.run();
+                        }
+                    }
+                }
+            };
+            asyncTraceBegin(trace + "-" + name, 0);
+            consumer.accept(mUserSwitchObservers.getBroadcastItem(i), callback);
         }
         mUserSwitchObservers.finishBroadcast();
-        t.traceEnd(); // end dispatchUserSwitch-
     }
 
     @GuardedBy("mLock")
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 295e044..8a63f9a 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -359,7 +359,7 @@
     private static final Duration RATE_LIMITER_WINDOW = Duration.ofMillis(10);
     private final RateLimiter mRateLimiter = new RateLimiter(RATE_LIMITER_WINDOW);
 
-    volatile @NonNull HistoricalRegistry mHistoricalRegistry = new HistoricalRegistry(this);
+    volatile @NonNull HistoricalRegistry mHistoricalRegistry;
 
     /*
      * These are app op restrictions imposed per user from various parties.
@@ -1039,6 +1039,8 @@
         // will not exist and the nonce will be UNSET.
         AppOpsManager.invalidateAppOpModeCache();
         AppOpsManager.disableAppOpModeCache();
+
+        mHistoricalRegistry = new HistoricalRegistry(this, context);
     }
 
     public void publish() {
diff --git a/services/core/java/com/android/server/appop/AttributedOp.java b/services/core/java/com/android/server/appop/AttributedOp.java
index 4d114b4..9dd09ce 100644
--- a/services/core/java/com/android/server/appop/AttributedOp.java
+++ b/services/core/java/com/android/server/appop/AttributedOp.java
@@ -113,7 +113,7 @@
         mAppOpsService.mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid,
                 parent.packageName, persistentDeviceId, tag, uidState, flags, accessTime,
                 AppOpsManager.ATTRIBUTION_FLAGS_NONE, AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE,
-                DiscreteRegistry.ACCESS_TYPE_NOTE_OP, accessCount);
+                DiscreteOpsRegistry.ACCESS_TYPE_NOTE_OP, accessCount);
     }
 
     /**
@@ -257,7 +257,8 @@
         if (isStarted) {
             mAppOpsService.mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid,
                     parent.packageName, persistentDeviceId, tag, uidState, flags, startTime,
-                    attributionFlags, attributionChainId, DiscreteRegistry.ACCESS_TYPE_START_OP, 1);
+                    attributionFlags, attributionChainId,
+                    DiscreteOpsRegistry.ACCESS_TYPE_START_OP, 1);
         }
     }
 
@@ -344,8 +345,8 @@
                     parent.packageName, persistentDeviceId, tag, event.getUidState(),
                     event.getFlags(), finishedEvent.getNoteTime(), finishedEvent.getDuration(),
                     event.getAttributionFlags(), event.getAttributionChainId(),
-                    isPausing ? DiscreteRegistry.ACCESS_TYPE_PAUSE_OP
-                            : DiscreteRegistry.ACCESS_TYPE_FINISH_OP);
+                    isPausing ? DiscreteOpsRegistry.ACCESS_TYPE_PAUSE_OP
+                            : DiscreteOpsRegistry.ACCESS_TYPE_FINISH_OP);
 
             if (!isPausing) {
                 mAppOpsService.mInProgressStartOpEventPool.release(event);
@@ -453,7 +454,7 @@
             mAppOpsService.mHistoricalRegistry.incrementOpAccessedCount(parent.op, parent.uid,
                     parent.packageName, persistentDeviceId, tag, event.getUidState(),
                     event.getFlags(), startTime, event.getAttributionFlags(),
-                    event.getAttributionChainId(), DiscreteRegistry.ACCESS_TYPE_RESUME_OP, 1);
+                    event.getAttributionChainId(), DiscreteOpsRegistry.ACCESS_TYPE_RESUME_OP, 1);
             if (shouldSendActive) {
                 mAppOpsService.scheduleOpActiveChangedIfNeededLocked(parent.op, parent.uid,
                         parent.packageName, tag, event.getVirtualDeviceId(), true,
diff --git a/services/core/java/com/android/server/appop/DiscreteOpsDbHelper.java b/services/core/java/com/android/server/appop/DiscreteOpsDbHelper.java
new file mode 100644
index 0000000..e4c36cc
--- /dev/null
+++ b/services/core/java/com/android/server/appop/DiscreteOpsDbHelper.java
@@ -0,0 +1,387 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.appop;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.database.DatabaseErrorHandler;
+import android.database.DefaultDatabaseErrorHandler;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteException;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.database.sqlite.SQLiteRawStatement;
+import android.os.Environment;
+import android.util.IntArray;
+import android.util.Slog;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+class DiscreteOpsDbHelper extends SQLiteOpenHelper {
+    private static final String LOG_TAG = "DiscreteOpsDbHelper";
+    static final String DATABASE_NAME = "app_op_history.db";
+    private static final int DATABASE_VERSION = 1;
+    private static final boolean DEBUG = false;
+
+    DiscreteOpsDbHelper(@NonNull Context context, @NonNull File databaseFile) {
+        super(context, databaseFile.getAbsolutePath(), null, DATABASE_VERSION,
+                new DiscreteOpsDatabaseErrorHandler());
+        setOpenParams(getDatabaseOpenParams());
+    }
+
+    private static SQLiteDatabase.OpenParams getDatabaseOpenParams() {
+        return new SQLiteDatabase.OpenParams.Builder()
+                .addOpenFlags(SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING)
+                .build();
+    }
+
+    @NonNull
+    static File getDatabaseFile() {
+        return new File(new File(Environment.getDataSystemDirectory(), "appops"), DATABASE_NAME);
+    }
+
+    @Override
+    public void onConfigure(SQLiteDatabase db) {
+        db.execSQL("PRAGMA synchronous = NORMAL");
+    }
+
+    @Override
+    public void onCreate(SQLiteDatabase db) {
+        db.execSQL(DiscreteOpsTable.CREATE_TABLE_SQL);
+        db.execSQL(DiscreteOpsTable.CREATE_INDEX_SQL);
+    }
+
+    @Override
+    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+    }
+
+    void insertDiscreteOps(@NonNull List<DiscreteOpsSqlRegistry.DiscreteOp> opEvents) {
+        if (opEvents.isEmpty()) {
+            return;
+        }
+
+        SQLiteDatabase db = getWritableDatabase();
+        // TODO (b/383157289) what if database is busy and can't start a transaction? will read
+        //  more about it and can be done in a follow up cl.
+        db.beginTransaction();
+        try (SQLiteRawStatement statement = db.createRawStatement(
+                DiscreteOpsTable.INSERT_TABLE_SQL)) {
+            for (DiscreteOpsSqlRegistry.DiscreteOp event : opEvents) {
+                try {
+                    statement.bindInt(DiscreteOpsTable.UID_INDEX, event.getUid());
+                    bindTextOrNull(statement, DiscreteOpsTable.PACKAGE_NAME_INDEX,
+                            event.getPackageName());
+                    bindTextOrNull(statement, DiscreteOpsTable.DEVICE_ID_INDEX,
+                            event.getDeviceId());
+                    statement.bindInt(DiscreteOpsTable.OP_CODE_INDEX, event.getOpCode());
+                    bindTextOrNull(statement, DiscreteOpsTable.ATTRIBUTION_TAG_INDEX,
+                            event.getAttributionTag());
+                    statement.bindLong(DiscreteOpsTable.ACCESS_TIME_INDEX, event.getAccessTime());
+                    statement.bindLong(
+                            DiscreteOpsTable.ACCESS_DURATION_INDEX, event.getDuration());
+                    statement.bindInt(DiscreteOpsTable.UID_STATE_INDEX, event.getUidState());
+                    statement.bindInt(DiscreteOpsTable.OP_FLAGS_INDEX, event.getOpFlags());
+                    statement.bindInt(DiscreteOpsTable.ATTRIBUTION_FLAGS_INDEX,
+                            event.getAttributionFlags());
+                    statement.bindLong(DiscreteOpsTable.CHAIN_ID_INDEX, event.getChainId());
+                    statement.step();
+                } catch (Exception exception) {
+                    Slog.e(LOG_TAG, "Error inserting the discrete op: " + event, exception);
+                } finally {
+                    statement.reset();
+                }
+            }
+            db.setTransactionSuccessful();
+        } finally {
+            db.endTransaction();
+        }
+    }
+
+    private void bindTextOrNull(SQLiteRawStatement statement, int index, @Nullable String text) {
+        if (text == null) {
+            statement.bindNull(index);
+        } else {
+            statement.bindText(index, text);
+        }
+    }
+
+    /**
+     * This will be used as an offset for inserting new chain id in discrete ops table.
+     */
+    long getLargestAttributionChainId() {
+        long chainId = 0;
+        try {
+            SQLiteDatabase db = getReadableDatabase();
+            db.beginTransactionReadOnly();
+            try (SQLiteRawStatement statement =
+                     db.createRawStatement(DiscreteOpsTable.SELECT_MAX_ATTRIBUTION_CHAIN_ID)) {
+                if (statement.step()) {
+                    chainId = statement.getColumnLong(0);
+                    if (chainId < 0) {
+                        chainId = 0;
+                    }
+                }
+                db.setTransactionSuccessful();
+            } finally {
+                db.endTransaction();
+            }
+        } catch (SQLiteException exception) {
+            Slog.e(LOG_TAG, "Error reading attribution chain id", exception);
+        }
+        return chainId;
+    }
+
+    void execSQL(@NonNull String sql) {
+        execSQL(sql, null);
+    }
+
+    void execSQL(@NonNull String sql, Object[] bindArgs) {
+        if (DEBUG) {
+            Slog.i(LOG_TAG, "DB execSQL, sql: " + sql);
+        }
+        SQLiteDatabase db = getWritableDatabase();
+        if (bindArgs == null) {
+            db.execSQL(sql);
+        } else {
+            db.execSQL(sql, bindArgs);
+        }
+    }
+
+    /**
+     * Returns a list of {@link DiscreteOpsSqlRegistry.DiscreteOp} based on the given filters.
+     */
+    List<DiscreteOpsSqlRegistry.DiscreteOp> getDiscreteOps(
+            @AppOpsManager.HistoricalOpsRequestFilter int requestFilters,
+            int uidFilter, @Nullable String packageNameFilter,
+            @Nullable String attributionTagFilter, IntArray opCodesFilter, int opFlagsFilter,
+            long beginTime, long endTime, int limit, String orderByColumn) {
+        List<SQLCondition> conditions = prepareConditions(beginTime, endTime, requestFilters,
+                uidFilter, packageNameFilter,
+                attributionTagFilter, opCodesFilter, opFlagsFilter);
+        String sql = buildSql(conditions, orderByColumn, limit);
+
+        SQLiteDatabase db = getReadableDatabase();
+        List<DiscreteOpsSqlRegistry.DiscreteOp> results = new ArrayList<>();
+        db.beginTransactionReadOnly();
+        try (SQLiteRawStatement statement = db.createRawStatement(sql)) {
+            int size = conditions.size();
+            for (int i = 0; i < size; i++) {
+                SQLCondition condition = conditions.get(i);
+                if (DEBUG) {
+                    Slog.i(LOG_TAG, condition + ", binding value = " + condition.mFilterValue);
+                }
+                switch (condition.mColumnFilter) {
+                    case PACKAGE_NAME, ATTR_TAG -> statement.bindText(i + 1,
+                            condition.mFilterValue.toString());
+                    case UID, OP_CODE_EQUAL, OP_FLAGS -> statement.bindInt(i + 1,
+                            Integer.parseInt(condition.mFilterValue.toString()));
+                    case BEGIN_TIME, END_TIME -> statement.bindLong(i + 1,
+                            Long.parseLong(condition.mFilterValue.toString()));
+                    case OP_CODE_IN -> Slog.d(LOG_TAG, "No binding for In operator");
+                    default -> Slog.w(LOG_TAG, "unknown sql condition " + condition);
+                }
+            }
+
+            while (statement.step()) {
+                int uid = statement.getColumnInt(0);
+                String packageName = statement.getColumnText(1);
+                String deviceId = statement.getColumnText(2);
+                int opCode = statement.getColumnInt(3);
+                String attributionTag = statement.getColumnText(4);
+                long accessTime = statement.getColumnLong(5);
+                long duration = statement.getColumnLong(6);
+                int uidState = statement.getColumnInt(7);
+                int opFlags = statement.getColumnInt(8);
+                int attributionFlags = statement.getColumnInt(9);
+                long chainId = statement.getColumnLong(10);
+                DiscreteOpsSqlRegistry.DiscreteOp event = new DiscreteOpsSqlRegistry.DiscreteOp(uid,
+                        packageName, attributionTag, deviceId, opCode,
+                        opFlags, attributionFlags, uidState, chainId, accessTime, duration);
+                results.add(event);
+            }
+            db.setTransactionSuccessful();
+        } finally {
+            db.endTransaction();
+        }
+        return results;
+    }
+
+    private String buildSql(List<SQLCondition> conditions, String orderByColumn, int limit) {
+        StringBuilder sql = new StringBuilder(DiscreteOpsTable.SELECT_TABLE_DATA);
+        if (!conditions.isEmpty()) {
+            sql.append(" WHERE ");
+            int size = conditions.size();
+            for (int i = 0; i < size; i++) {
+                sql.append(conditions.get(i).toString());
+                if (i < size - 1) {
+                    sql.append(" AND ");
+                }
+            }
+        }
+
+        if (orderByColumn != null) {
+            sql.append(" ORDER BY ").append(orderByColumn);
+        }
+        if (limit > 0) {
+            sql.append(" LIMIT ").append(limit);
+        }
+        if (DEBUG) {
+            Slog.i(LOG_TAG, "Sql query " + sql);
+        }
+        return sql.toString();
+    }
+
+    /**
+     * Creates where conditions for package, uid, attribution tag and app op codes,
+     * app op codes condition does not support argument binding.
+     */
+    private List<SQLCondition> prepareConditions(long beginTime, long endTime, int requestFilters,
+            int uid, @Nullable String packageName, @Nullable String attributionTag,
+            IntArray opCodes, int opFlags) {
+        final List<SQLCondition> conditions = new ArrayList<>();
+
+        if (beginTime != -1) {
+            conditions.add(new SQLCondition(ColumnFilter.BEGIN_TIME, beginTime));
+        }
+        if (endTime != -1) {
+            conditions.add(new SQLCondition(ColumnFilter.END_TIME, endTime));
+        }
+        if (opFlags != 0) {
+            conditions.add(new SQLCondition(ColumnFilter.OP_FLAGS, opFlags));
+        }
+
+        if (requestFilters != 0) {
+            if ((requestFilters & AppOpsManager.FILTER_BY_PACKAGE_NAME) != 0) {
+                conditions.add(new SQLCondition(ColumnFilter.PACKAGE_NAME, packageName));
+            }
+            if ((requestFilters & AppOpsManager.FILTER_BY_UID) != 0) {
+                conditions.add(new SQLCondition(ColumnFilter.UID, uid));
+
+            }
+            if ((requestFilters & AppOpsManager.FILTER_BY_ATTRIBUTION_TAG) != 0) {
+                conditions.add(new SQLCondition(ColumnFilter.ATTR_TAG, attributionTag));
+            }
+            // filter op codes
+            if (opCodes != null && opCodes.size() == 1) {
+                conditions.add(new SQLCondition(ColumnFilter.OP_CODE_EQUAL, opCodes.get(0)));
+            } else if (opCodes != null && opCodes.size() > 1) {
+                StringBuilder b = new StringBuilder();
+                int size = opCodes.size();
+                for (int i = 0; i < size; i++) {
+                    b.append(opCodes.get(i));
+                    if (i < size - 1) {
+                        b.append(", ");
+                    }
+                }
+                conditions.add(new SQLCondition(ColumnFilter.OP_CODE_IN, b.toString()));
+            }
+        }
+        return conditions;
+    }
+
+    /**
+     * This class prepares a where clause condition for discrete ops table column.
+     */
+    static final class SQLCondition {
+        private final ColumnFilter mColumnFilter;
+        private final Object mFilterValue;
+
+        SQLCondition(ColumnFilter columnFilter, Object filterValue) {
+            mColumnFilter = columnFilter;
+            mFilterValue = filterValue;
+        }
+
+        @Override
+        public String toString() {
+            if (mColumnFilter == ColumnFilter.OP_CODE_IN) {
+                return mColumnFilter + " ( " + mFilterValue + " )";
+            }
+            return mColumnFilter.toString();
+        }
+    }
+
+    /**
+     * This enum describes the where clause conditions for different columns in discrete ops
+     * table.
+     */
+    private enum ColumnFilter {
+        PACKAGE_NAME(DiscreteOpsTable.Columns.PACKAGE_NAME + " = ? "),
+        UID(DiscreteOpsTable.Columns.UID + " = ? "),
+        ATTR_TAG(DiscreteOpsTable.Columns.ATTRIBUTION_TAG + " = ? "),
+        END_TIME(DiscreteOpsTable.Columns.ACCESS_TIME + " < ? "),
+        OP_CODE_EQUAL(DiscreteOpsTable.Columns.OP_CODE + " = ? "),
+        BEGIN_TIME(DiscreteOpsTable.Columns.ACCESS_TIME + " + "
+                + DiscreteOpsTable.Columns.ACCESS_DURATION + " > ? "),
+        OP_FLAGS("(" + DiscreteOpsTable.Columns.OP_FLAGS + " & ? ) != 0"),
+        OP_CODE_IN(DiscreteOpsTable.Columns.OP_CODE + " IN ");
+
+        final String mCondition;
+
+        ColumnFilter(String condition) {
+            mCondition = condition;
+        }
+
+        @Override
+        public String toString() {
+            return mCondition;
+        }
+    }
+
+    static final class DiscreteOpsDatabaseErrorHandler implements DatabaseErrorHandler {
+        private final DefaultDatabaseErrorHandler mDefaultDatabaseErrorHandler =
+                new DefaultDatabaseErrorHandler();
+
+        @Override
+        public void onCorruption(SQLiteDatabase dbObj) {
+            Slog.e(LOG_TAG, "discrete ops database got corrupted.");
+            mDefaultDatabaseErrorHandler.onCorruption(dbObj);
+        }
+    }
+
+    // USED for testing only
+    List<DiscreteOpsSqlRegistry.DiscreteOp> getAllDiscreteOps(@NonNull String sql) {
+        SQLiteDatabase db = getReadableDatabase();
+        List<DiscreteOpsSqlRegistry.DiscreteOp> results = new ArrayList<>();
+        db.beginTransactionReadOnly();
+        try (SQLiteRawStatement statement = db.createRawStatement(sql)) {
+            while (statement.step()) {
+                int uid = statement.getColumnInt(0);
+                String packageName = statement.getColumnText(1);
+                String deviceId = statement.getColumnText(2);
+                int opCode = statement.getColumnInt(3);
+                String attributionTag = statement.getColumnText(4);
+                long accessTime = statement.getColumnLong(5);
+                long duration = statement.getColumnLong(6);
+                int uidState = statement.getColumnInt(7);
+                int opFlags = statement.getColumnInt(8);
+                int attributionFlags = statement.getColumnInt(9);
+                long chainId = statement.getColumnLong(10);
+                DiscreteOpsSqlRegistry.DiscreteOp event = new DiscreteOpsSqlRegistry.DiscreteOp(uid,
+                        packageName, attributionTag, deviceId, opCode,
+                        opFlags, attributionFlags, uidState, chainId, accessTime, duration);
+                results.add(event);
+            }
+            db.setTransactionSuccessful();
+        } finally {
+            db.endTransaction();
+        }
+        return results;
+    }
+}
diff --git a/services/core/java/com/android/server/appop/DiscreteOpsMigrationHelper.java b/services/core/java/com/android/server/appop/DiscreteOpsMigrationHelper.java
new file mode 100644
index 0000000..c38ee55
--- /dev/null
+++ b/services/core/java/com/android/server/appop/DiscreteOpsMigrationHelper.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 com.android.server.appop;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Helper class for migrating discrete ops from xml to sqlite
+ */
+public class DiscreteOpsMigrationHelper {
+    /**
+     * migrate discrete ops from xml to sqlite.
+     */
+    static void migrateDiscreteOpsToSqlite(DiscreteOpsXmlRegistry xmlRegistry,
+            DiscreteOpsSqlRegistry sqlRegistry) {
+        DiscreteOpsXmlRegistry.DiscreteOps xmlOps = xmlRegistry.getAllDiscreteOps();
+        List<DiscreteOpsSqlRegistry.DiscreteOp> discreteOps = getSqlDiscreteOps(xmlOps);
+        sqlRegistry.migrateXmlData(discreteOps, xmlOps.mChainIdOffset);
+        xmlRegistry.deleteDiscreteOpsDir();
+    }
+
+    /**
+     * rollback discrete ops from sqlite to xml.
+     */
+    static void migrateDiscreteOpsToXml(DiscreteOpsSqlRegistry sqlRegistry,
+            DiscreteOpsXmlRegistry xmlRegistry) {
+        List<DiscreteOpsSqlRegistry.DiscreteOp> sqlOps = sqlRegistry.getAllDiscreteOps();
+        DiscreteOpsXmlRegistry.DiscreteOps xmlOps = getXmlDiscreteOps(sqlOps);
+        xmlRegistry.migrateSqliteData(xmlOps);
+        sqlRegistry.deleteDatabase();
+    }
+
+    /**
+     * Convert sqlite flat rows to hierarchical data.
+     */
+    private static DiscreteOpsXmlRegistry.DiscreteOps getXmlDiscreteOps(
+            List<DiscreteOpsSqlRegistry.DiscreteOp> discreteOps) {
+        DiscreteOpsXmlRegistry.DiscreteOps xmlOps =
+                new DiscreteOpsXmlRegistry.DiscreteOps(0);
+        if (discreteOps.isEmpty()) {
+            return xmlOps;
+        }
+
+        for (DiscreteOpsSqlRegistry.DiscreteOp discreteOp : discreteOps) {
+            xmlOps.addDiscreteAccess(discreteOp.getOpCode(), discreteOp.getUid(),
+                    discreteOp.getPackageName(), discreteOp.getDeviceId(),
+                    discreteOp.getAttributionTag(), discreteOp.getOpFlags(),
+                    discreteOp.getUidState(),
+                    discreteOp.getAccessTime(), discreteOp.getDuration(),
+                    discreteOp.getAttributionFlags(), (int) discreteOp.getChainId());
+        }
+        return xmlOps;
+    }
+
+    /**
+     * Convert xml (hierarchical) data to flat row based data.
+     */
+    private static List<DiscreteOpsSqlRegistry.DiscreteOp> getSqlDiscreteOps(
+            DiscreteOpsXmlRegistry.DiscreteOps discreteOps) {
+        List<DiscreteOpsSqlRegistry.DiscreteOp> opEvents = new ArrayList<>();
+
+        if (discreteOps.isEmpty()) {
+            return opEvents;
+        }
+
+        discreteOps.mUids.forEach((uid, discreteUidOps) -> {
+            discreteUidOps.mPackages.forEach((packageName, packageOps) -> {
+                packageOps.mPackageOps.forEach((opcode, ops) -> {
+                    ops.mDeviceAttributedOps.forEach((deviceId, deviceOps) -> {
+                        deviceOps.mAttributedOps.forEach((tag, attributedOps) -> {
+                            for (DiscreteOpsXmlRegistry.DiscreteOpEvent attributedOp :
+                                    attributedOps) {
+                                DiscreteOpsSqlRegistry.DiscreteOp
+                                        opModel = new DiscreteOpsSqlRegistry.DiscreteOp(uid,
+                                        packageName, tag,
+                                        deviceId, opcode, attributedOp.mOpFlag,
+                                        attributedOp.mAttributionFlags,
+                                        attributedOp.mUidState, attributedOp.mAttributionChainId,
+                                        attributedOp.mNoteTime,
+                                        attributedOp.mNoteDuration);
+                                opEvents.add(opModel);
+                            }
+                        });
+                    });
+                });
+            });
+        });
+
+        return opEvents;
+    }
+}
diff --git a/services/core/java/com/android/server/appop/DiscreteOpsRegistry.java b/services/core/java/com/android/server/appop/DiscreteOpsRegistry.java
new file mode 100644
index 0000000..88b3f6d
--- /dev/null
+++ b/services/core/java/com/android/server/appop/DiscreteOpsRegistry.java
@@ -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.server.appop;
+
+import static android.app.AppOpsManager.OP_CAMERA;
+import static android.app.AppOpsManager.OP_COARSE_LOCATION;
+import static android.app.AppOpsManager.OP_EMERGENCY_LOCATION;
+import static android.app.AppOpsManager.OP_FINE_LOCATION;
+import static android.app.AppOpsManager.OP_FLAG_SELF;
+import static android.app.AppOpsManager.OP_FLAG_TRUSTED_PROXIED;
+import static android.app.AppOpsManager.OP_FLAG_TRUSTED_PROXY;
+import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
+import static android.app.AppOpsManager.OP_MONITOR_LOCATION;
+import static android.app.AppOpsManager.OP_PHONE_CALL_CAMERA;
+import static android.app.AppOpsManager.OP_PHONE_CALL_MICROPHONE;
+import static android.app.AppOpsManager.OP_PROCESS_OUTGOING_CALLS;
+import static android.app.AppOpsManager.OP_READ_ICC_SMS;
+import static android.app.AppOpsManager.OP_READ_SMS;
+import static android.app.AppOpsManager.OP_RECEIVE_AMBIENT_TRIGGER_AUDIO;
+import static android.app.AppOpsManager.OP_RECEIVE_SANDBOX_TRIGGER_AUDIO;
+import static android.app.AppOpsManager.OP_RECORD_AUDIO;
+import static android.app.AppOpsManager.OP_RESERVED_FOR_TESTING;
+import static android.app.AppOpsManager.OP_SEND_SMS;
+import static android.app.AppOpsManager.OP_SMS_FINANCIAL_TRANSACTIONS;
+import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
+import static android.app.AppOpsManager.OP_WRITE_ICC_SMS;
+import static android.app.AppOpsManager.OP_WRITE_SMS;
+
+import static java.lang.Long.min;
+import static java.lang.Math.max;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AppOpsManager;
+import android.os.AsyncTask;
+import android.os.Build;
+import android.permission.flags.Flags;
+import android.provider.DeviceConfig;
+import android.util.Slog;
+
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.FrameworkStatsLog;
+
+import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.text.SimpleDateFormat;
+import java.time.Duration;
+import java.util.Date;
+import java.util.Set;
+
+/**
+ * This class provides interface for xml and sqlite implementation. Implementation manages
+ * information about recent accesses to ops for permission usage timeline.
+ * <p>
+ * The discrete history is kept for limited time (initial default is 24 hours, set in
+ * {@link DiscreteOpsRegistry#sDiscreteHistoryCutoff} and discarded after that.
+ * <p>
+ * Discrete history is quantized to reduce resources footprint. By default, quantization is set to
+ * one minute in {@link DiscreteOpsRegistry#sDiscreteHistoryQuantization}. All access times are
+ * aligned to the closest quantized time. All durations (except -1, meaning no duration) are
+ * rounded up to the closest quantized interval.
+ * <p>
+ * When data is queried through API, events are deduplicated and for every time quant there can
+ * be only one {@link AppOpsManager.AttributedOpEntry}. Each entry contains information about
+ * different accesses which happened in specified time quant - across dimensions of
+ * {@link AppOpsManager.UidState} and {@link AppOpsManager.OpFlags}. For each dimension
+ * it is only possible to know if at least one access happened in the time quant.
+ * <p>
+ * INITIALIZATION: We can initialize persistence only after the system is ready
+ * as we need to check the optional configuration override from the settings
+ * database which is not initialized at the time the app ops service is created. This class
+ * relies on {@link HistoricalRegistry} for controlling that no calls are allowed until then. All
+ * outside calls are going through {@link HistoricalRegistry}.
+ *
+ */
+abstract class DiscreteOpsRegistry {
+    private static final String TAG = DiscreteOpsRegistry.class.getSimpleName();
+
+    static final boolean DEBUG_LOG = false;
+    static final String PROPERTY_DISCRETE_HISTORY_CUTOFF = "discrete_history_cutoff_millis";
+    static final String PROPERTY_DISCRETE_HISTORY_QUANTIZATION =
+            "discrete_history_quantization_millis";
+    static final String PROPERTY_DISCRETE_FLAGS = "discrete_history_op_flags";
+    static final String PROPERTY_DISCRETE_OPS_LIST = "discrete_history_ops_cslist";
+    static final String DEFAULT_DISCRETE_OPS = OP_FINE_LOCATION + "," + OP_COARSE_LOCATION
+            + "," + OP_EMERGENCY_LOCATION + "," + OP_CAMERA + "," + OP_RECORD_AUDIO + ","
+            + OP_PHONE_CALL_MICROPHONE + "," + OP_PHONE_CALL_CAMERA + ","
+            + OP_RECEIVE_AMBIENT_TRIGGER_AUDIO + "," + OP_RECEIVE_SANDBOX_TRIGGER_AUDIO
+            + "," + OP_RESERVED_FOR_TESTING;
+    static final int[] sDiscreteOpsToLog =
+            new int[]{OP_FINE_LOCATION, OP_COARSE_LOCATION, OP_EMERGENCY_LOCATION, OP_CAMERA,
+                    OP_RECORD_AUDIO, OP_PHONE_CALL_MICROPHONE, OP_PHONE_CALL_CAMERA,
+                    OP_RECEIVE_AMBIENT_TRIGGER_AUDIO, OP_RECEIVE_SANDBOX_TRIGGER_AUDIO, OP_READ_SMS,
+                    OP_WRITE_SMS, OP_SEND_SMS, OP_READ_ICC_SMS, OP_WRITE_ICC_SMS,
+                    OP_SMS_FINANCIAL_TRANSACTIONS, OP_SYSTEM_ALERT_WINDOW, OP_MONITOR_LOCATION,
+                    OP_MONITOR_HIGH_POWER_LOCATION, OP_PROCESS_OUTGOING_CALLS,
+            };
+
+    static final long DEFAULT_DISCRETE_HISTORY_CUTOFF = Duration.ofDays(7).toMillis();
+    static final long MAXIMUM_DISCRETE_HISTORY_CUTOFF = Duration.ofDays(30).toMillis();
+    // The duration for which the data is kept, default is 7 days and max 30 days enforced.
+    static long sDiscreteHistoryCutoff;
+
+    static final long DEFAULT_DISCRETE_HISTORY_QUANTIZATION = Duration.ofMinutes(1).toMillis();
+    // discrete ops are rounded up to quantization time, meaning we record one op per time bucket
+    // in case of duplicate op events.
+    static long sDiscreteHistoryQuantization;
+
+    static int[] sDiscreteOps;
+    static int sDiscreteFlags;
+
+    static final int OP_FLAGS_DISCRETE = OP_FLAG_SELF | OP_FLAG_TRUSTED_PROXIED
+            | OP_FLAG_TRUSTED_PROXY;
+
+    boolean mDebugMode = false;
+
+    static final int ACCESS_TYPE_NOTE_OP =
+            FrameworkStatsLog.APP_OP_ACCESS_TRACKED__ACCESS_TYPE__NOTE_OP;
+    static final int ACCESS_TYPE_START_OP =
+            FrameworkStatsLog.APP_OP_ACCESS_TRACKED__ACCESS_TYPE__START_OP;
+    static final int ACCESS_TYPE_FINISH_OP =
+            FrameworkStatsLog.APP_OP_ACCESS_TRACKED__ACCESS_TYPE__FINISH_OP;
+    static final int ACCESS_TYPE_PAUSE_OP =
+            FrameworkStatsLog.APP_OP_ACCESS_TRACKED__ACCESS_TYPE__PAUSE_OP;
+    static final int ACCESS_TYPE_RESUME_OP =
+            FrameworkStatsLog.APP_OP_ACCESS_TRACKED__ACCESS_TYPE__RESUME_OP;
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"ACCESS_TYPE_"}, value = {
+            ACCESS_TYPE_NOTE_OP,
+            ACCESS_TYPE_START_OP,
+            ACCESS_TYPE_FINISH_OP,
+            ACCESS_TYPE_PAUSE_OP,
+            ACCESS_TYPE_RESUME_OP
+    })
+    @interface AccessType {}
+
+    void systemReady() {
+        DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_PRIVACY,
+                AsyncTask.THREAD_POOL_EXECUTOR, (DeviceConfig.Properties p) -> {
+                    setDiscreteHistoryParameters(p);
+                });
+        setDiscreteHistoryParameters(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_PRIVACY));
+    }
+
+    abstract void recordDiscreteAccess(int uid, String packageName, @NonNull String deviceId,
+            int op, @Nullable String attributionTag, @AppOpsManager.OpFlags int flags,
+            @AppOpsManager.UidState int uidState, long accessTime, long accessDuration,
+            @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId,
+            @DiscreteOpsRegistry.AccessType int accessType);
+
+    /**
+     * A periodic callback from {@link AppOpsService} to flush the in memory events to disk.
+     * The shutdown callback is also plugged into it.
+     * <p>
+     * This method flushes in memory records to disk, and also clears old records from disk.
+     */
+    abstract void writeAndClearOldAccessHistory();
+
+    /** Remove all discrete op events. */
+    abstract void clearHistory();
+
+    /** Remove all discrete op events for given UID and package. */
+    abstract void clearHistory(int uid, String packageName);
+
+    /**
+     * Offset access time by given timestamp, new access time would be accessTime - offsetMillis.
+     */
+    abstract void offsetHistory(long offset);
+
+    abstract  void addFilteredDiscreteOpsToHistoricalOps(AppOpsManager.HistoricalOps result,
+            long beginTimeMillis, long endTimeMillis,
+            @AppOpsManager.HistoricalOpsRequestFilter int filter, int uidFilter,
+            @Nullable String packageNameFilter, @Nullable String[] opNamesFilter,
+            @Nullable String attributionTagFilter, @AppOpsManager.OpFlags int flagsFilter,
+            Set<String> attributionExemptPkgs);
+
+    abstract void dump(@NonNull PrintWriter pw, int uidFilter, @Nullable String packageNameFilter,
+            @Nullable String attributionTagFilter,
+            @AppOpsManager.HistoricalOpsRequestFilter int filter, int dumpOp,
+            @NonNull SimpleDateFormat sdf, @NonNull Date date, @NonNull String prefix,
+            int nDiscreteOps);
+
+    void setDebugMode(boolean debugMode) {
+        this.mDebugMode = debugMode;
+    }
+
+    static long discretizeTimeStamp(long timeStamp) {
+        return timeStamp / sDiscreteHistoryQuantization * sDiscreteHistoryQuantization;
+
+    }
+
+    static long discretizeDuration(long duration) {
+        return duration == -1 ? -1 : (duration + sDiscreteHistoryQuantization - 1)
+                / sDiscreteHistoryQuantization * sDiscreteHistoryQuantization;
+    }
+
+    static boolean isDiscreteOp(int op, @AppOpsManager.OpFlags int flags) {
+        if (!ArrayUtils.contains(sDiscreteOps, op)) {
+            return false;
+        }
+        if ((flags & (sDiscreteFlags)) == 0) {
+            return false;
+        }
+        return true;
+    }
+
+    // could this be impl detail of discrete registry, just one test is using the method
+    // abstract DiscreteRegistry.DiscreteOps getAllDiscreteOps();
+
+    private void setDiscreteHistoryParameters(DeviceConfig.Properties p) {
+        if (p.getKeyset().contains(PROPERTY_DISCRETE_HISTORY_CUTOFF)) {
+            sDiscreteHistoryCutoff = p.getLong(PROPERTY_DISCRETE_HISTORY_CUTOFF,
+                    DEFAULT_DISCRETE_HISTORY_CUTOFF);
+            if (!Build.IS_DEBUGGABLE && !mDebugMode) {
+                sDiscreteHistoryCutoff = min(MAXIMUM_DISCRETE_HISTORY_CUTOFF,
+                        sDiscreteHistoryCutoff);
+            }
+        } else {
+            sDiscreteHistoryCutoff = DEFAULT_DISCRETE_HISTORY_CUTOFF;
+        }
+        if (p.getKeyset().contains(PROPERTY_DISCRETE_HISTORY_QUANTIZATION)) {
+            sDiscreteHistoryQuantization = p.getLong(PROPERTY_DISCRETE_HISTORY_QUANTIZATION,
+                    DEFAULT_DISCRETE_HISTORY_QUANTIZATION);
+            if (!Build.IS_DEBUGGABLE && !mDebugMode) {
+                sDiscreteHistoryQuantization = max(DEFAULT_DISCRETE_HISTORY_QUANTIZATION,
+                        sDiscreteHistoryQuantization);
+            }
+        } else {
+            sDiscreteHistoryQuantization = DEFAULT_DISCRETE_HISTORY_QUANTIZATION;
+        }
+        sDiscreteFlags = p.getKeyset().contains(PROPERTY_DISCRETE_FLAGS) ? sDiscreteFlags =
+                p.getInt(PROPERTY_DISCRETE_FLAGS, OP_FLAGS_DISCRETE) : OP_FLAGS_DISCRETE;
+        sDiscreteOps = p.getKeyset().contains(PROPERTY_DISCRETE_OPS_LIST) ? parseOpsList(
+                p.getString(PROPERTY_DISCRETE_OPS_LIST, DEFAULT_DISCRETE_OPS)) : parseOpsList(
+                DEFAULT_DISCRETE_OPS);
+    }
+
+    private static int[] parseOpsList(String opsList) {
+        String[] strArr;
+        if (opsList.isEmpty()) {
+            strArr = new String[0];
+        } else {
+            strArr = opsList.split(",");
+        }
+        int nOps = strArr.length;
+        int[] result = new int[nOps];
+        try {
+            for (int i = 0; i < nOps; i++) {
+                result[i] = Integer.parseInt(strArr[i]);
+            }
+        } catch (NumberFormatException e) {
+            Slog.e(TAG, "Failed to parse Discrete ops list: " + e.getMessage());
+            return parseOpsList(DEFAULT_DISCRETE_OPS);
+        }
+        return result;
+    }
+
+    /**
+     * Whether app op access tacking is enabled and a metric event should be logged.
+     */
+    static boolean shouldLogAccess(int op) {
+        return Flags.appopAccessTrackingLoggingEnabled()
+                && ArrayUtils.contains(sDiscreteOpsToLog, op);
+    }
+
+    String getAttributionTag(String attributionTag, String packageName) {
+        if (attributionTag == null || packageName == null) {
+            return attributionTag;
+        }
+        int firstChar = 0;
+        if (attributionTag.startsWith(packageName)) {
+            firstChar = packageName.length();
+            if (firstChar < attributionTag.length() && attributionTag.charAt(firstChar)
+                    == '.') {
+                firstChar++;
+            }
+        }
+        return attributionTag.substring(firstChar);
+    }
+
+}
diff --git a/services/core/java/com/android/server/appop/DiscreteOpsSqlRegistry.java b/services/core/java/com/android/server/appop/DiscreteOpsSqlRegistry.java
new file mode 100644
index 0000000..4b3981c
--- /dev/null
+++ b/services/core/java/com/android/server/appop/DiscreteOpsSqlRegistry.java
@@ -0,0 +1,689 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.appop;
+
+import static android.app.AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE;
+import static android.app.AppOpsManager.ATTRIBUTION_FLAG_ACCESSOR;
+import static android.app.AppOpsManager.ATTRIBUTION_FLAG_RECEIVER;
+import static android.app.AppOpsManager.ATTRIBUTION_FLAG_TRUSTED;
+import static android.app.AppOpsManager.flagsToString;
+import static android.app.AppOpsManager.getUidStateName;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Process;
+import android.util.ArraySet;
+import android.util.IntArray;
+import android.util.LongSparseArray;
+import android.util.Slog;
+
+import com.android.internal.util.FrameworkStatsLog;
+import com.android.server.ServiceThread;
+
+import java.io.File;
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * This class handles sqlite persistence layer for discrete ops.
+ */
+public class DiscreteOpsSqlRegistry extends DiscreteOpsRegistry {
+    private static final String TAG = "DiscreteOpsSqlRegistry";
+
+    private final Context mContext;
+    private final DiscreteOpsDbHelper mDiscreteOpsDbHelper;
+    private final SqliteWriteHandler mSqliteWriteHandler;
+    private final DiscreteOpCache mDiscreteOpCache = new DiscreteOpCache(512);
+    private static final long THREE_HOURS = Duration.ofHours(3).toMillis();
+    private static final int WRITE_CACHE_EVICTED_OP_EVENTS = 1;
+    private static final int DELETE_OLD_OP_EVENTS = 2;
+    // Attribution chain id is used to identify an attribution source chain, This is
+    // set for startOp only. PermissionManagerService resets this ID on device restart, so
+    // we use previously persisted chain id as offset, and add it to chain id received from
+    // permission manager service.
+    private long mChainIdOffset;
+    private final File mDatabaseFile;
+
+    DiscreteOpsSqlRegistry(Context context) {
+        this(context, DiscreteOpsDbHelper.getDatabaseFile());
+    }
+
+    DiscreteOpsSqlRegistry(Context context, File databaseFile) {
+        ServiceThread thread = new ServiceThread(TAG, Process.THREAD_PRIORITY_BACKGROUND, true);
+        thread.start();
+        mContext = context;
+        mDatabaseFile = databaseFile;
+        mSqliteWriteHandler = new SqliteWriteHandler(thread.getLooper());
+        mDiscreteOpsDbHelper = new DiscreteOpsDbHelper(context, databaseFile);
+        mChainIdOffset = mDiscreteOpsDbHelper.getLargestAttributionChainId();
+    }
+
+    @Override
+    void recordDiscreteAccess(int uid, String packageName,
+            @NonNull String deviceId, int op,
+            @Nullable String attributionTag, int flags, int uidState,
+            long accessTime, long accessDuration, int attributionFlags, int attributionChainId,
+            int accessType) {
+        if (shouldLogAccess(op)) {
+            FrameworkStatsLog.write(FrameworkStatsLog.APP_OP_ACCESS_TRACKED, uid, op, accessType,
+                    uidState, flags, attributionFlags,
+                    getAttributionTag(attributionTag, packageName),
+                    attributionChainId);
+        }
+
+        if (!isDiscreteOp(op, flags)) {
+            return;
+        }
+
+        long offsetChainId = attributionChainId;
+        if (attributionChainId != ATTRIBUTION_CHAIN_ID_NONE) {
+            offsetChainId = attributionChainId + mChainIdOffset;
+            // PermissionManagerService chain id reached the max value,
+            // reset offset, it's going to be very rare.
+            if (attributionChainId == Integer.MAX_VALUE) {
+                mChainIdOffset = offsetChainId;
+            }
+        }
+        DiscreteOp discreteOpEvent = new DiscreteOp(uid, packageName, attributionTag, deviceId, op,
+                flags, attributionFlags, uidState, offsetChainId, accessTime, accessDuration);
+        mDiscreteOpCache.add(discreteOpEvent);
+    }
+
+    @Override
+    void writeAndClearOldAccessHistory() {
+        // Let the sql impl also follow the same disk write frequencies as xml,
+        // controlled by AppOpsService.
+        mDiscreteOpsDbHelper.insertDiscreteOps(mDiscreteOpCache.getAllEventsAndClear());
+        if (!mSqliteWriteHandler.hasMessages(DELETE_OLD_OP_EVENTS)) {
+            if (mSqliteWriteHandler.sendEmptyMessageDelayed(DELETE_OLD_OP_EVENTS, THREE_HOURS)) {
+                Slog.w(TAG, "DELETE_OLD_OP_EVENTS is not queued");
+            }
+        }
+    }
+
+    @Override
+    void clearHistory() {
+        mDiscreteOpCache.clear();
+        mDiscreteOpsDbHelper.execSQL(DiscreteOpsTable.DELETE_TABLE_DATA);
+    }
+
+    @Override
+    void clearHistory(int uid, String packageName) {
+        mDiscreteOpCache.clear(uid, packageName);
+        mDiscreteOpsDbHelper.execSQL(DiscreteOpsTable.DELETE_DATA_FOR_UID_PACKAGE,
+                new Object[]{uid, packageName});
+    }
+
+    @Override
+    void offsetHistory(long offset) {
+        mDiscreteOpCache.offsetTimestamp(offset);
+        mDiscreteOpsDbHelper.execSQL(DiscreteOpsTable.OFFSET_ACCESS_TIME,
+                new Object[]{offset});
+    }
+
+    private IntArray getAppOpCodes(@AppOpsManager.HistoricalOpsRequestFilter int filter,
+            @Nullable String[] opNamesFilter) {
+        if ((filter & AppOpsManager.FILTER_BY_OP_NAMES) != 0) {
+            IntArray opCodes = new IntArray(opNamesFilter.length);
+            for (int i = 0; i < opNamesFilter.length; i++) {
+                int op;
+                try {
+                    op = AppOpsManager.strOpToOp(opNamesFilter[i]);
+                } catch (IllegalArgumentException ex) {
+                    Slog.w(TAG, "Appop `" + opNamesFilter[i] + "` is not recognized.");
+                    continue;
+                }
+                opCodes.add(op);
+            }
+            return opCodes;
+        }
+        return null;
+    }
+
+    @Override
+    void addFilteredDiscreteOpsToHistoricalOps(AppOpsManager.HistoricalOps result,
+            long beginTimeMillis, long endTimeMillis, int filter, int uidFilter,
+            @Nullable String packageNameFilter,
+            @Nullable String[] opNamesFilter,
+            @Nullable String attributionTagFilter, int opFlagsFilter,
+            Set<String> attributionExemptPkgs) {
+        // flush the cache into database before read.
+        writeAndClearOldAccessHistory();
+        boolean assembleChains = attributionExemptPkgs != null;
+        IntArray opCodes = getAppOpCodes(filter, opNamesFilter);
+        List<DiscreteOp> discreteOps = mDiscreteOpsDbHelper.getDiscreteOps(filter, uidFilter,
+                packageNameFilter, attributionTagFilter, opCodes, opFlagsFilter, beginTimeMillis,
+                endTimeMillis, -1, null);
+
+        LongSparseArray<AttributionChain> attributionChains = null;
+        if (assembleChains) {
+            attributionChains = createAttributionChains(discreteOps, attributionExemptPkgs);
+        }
+
+        int nEvents = discreteOps.size();
+        for (int j = 0; j < nEvents; j++) {
+            DiscreteOp event = discreteOps.get(j);
+            AppOpsManager.OpEventProxyInfo proxy = null;
+            if (assembleChains && event.mChainId != ATTRIBUTION_CHAIN_ID_NONE) {
+                AttributionChain chain = attributionChains.get(event.mChainId);
+                if (chain != null && chain.isComplete()
+                        && chain.isStart(event)
+                        && chain.mLastVisibleEvent != null) {
+                    DiscreteOp proxyEvent = chain.mLastVisibleEvent;
+                    proxy = new AppOpsManager.OpEventProxyInfo(proxyEvent.mUid,
+                            proxyEvent.mPackageName, proxyEvent.mAttributionTag);
+                }
+            }
+            result.addDiscreteAccess(event.mOpCode, event.mUid, event.mPackageName,
+                    event.mAttributionTag, event.mUidState, event.mOpFlags,
+                    event.mDiscretizedAccessTime, event.mDiscretizedDuration, proxy);
+        }
+    }
+
+    @Override
+    void dump(@NonNull PrintWriter pw, int uidFilter, @Nullable String packageNameFilter,
+            @Nullable String attributionTagFilter,
+            @AppOpsManager.HistoricalOpsRequestFilter int filter, int dumpOp,
+            @NonNull SimpleDateFormat sdf, @NonNull Date date, @NonNull String prefix,
+            int nDiscreteOps) {
+        writeAndClearOldAccessHistory();
+        IntArray opCodes = new IntArray();
+        if (dumpOp != AppOpsManager.OP_NONE) {
+            opCodes.add(dumpOp);
+        }
+        List<DiscreteOp> discreteOps = mDiscreteOpsDbHelper.getDiscreteOps(filter, uidFilter,
+                packageNameFilter, attributionTagFilter, opCodes, 0, -1,
+                -1, nDiscreteOps, DiscreteOpsTable.Columns.ACCESS_TIME);
+
+        pw.print(prefix);
+        pw.print("Largest chain id: ");
+        pw.print(mDiscreteOpsDbHelper.getLargestAttributionChainId());
+        pw.println();
+        pw.println("UID|PACKAGE_NAME|DEVICE_ID|OP_NAME|ATTRIBUTION_TAG|UID_STATE|OP_FLAGS|"
+                + "ATTR_FLAGS|CHAIN_ID|ACCESS_TIME|DURATION");
+        int discreteOpsCount = discreteOps.size();
+        for (int i = 0; i < discreteOpsCount; i++) {
+            DiscreteOp event = discreteOps.get(i);
+            date.setTime(event.mAccessTime);
+            pw.println(event.mUid + "|" + event.mPackageName + "|" + event.mDeviceId + "|"
+                    + AppOpsManager.opToName(event.mOpCode) + "|" + event.mAttributionTag + "|"
+                    + getUidStateName(event.mUidState) + "|"
+                    + flagsToString(event.mOpFlags) + "|" + event.mAttributionFlags + "|"
+                    + event.mChainId + "|"
+                    + sdf.format(date) + "|" + event.mDuration);
+        }
+        pw.println();
+    }
+
+    void migrateXmlData(List<DiscreteOp> opEvents, int chainIdOffset) {
+        mChainIdOffset = chainIdOffset;
+        mDiscreteOpsDbHelper.insertDiscreteOps(opEvents);
+    }
+
+    LongSparseArray<AttributionChain> createAttributionChains(
+            List<DiscreteOp> discreteOps, Set<String> attributionExemptPkgs) {
+        LongSparseArray<AttributionChain> chains = new LongSparseArray<>();
+        final int count = discreteOps.size();
+
+        for (int i = 0; i < count; i++) {
+            DiscreteOp opEvent = discreteOps.get(i);
+            if (opEvent.mChainId == ATTRIBUTION_CHAIN_ID_NONE
+                    || (opEvent.mAttributionFlags & ATTRIBUTION_FLAG_TRUSTED) == 0) {
+                continue;
+            }
+            AttributionChain chain = chains.get(opEvent.mChainId);
+            if (chain == null) {
+                chain = new AttributionChain(attributionExemptPkgs);
+                chains.put(opEvent.mChainId, chain);
+            }
+            chain.addEvent(opEvent);
+        }
+        return chains;
+    }
+
+    static class AttributionChain {
+        List<DiscreteOp> mChain = new ArrayList<>();
+        Set<String> mExemptPkgs;
+        DiscreteOp mStartEvent = null;
+        DiscreteOp mLastVisibleEvent = null;
+
+        AttributionChain(Set<String> exemptPkgs) {
+            mExemptPkgs = exemptPkgs;
+        }
+
+        boolean isComplete() {
+            return !mChain.isEmpty() && getStart() != null && isEnd(mChain.get(mChain.size() - 1));
+        }
+
+        DiscreteOp getStart() {
+            return mChain.isEmpty() || !isStart(mChain.get(0)) ? null : mChain.get(0);
+        }
+
+        private boolean isEnd(DiscreteOp event) {
+            return event != null
+                    && (event.mAttributionFlags & ATTRIBUTION_FLAG_ACCESSOR) != 0;
+        }
+
+        private boolean isStart(DiscreteOp event) {
+            return event != null
+                    && (event.mAttributionFlags & ATTRIBUTION_FLAG_RECEIVER) != 0;
+        }
+
+        DiscreteOp getLastVisible() {
+            // Search all nodes but the first one, which is the start node
+            for (int i = mChain.size() - 1; i > 0; i--) {
+                DiscreteOp event = mChain.get(i);
+                if (!mExemptPkgs.contains(event.mPackageName)) {
+                    return event;
+                }
+            }
+            return null;
+        }
+
+        void addEvent(DiscreteOp opEvent) {
+            // check if we have a matching event except duration.
+            DiscreteOp matchingItem = null;
+            for (int i = 0; i < mChain.size(); i++) {
+                DiscreteOp item = mChain.get(i);
+                if (item.equalsExceptDuration(opEvent)) {
+                    matchingItem = item;
+                    break;
+                }
+            }
+
+            if (matchingItem != null) {
+                // exact match or existing event has longer duration
+                if (matchingItem.mDuration == opEvent.mDuration
+                        || matchingItem.mDuration > opEvent.mDuration) {
+                    return;
+                }
+                mChain.remove(matchingItem);
+            }
+
+            if (mChain.isEmpty() || isEnd(opEvent)) {
+                mChain.add(opEvent);
+            } else if (isStart(opEvent)) {
+                mChain.add(0, opEvent);
+            } else {
+                for (int i = 0; i < mChain.size(); i++) {
+                    DiscreteOp currEvent = mChain.get(i);
+                    if ((!isStart(currEvent)
+                            && currEvent.mAccessTime > opEvent.mAccessTime)
+                            || (i == mChain.size() - 1 && isEnd(currEvent))) {
+                        mChain.add(i, opEvent);
+                        break;
+                    } else if (i == mChain.size() - 1) {
+                        mChain.add(opEvent);
+                        break;
+                    }
+                }
+            }
+            mStartEvent = isComplete() ? getStart() : null;
+            mLastVisibleEvent = isComplete() ? getLastVisible() : null;
+        }
+    }
+
+    /**
+     * Handler to write asynchronously to sqlite database.
+     */
+    class SqliteWriteHandler extends Handler {
+        SqliteWriteHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case WRITE_CACHE_EVICTED_OP_EVENTS:
+                    List<DiscreteOp> opEvents = (List<DiscreteOp>) msg.obj;
+                    mDiscreteOpsDbHelper.insertDiscreteOps(opEvents);
+                    break;
+                case DELETE_OLD_OP_EVENTS:
+                    long cutOffTimeStamp = System.currentTimeMillis() - sDiscreteHistoryCutoff;
+                    mDiscreteOpsDbHelper.execSQL(
+                            DiscreteOpsTable.DELETE_TABLE_DATA_BEFORE_ACCESS_TIME,
+                            new Object[]{cutOffTimeStamp});
+                    break;
+                default:
+                    throw new IllegalStateException("Unexpected value: " + msg.what);
+            }
+        }
+    }
+
+    /**
+     * A write cache for discrete ops. The noteOp, start/finishOp discrete op events are written to
+     * the cache first.
+     * <p>
+     * These events are persisted into sqlite database
+     * 1) Periodic interval, controlled by {@link AppOpsService}
+     * 2) When total events in the cache exceeds cache limit.
+     * 3) During read call we flush the whole cache to sqlite.
+     * 4) During shutdown.
+     */
+    class DiscreteOpCache {
+        private final int mCapacity;
+        private final ArraySet<DiscreteOp> mCache;
+
+        DiscreteOpCache(int capacity) {
+            mCapacity = capacity;
+            mCache = new ArraySet<>();
+        }
+
+        public void add(DiscreteOp opEvent) {
+            synchronized (this) {
+                if (mCache.contains(opEvent)) {
+                    return;
+                }
+                mCache.add(opEvent);
+                if (mCache.size() >= mCapacity) {
+                    if (DEBUG_LOG) {
+                        Slog.i(TAG, "Current discrete ops cache size: " + mCache.size());
+                    }
+                    List<DiscreteOp> evictedEvents = evict();
+                    if (DEBUG_LOG) {
+                        Slog.i(TAG, "Evicted discrete ops size: " + evictedEvents.size());
+                    }
+                    // if nothing to evict, just write the whole cache to disk
+                    if (evictedEvents.isEmpty()) {
+                        Slog.w(TAG, "No discrete ops event is evicted, write cache to db.");
+                        evictedEvents.addAll(mCache);
+                        mCache.clear();
+                    }
+                    mSqliteWriteHandler.obtainMessage(WRITE_CACHE_EVICTED_OP_EVENTS, evictedEvents);
+                }
+            }
+        }
+
+        /**
+         * Evict entries older than {@link DiscreteOpsRegistry#sDiscreteHistoryQuantization}.
+         */
+        private List<DiscreteOp> evict() {
+            synchronized (this) {
+                List<DiscreteOp> evictedEvents = new ArrayList<>();
+                Set<DiscreteOp> snapshot = new ArraySet<>(mCache);
+                long evictionTimestamp = System.currentTimeMillis() - sDiscreteHistoryQuantization;
+                evictionTimestamp = discretizeTimeStamp(evictionTimestamp);
+                for (DiscreteOp opEvent : snapshot) {
+                    if (opEvent.mDiscretizedAccessTime <= evictionTimestamp) {
+                        evictedEvents.add(opEvent);
+                        mCache.remove(opEvent);
+                    }
+                }
+                return evictedEvents;
+            }
+        }
+
+        /**
+         * Remove all the entries from cache.
+         *
+         * @return return all removed entries.
+         */
+        public List<DiscreteOp> getAllEventsAndClear() {
+            synchronized (this) {
+                List<DiscreteOp> cachedOps = new ArrayList<>(mCache.size());
+                if (mCache.isEmpty()) {
+                    return cachedOps;
+                }
+                cachedOps.addAll(mCache);
+                mCache.clear();
+                return cachedOps;
+            }
+        }
+
+        /**
+         * Remove all entries from the cache.
+         */
+        public void clear() {
+            synchronized (this) {
+                mCache.clear();
+            }
+        }
+
+        /**
+         * Offset access time by given offset milliseconds.
+         */
+        public void offsetTimestamp(long offsetMillis) {
+            synchronized (this) {
+                List<DiscreteOp> cachedOps = new ArrayList<>(mCache);
+                mCache.clear();
+                for (DiscreteOp discreteOp : cachedOps) {
+                    add(new DiscreteOp(discreteOp.getUid(), discreteOp.mPackageName,
+                            discreteOp.getAttributionTag(), discreteOp.getDeviceId(),
+                            discreteOp.mOpCode, discreteOp.mOpFlags,
+                            discreteOp.getAttributionFlags(), discreteOp.getUidState(),
+                            discreteOp.getChainId(), discreteOp.mAccessTime - offsetMillis,
+                            discreteOp.getDuration())
+                    );
+                }
+            }
+        }
+
+        /** Remove cached events for given UID and package. */
+        public void clear(int uid, String packageName) {
+            synchronized (this) {
+                Set<DiscreteOp> snapshot = new ArraySet<>(mCache);
+                for (DiscreteOp currentEvent : snapshot) {
+                    if (Objects.equals(packageName, currentEvent.mPackageName)
+                            && uid == currentEvent.getUid()) {
+                        mCache.remove(currentEvent);
+                    }
+                }
+            }
+        }
+    }
+
+    /** Immutable discrete op object. */
+    static class DiscreteOp {
+        private final int mUid;
+        private final String mPackageName;
+        private final String mAttributionTag;
+        private final String mDeviceId;
+        private final int mOpCode;
+        private final int mOpFlags;
+        private final int mAttributionFlags;
+        private final int mUidState;
+        private final long mChainId;
+        private final long mAccessTime;
+        private final long mDuration;
+        // store discretized timestamp to avoid repeated calculations.
+        private final long mDiscretizedAccessTime;
+        private final long mDiscretizedDuration;
+
+        DiscreteOp(int uid, String packageName, String attributionTag, String deviceId,
+                int opCode,
+                int mOpFlags, int mAttributionFlags, int uidState, long chainId, long accessTime,
+                long duration) {
+            this.mUid = uid;
+            this.mPackageName = packageName.intern();
+            this.mAttributionTag = attributionTag;
+            this.mDeviceId = deviceId;
+            this.mOpCode = opCode;
+            this.mOpFlags = mOpFlags;
+            this.mAttributionFlags = mAttributionFlags;
+            this.mUidState = uidState;
+            this.mChainId = chainId;
+            this.mAccessTime = accessTime;
+            this.mDiscretizedAccessTime = discretizeTimeStamp(accessTime);
+            this.mDuration = duration;
+            this.mDiscretizedDuration = discretizeDuration(duration);
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (!(o instanceof DiscreteOp that)) return false;
+
+            if (mUid != that.mUid) return false;
+            if (mOpCode != that.mOpCode) return false;
+            if (mOpFlags != that.mOpFlags) return false;
+            if (mAttributionFlags != that.mAttributionFlags) return false;
+            if (mUidState != that.mUidState) return false;
+            if (mChainId != that.mChainId) return false;
+            if (!Objects.equals(mPackageName, that.mPackageName)) {
+                return false;
+            }
+            if (!Objects.equals(mAttributionTag, that.mAttributionTag)) {
+                return false;
+            }
+            if (!Objects.equals(mDeviceId, that.mDeviceId)) {
+                return false;
+            }
+            if (mDiscretizedAccessTime != that.mDiscretizedAccessTime) {
+                return false;
+            }
+            return mDiscretizedDuration == that.mDiscretizedDuration;
+        }
+
+        @Override
+        public int hashCode() {
+            int result = mUid;
+            result = 31 * result + (mPackageName != null ? mPackageName.hashCode() : 0);
+            result = 31 * result + (mAttributionTag != null ? mAttributionTag.hashCode() : 0);
+            result = 31 * result + (mDeviceId != null ? mDeviceId.hashCode() : 0);
+            result = 31 * result + mOpCode;
+            result = 31 * result + mOpFlags;
+            result = 31 * result + mAttributionFlags;
+            result = 31 * result + mUidState;
+            result = 31 * result + Objects.hash(mChainId);
+            result = 31 * result + Objects.hash(mDiscretizedAccessTime);
+            result = 31 * result + Objects.hash(mDiscretizedDuration);
+            return result;
+        }
+
+        public boolean equalsExceptDuration(DiscreteOp that) {
+            if (mUid != that.mUid) return false;
+            if (mOpCode != that.mOpCode) return false;
+            if (mOpFlags != that.mOpFlags) return false;
+            if (mAttributionFlags != that.mAttributionFlags) return false;
+            if (mUidState != that.mUidState) return false;
+            if (mChainId != that.mChainId) return false;
+            if (!Objects.equals(mPackageName, that.mPackageName)) {
+                return false;
+            }
+            if (!Objects.equals(mAttributionTag, that.mAttributionTag)) {
+                return false;
+            }
+            if (!Objects.equals(mDeviceId, that.mDeviceId)) {
+                return false;
+            }
+            return mAccessTime == that.mAccessTime;
+        }
+
+        @Override
+        public String toString() {
+            return "DiscreteOp{"
+                    + "uid=" + mUid
+                    + ", packageName='" + mPackageName + '\''
+                    + ", attributionTag='" + mAttributionTag + '\''
+                    + ", deviceId='" + mDeviceId + '\''
+                    + ", opCode=" + AppOpsManager.opToName(mOpCode)
+                    + ", opFlag=" + flagsToString(mOpFlags)
+                    + ", attributionFlag=" + mAttributionFlags
+                    + ", uidState=" + getUidStateName(mUidState)
+                    + ", chainId=" + mChainId
+                    + ", accessTime=" + mAccessTime
+                    + ", duration=" + mDuration + '}';
+        }
+
+        public int getUid() {
+            return mUid;
+        }
+
+        public String getPackageName() {
+            return mPackageName;
+        }
+
+        public String getAttributionTag() {
+            return mAttributionTag;
+        }
+
+        public String getDeviceId() {
+            return mDeviceId;
+        }
+
+        public int getOpCode() {
+            return mOpCode;
+        }
+
+        @AppOpsManager.OpFlags
+        public int getOpFlags() {
+            return mOpFlags;
+        }
+
+
+        @AppOpsManager.AttributionFlags
+        public int getAttributionFlags() {
+            return mAttributionFlags;
+        }
+
+        @AppOpsManager.UidState
+        public int getUidState() {
+            return mUidState;
+        }
+
+        public long getChainId() {
+            return mChainId;
+        }
+
+        public long getAccessTime() {
+            return mAccessTime;
+        }
+
+        public long getDuration() {
+            return mDuration;
+        }
+    }
+
+    // API for tests only, can be removed or changed.
+    void recordDiscreteAccess(DiscreteOp discreteOpEvent) {
+        mDiscreteOpCache.add(discreteOpEvent);
+    }
+
+    // API for tests only, can be removed or changed.
+    List<DiscreteOp> getCachedDiscreteOps() {
+        return new ArrayList<>(mDiscreteOpCache.mCache);
+    }
+
+    // API for tests only, can be removed or changed.
+    List<DiscreteOp> getAllDiscreteOps() {
+        List<DiscreteOp> ops = new ArrayList<>(mDiscreteOpCache.mCache);
+        ops.addAll(mDiscreteOpsDbHelper.getAllDiscreteOps(DiscreteOpsTable.SELECT_TABLE_DATA));
+        return ops;
+    }
+
+    // API for testing and migration
+    long getLargestAttributionChainId() {
+        return mDiscreteOpsDbHelper.getLargestAttributionChainId();
+    }
+
+    // API for testing and migration
+    void deleteDatabase() {
+        mDiscreteOpsDbHelper.close();
+        mContext.deleteDatabase(mDatabaseFile.getName());
+    }
+}
diff --git a/services/core/java/com/android/server/appop/DiscreteOpsTable.java b/services/core/java/com/android/server/appop/DiscreteOpsTable.java
new file mode 100644
index 0000000..9cb19aa
--- /dev/null
+++ b/services/core/java/com/android/server/appop/DiscreteOpsTable.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.appop;
+
+
+/**
+ * SQLite table for storing app op accesses.
+ */
+final class DiscreteOpsTable {
+    private static final String TABLE_NAME = "app_op_accesses";
+    private static final String INDEX_APP_OP = "app_op_access_index";
+
+    static final class Columns {
+        /** Auto increment primary key. */
+        static final String ID = "id";
+        /** UID of the package accessing private data. */
+        static final String UID = "uid";
+        /** Package accessing private data. */
+        static final String PACKAGE_NAME = "package_name";
+        /** The device from which the private data is accessed. */
+        static final String DEVICE_ID = "device_id";
+        /** Op code representing private data i.e. location, mic etc. */
+        static final String OP_CODE = "op_code";
+        /** Attribution tag provided when accessing the private data. */
+        static final String ATTRIBUTION_TAG = "attribution_tag";
+        /** Timestamp when private data is accessed, number of milliseconds that have passed
+         * since Unix epoch */
+        static final String ACCESS_TIME = "access_time";
+        /** For how long the private data is accessed. */
+        static final String ACCESS_DURATION = "access_duration";
+        /** App process state, whether the app is in foreground, background or cached etc. */
+        static final String UID_STATE = "uid_state";
+        /** App op flags */
+        static final String OP_FLAGS = "op_flags";
+        /** Attribution flags */
+        static final String ATTRIBUTION_FLAGS = "attribution_flags";
+        /** Chain id */
+        static final String CHAIN_ID = "chain_id";
+    }
+
+    static final int UID_INDEX = 1;
+    static final int PACKAGE_NAME_INDEX = 2;
+    static final int DEVICE_ID_INDEX = 3;
+    static final int OP_CODE_INDEX = 4;
+    static final int ATTRIBUTION_TAG_INDEX = 5;
+    static final int ACCESS_TIME_INDEX = 6;
+    static final int ACCESS_DURATION_INDEX = 7;
+    static final int UID_STATE_INDEX = 8;
+    static final int OP_FLAGS_INDEX = 9;
+    static final int ATTRIBUTION_FLAGS_INDEX = 10;
+    static final int CHAIN_ID_INDEX = 11;
+
+    static final String CREATE_TABLE_SQL = "CREATE TABLE IF NOT EXISTS "
+            + TABLE_NAME + "("
+            + Columns.ID + " INTEGER PRIMARY KEY,"
+            + Columns.UID + " INTEGER,"
+            + Columns.PACKAGE_NAME + " TEXT,"
+            + Columns.DEVICE_ID + " TEXT NOT NULL,"
+            + Columns.OP_CODE + " INTEGER,"
+            + Columns.ATTRIBUTION_TAG + " TEXT,"
+            + Columns.ACCESS_TIME + " INTEGER,"
+            + Columns.ACCESS_DURATION + " INTEGER,"
+            + Columns.UID_STATE + " INTEGER,"
+            + Columns.OP_FLAGS + " INTEGER,"
+            + Columns.ATTRIBUTION_FLAGS + " INTEGER,"
+            + Columns.CHAIN_ID + " INTEGER"
+            + ")";
+
+    static final String INSERT_TABLE_SQL = "INSERT INTO " + TABLE_NAME + "("
+            + Columns.UID + ", "
+            + Columns.PACKAGE_NAME + ", "
+            + Columns.DEVICE_ID + ", "
+            + Columns.OP_CODE + ", "
+            + Columns.ATTRIBUTION_TAG + ", "
+            + Columns.ACCESS_TIME + ", "
+            + Columns.ACCESS_DURATION + ", "
+            + Columns.UID_STATE + ", "
+            + Columns.OP_FLAGS + ", "
+            + Columns.ATTRIBUTION_FLAGS + ", "
+            + Columns.CHAIN_ID + ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
+
+    static final String SELECT_MAX_ATTRIBUTION_CHAIN_ID = "SELECT MAX(" + Columns.CHAIN_ID + ")"
+            + " FROM " + TABLE_NAME;
+
+    static final String SELECT_TABLE_DATA = "SELECT DISTINCT "
+            + Columns.UID + ","
+            + Columns.PACKAGE_NAME + ","
+            + Columns.DEVICE_ID + ","
+            + Columns.OP_CODE + ","
+            + Columns.ATTRIBUTION_TAG + ","
+            + Columns.ACCESS_TIME + ","
+            + Columns.ACCESS_DURATION + ","
+            + Columns.UID_STATE + ","
+            + Columns.OP_FLAGS + ","
+            + Columns.ATTRIBUTION_FLAGS + ","
+            + Columns.CHAIN_ID
+            + " FROM " + TABLE_NAME;
+
+    static final String DELETE_TABLE_DATA = "DELETE FROM " + TABLE_NAME;
+
+    static final String DELETE_TABLE_DATA_BEFORE_ACCESS_TIME = "DELETE FROM " + TABLE_NAME
+            + " WHERE " + Columns.ACCESS_TIME + " < ?";
+
+    static final String DELETE_DATA_FOR_UID_PACKAGE = "DELETE FROM " + DiscreteOpsTable.TABLE_NAME
+            + " WHERE " + Columns.UID + " = ? AND " + Columns.PACKAGE_NAME + " = ?";
+
+    static final String OFFSET_ACCESS_TIME = "UPDATE " + DiscreteOpsTable.TABLE_NAME
+            + " SET " + Columns.ACCESS_TIME + " = ACCESS_TIME - ?";
+
+    // Index on access time, uid and op code
+    static final String CREATE_INDEX_SQL = "CREATE INDEX IF NOT EXISTS "
+            + INDEX_APP_OP + " ON " + TABLE_NAME
+            + " (" + Columns.ACCESS_TIME + ", " + Columns.UID + ", " + Columns.OP_CODE + ")";
+}
diff --git a/services/core/java/com/android/server/appop/DiscreteOpsTestingShim.java b/services/core/java/com/android/server/appop/DiscreteOpsTestingShim.java
new file mode 100644
index 0000000..1523cca
--- /dev/null
+++ b/services/core/java/com/android/server/appop/DiscreteOpsTestingShim.java
@@ -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.server.appop;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AppOpsManager;
+import android.os.SystemClock;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.Slog;
+
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * A testing class, which supports both xml and sqlite persistence for discrete ops, the class
+ * logs warning if there is a mismatch in the behavior.
+ */
+class DiscreteOpsTestingShim extends DiscreteOpsRegistry {
+    private static final String LOG_TAG = "DiscreteOpsTestingShim";
+    private final DiscreteOpsRegistry mXmlRegistry;
+    private final DiscreteOpsRegistry mSqlRegistry;
+
+    DiscreteOpsTestingShim(DiscreteOpsRegistry xmlRegistry,
+            DiscreteOpsRegistry sqlRegistry) {
+        mXmlRegistry = xmlRegistry;
+        mSqlRegistry = sqlRegistry;
+    }
+
+    @Override
+    void recordDiscreteAccess(int uid, String packageName, @NonNull String deviceId, int op,
+            @Nullable String attributionTag, int flags, int uidState, long accessTime,
+            long accessDuration, int attributionFlags, int attributionChainId, int accessType) {
+        long start = SystemClock.uptimeMillis();
+        mXmlRegistry.recordDiscreteAccess(uid, packageName, deviceId, op, attributionTag, flags,
+                uidState, accessTime, accessDuration, attributionFlags, attributionChainId,
+                accessType);
+        long start2 = SystemClock.uptimeMillis();
+        mSqlRegistry.recordDiscreteAccess(uid, packageName, deviceId, op, attributionTag, flags,
+                uidState, accessTime, accessDuration, attributionFlags, attributionChainId,
+                accessType);
+        long end = SystemClock.uptimeMillis();
+        long xmlTimeTaken = start2 - start;
+        long sqlTimeTaken = end - start2;
+        Log.i(LOG_TAG,
+                "recordDiscreteAccess: XML time taken : " + xmlTimeTaken + ", SQL time taken : "
+                        + sqlTimeTaken + ", diff (sql - xml): " + (sqlTimeTaken - xmlTimeTaken));
+    }
+
+
+    @Override
+    void writeAndClearOldAccessHistory() {
+        mXmlRegistry.writeAndClearOldAccessHistory();
+        mSqlRegistry.writeAndClearOldAccessHistory();
+    }
+
+    @Override
+    void clearHistory() {
+        mXmlRegistry.clearHistory();
+        mSqlRegistry.clearHistory();
+    }
+
+    @Override
+    void clearHistory(int uid, String packageName) {
+        mXmlRegistry.clearHistory(uid, packageName);
+        mSqlRegistry.clearHistory(uid, packageName);
+    }
+
+    @Override
+    void offsetHistory(long offset) {
+        mXmlRegistry.offsetHistory(offset);
+        mSqlRegistry.offsetHistory(offset);
+    }
+
+    @Override
+    void addFilteredDiscreteOpsToHistoricalOps(AppOpsManager.HistoricalOps result,
+            long beginTimeMillis, long endTimeMillis, int filter, int uidFilter,
+            @Nullable String packageNameFilter, @Nullable String[] opNamesFilter,
+            @Nullable String attributionTagFilter, int flagsFilter,
+            Set<String> attributionExemptPkgs) {
+        AppOpsManager.HistoricalOps result2 =
+                new AppOpsManager.HistoricalOps(beginTimeMillis, endTimeMillis);
+
+        long start = System.currentTimeMillis();
+        mXmlRegistry.addFilteredDiscreteOpsToHistoricalOps(result2, beginTimeMillis, endTimeMillis,
+                filter, uidFilter, packageNameFilter, opNamesFilter, attributionTagFilter,
+                flagsFilter, attributionExemptPkgs);
+        long start2 = System.currentTimeMillis();
+        mSqlRegistry.addFilteredDiscreteOpsToHistoricalOps(result, beginTimeMillis, endTimeMillis,
+                filter, uidFilter, packageNameFilter, opNamesFilter, attributionTagFilter,
+                flagsFilter, attributionExemptPkgs);
+        long end = System.currentTimeMillis();
+        long xmlTimeTaken = start2 - start;
+        long sqlTimeTaken = end - start2;
+        try {
+            assertHistoricalOpsAreEquals(result, result2);
+        } catch (Exception ex) {
+            Slog.e(LOG_TAG, "different output when reading discrete ops", ex);
+        }
+        Log.i(LOG_TAG, "Read: XML time taken : " + xmlTimeTaken + ", SQL time taken : "
+                + sqlTimeTaken + ", diff (sql - xml): " + (sqlTimeTaken - xmlTimeTaken));
+    }
+
+    void assertHistoricalOpsAreEquals(AppOpsManager.HistoricalOps sqlResult,
+            AppOpsManager.HistoricalOps xmlResult) {
+        assertEquals(sqlResult.getUidCount(), xmlResult.getUidCount());
+        int uidCount = sqlResult.getUidCount();
+
+        for (int i = 0; i < uidCount; i++) {
+            AppOpsManager.HistoricalUidOps sqlUidOps = sqlResult.getUidOpsAt(i);
+            AppOpsManager.HistoricalUidOps xmlUidOps = xmlResult.getUidOpsAt(i);
+            Slog.i(LOG_TAG, "sql uid: " + sqlUidOps.getUid() + ", xml uid: " + xmlUidOps.getUid());
+            assertEquals(sqlUidOps.getUid(), xmlUidOps.getUid());
+            assertEquals(sqlUidOps.getPackageCount(), xmlUidOps.getPackageCount());
+
+            int packageCount = sqlUidOps.getPackageCount();
+            for (int p = 0; p < packageCount; p++) {
+                AppOpsManager.HistoricalPackageOps sqlPackageOps = sqlUidOps.getPackageOpsAt(p);
+                AppOpsManager.HistoricalPackageOps xmlPackageOps = xmlUidOps.getPackageOpsAt(p);
+                Slog.i(LOG_TAG, "sql package: " + sqlPackageOps.getPackageName() + ", xml package: "
+                        + xmlPackageOps.getPackageName());
+                assertEquals(sqlPackageOps.getPackageName(), xmlPackageOps.getPackageName());
+                assertEquals(sqlPackageOps.getAttributedOpsCount(),
+                        xmlPackageOps.getAttributedOpsCount());
+
+                int attrCount = sqlPackageOps.getAttributedOpsCount();
+                for (int a = 0; a < attrCount; a++) {
+                    AppOpsManager.AttributedHistoricalOps sqlAttrOps =
+                            sqlPackageOps.getAttributedOpsAt(a);
+                    AppOpsManager.AttributedHistoricalOps xmlAttrOps =
+                            xmlPackageOps.getAttributedOpsAt(a);
+                    Slog.i(LOG_TAG, "sql tag: " + sqlAttrOps.getTag() + ", xml tag: "
+                            + xmlAttrOps.getTag());
+                    assertEquals(sqlAttrOps.getTag(), xmlAttrOps.getTag());
+                    assertEquals(sqlAttrOps.getOpCount(), xmlAttrOps.getOpCount());
+
+                    int opCount = sqlAttrOps.getOpCount();
+                    for (int o = 0; o < opCount; o++) {
+                        AppOpsManager.HistoricalOp sqlHistoricalOp = sqlAttrOps.getOpAt(o);
+                        AppOpsManager.HistoricalOp xmlHistoricalOp = xmlAttrOps.getOpAt(o);
+                        Slog.i(LOG_TAG, "sql op: " + sqlHistoricalOp.getOpName() + ", xml op: "
+                                + xmlHistoricalOp.getOpName());
+                        assertEquals(sqlHistoricalOp.getOpName(), xmlHistoricalOp.getOpName());
+                        assertEquals(sqlHistoricalOp.getDiscreteAccessCount(),
+                                xmlHistoricalOp.getDiscreteAccessCount());
+
+                        int accessCount = sqlHistoricalOp.getDiscreteAccessCount();
+                        for (int x = 0; x < accessCount; x++) {
+                            AppOpsManager.AttributedOpEntry sqlOpEntry =
+                                    sqlHistoricalOp.getDiscreteAccessAt(x);
+                            AppOpsManager.AttributedOpEntry xmlOpEntry =
+                                    xmlHistoricalOp.getDiscreteAccessAt(x);
+                            Slog.i(LOG_TAG, "sql keys: " + sqlOpEntry.collectKeys() + ", xml keys: "
+                                    + xmlOpEntry.collectKeys());
+                            assertEquals(sqlOpEntry.collectKeys(), xmlOpEntry.collectKeys());
+                            assertEquals(sqlOpEntry.isRunning(), xmlOpEntry.isRunning());
+                            ArraySet<Long> keys = sqlOpEntry.collectKeys();
+                            final int keyCount = keys.size();
+                            for (int k = 0; k < keyCount; k++) {
+                                final long key = keys.valueAt(k);
+                                final int flags = extractFlagsFromKey(key);
+                                assertEquals(sqlOpEntry.getLastDuration(flags),
+                                        xmlOpEntry.getLastDuration(flags));
+                                assertEquals(sqlOpEntry.getLastProxyInfo(flags),
+                                        xmlOpEntry.getLastProxyInfo(flags));
+                                assertEquals(sqlOpEntry.getLastAccessTime(flags),
+                                        xmlOpEntry.getLastAccessTime(flags));
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    // code duplicated for assertions
+    private static final int FLAGS_MASK = 0xFFFFFFFF;
+
+    public static int extractFlagsFromKey(@AppOpsManager.DataBucketKey long key) {
+        return (int) (key & FLAGS_MASK);
+    }
+
+    private void assertEquals(Object actual, Object expected) {
+        if (!Objects.equals(actual, expected)) {
+            throw new IllegalStateException("Actual (" + actual + ") is not equal to expected ("
+                    + expected + ")");
+        }
+    }
+
+    @Override
+    void dump(@NonNull PrintWriter pw, int uidFilter, @Nullable String packageNameFilter,
+            @Nullable String attributionTagFilter, int filter, int dumpOp,
+            @NonNull SimpleDateFormat sdf, @NonNull Date date, @NonNull String prefix,
+            int nDiscreteOps) {
+        mXmlRegistry.dump(pw, uidFilter, packageNameFilter, attributionTagFilter, filter, dumpOp,
+                sdf, date, prefix, nDiscreteOps);
+        pw.println("--------------------------------------------------------");
+        pw.println("--------------------------------------------------------");
+        mSqlRegistry.dump(pw, uidFilter, packageNameFilter, attributionTagFilter, filter, dumpOp,
+                sdf, date, prefix, nDiscreteOps);
+    }
+}
diff --git a/services/core/java/com/android/server/appop/DiscreteRegistry.java b/services/core/java/com/android/server/appop/DiscreteOpsXmlRegistry.java
similarity index 84%
rename from services/core/java/com/android/server/appop/DiscreteRegistry.java
rename to services/core/java/com/android/server/appop/DiscreteOpsXmlRegistry.java
index 7f161f6..a6e3fc7 100644
--- a/services/core/java/com/android/server/appop/DiscreteRegistry.java
+++ b/services/core/java/com/android/server/appop/DiscreteOpsXmlRegistry.java
@@ -24,48 +24,20 @@
 import static android.app.AppOpsManager.FILTER_BY_OP_NAMES;
 import static android.app.AppOpsManager.FILTER_BY_PACKAGE_NAME;
 import static android.app.AppOpsManager.FILTER_BY_UID;
-import static android.app.AppOpsManager.OP_CAMERA;
-import static android.app.AppOpsManager.OP_COARSE_LOCATION;
-import static android.app.AppOpsManager.OP_EMERGENCY_LOCATION;
-import static android.app.AppOpsManager.OP_FINE_LOCATION;
 import static android.app.AppOpsManager.OP_FLAGS_ALL;
-import static android.app.AppOpsManager.OP_FLAG_SELF;
-import static android.app.AppOpsManager.OP_FLAG_TRUSTED_PROXIED;
-import static android.app.AppOpsManager.OP_FLAG_TRUSTED_PROXY;
-import static android.app.AppOpsManager.OP_MONITOR_HIGH_POWER_LOCATION;
-import static android.app.AppOpsManager.OP_MONITOR_LOCATION;
 import static android.app.AppOpsManager.OP_NONE;
-import static android.app.AppOpsManager.OP_PHONE_CALL_CAMERA;
-import static android.app.AppOpsManager.OP_PHONE_CALL_MICROPHONE;
-import static android.app.AppOpsManager.OP_PROCESS_OUTGOING_CALLS;
-import static android.app.AppOpsManager.OP_READ_ICC_SMS;
-import static android.app.AppOpsManager.OP_READ_SMS;
-import static android.app.AppOpsManager.OP_RECEIVE_AMBIENT_TRIGGER_AUDIO;
-import static android.app.AppOpsManager.OP_RECEIVE_SANDBOX_TRIGGER_AUDIO;
-import static android.app.AppOpsManager.OP_RECORD_AUDIO;
-import static android.app.AppOpsManager.OP_RESERVED_FOR_TESTING;
-import static android.app.AppOpsManager.OP_SEND_SMS;
-import static android.app.AppOpsManager.OP_SMS_FINANCIAL_TRANSACTIONS;
-import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
-import static android.app.AppOpsManager.OP_WRITE_ICC_SMS;
-import static android.app.AppOpsManager.OP_WRITE_SMS;
 import static android.app.AppOpsManager.flagsToString;
 import static android.app.AppOpsManager.getUidStateName;
 import static android.companion.virtual.VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT;
 
-import static java.lang.Long.min;
 import static java.lang.Math.max;
 
-import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.AppOpsManager;
-import android.os.AsyncTask;
-import android.os.Build;
 import android.os.Environment;
 import android.os.FileUtils;
 import android.permission.flags.Flags;
-import android.provider.DeviceConfig;
 import android.util.ArrayMap;
 import android.util.AtomicFile;
 import android.util.Slog;
@@ -84,10 +56,7 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.PrintWriter;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
 import java.text.SimpleDateFormat;
-import java.time.Duration;
 import java.time.Instant;
 import java.time.temporal.ChronoUnit;
 import java.util.ArrayList;
@@ -99,100 +68,30 @@
 import java.util.Set;
 
 /**
- * This class manages information about recent accesses to ops for permission usage timeline.
+ * Xml persistence implementation for discrete ops.
  *
- * The discrete history is kept for limited time (initial default is 24 hours, set in
- * {@link DiscreteRegistry#sDiscreteHistoryCutoff) and discarded after that.
- *
- * Discrete history is quantized to reduce resources footprint. By default quantization is set to
- * one minute in {@link DiscreteRegistry#sDiscreteHistoryQuantization}. All access times are aligned
- * to the closest quantized time. All durations (except -1, meaning no duration) are rounded up to
- * the closest quantized interval.
- *
- * When data is queried through API, events are deduplicated and for every time quant there can
- * be only one {@link AppOpsManager.AttributedOpEntry}. Each entry contains information about
- * different accesses which happened in specified time quant - across dimensions of
- * {@link AppOpsManager.UidState} and {@link AppOpsManager.OpFlags}. For each dimension
- * it is only possible to know if at least one access happened in the time quant.
- *
+ * <p>
  * Every time state is saved (default is 30 minutes), memory state is dumped to a
  * new file and memory state is cleared. Files older than time limit are deleted
  * during the process.
- *
+ * <p>
  * When request comes in, files are read and requested information is collected
  * and delivered. Information is cached in memory until the next state save (up to 30 minutes), to
  * avoid reading disk if more API calls come in a quick succession.
- *
+ * <p>
  * THREADING AND LOCKING:
- * For in-memory transactions this class relies on {@link DiscreteRegistry#mInMemoryLock}. It is
- * assumed that the same lock is used for in-memory transactions in {@link AppOpsService},
- * {@link HistoricalRegistry}, and {@link DiscreteRegistry}.
- * {@link DiscreteRegistry#recordDiscreteAccess(int, String, int, String, int, int, long, long)}
- * must only be called while holding this lock.
- * {@link DiscreteRegistry#mOnDiskLock} is used when disk transactions are performed.
- * It is very important to release {@link DiscreteRegistry#mInMemoryLock} as soon as possible, as
- * no AppOps related transactions across the system can be performed while it is held.
+ * For in-memory transactions this class relies on {@link DiscreteOpsXmlRegistry#mInMemoryLock}.
+ * It is assumed that the same lock is used for in-memory transactions in {@link AppOpsService},
+ * {@link HistoricalRegistry}, and {@link DiscreteOpsXmlRegistry }.
+ * {@link DiscreteOpsRegistry#recordDiscreteAccess} must only be called while holding this lock.
+ * {@link DiscreteOpsXmlRegistry#mOnDiskLock} is used when disk transactions are performed.
+ * It is very important to release {@link DiscreteOpsXmlRegistry#mInMemoryLock} as soon as
+ * possible, as no AppOps related transactions across the system can be performed while it is held.
  *
- * INITIALIZATION: We can initialize persistence only after the system is ready
- * as we need to check the optional configuration override from the settings
- * database which is not initialized at the time the app ops service is created. This class
- * relies on {@link HistoricalRegistry} for controlling that no calls are allowed until then. All
- * outside calls are going through {@link HistoricalRegistry}, where
- * {@link HistoricalRegistry#isPersistenceInitializedMLocked()} check is done.
  */
-
-final class DiscreteRegistry {
+class DiscreteOpsXmlRegistry extends DiscreteOpsRegistry {
     static final String DISCRETE_HISTORY_FILE_SUFFIX = "tl";
-    private static final String TAG = DiscreteRegistry.class.getSimpleName();
-
-    private static final String PROPERTY_DISCRETE_HISTORY_CUTOFF = "discrete_history_cutoff_millis";
-    private static final String PROPERTY_DISCRETE_HISTORY_QUANTIZATION =
-            "discrete_history_quantization_millis";
-    private static final String PROPERTY_DISCRETE_FLAGS = "discrete_history_op_flags";
-    private static final String PROPERTY_DISCRETE_OPS_LIST = "discrete_history_ops_cslist";
-    private static final String DEFAULT_DISCRETE_OPS = OP_FINE_LOCATION + "," + OP_COARSE_LOCATION
-            + "," + OP_EMERGENCY_LOCATION + "," + OP_CAMERA + "," + OP_RECORD_AUDIO + ","
-            + OP_PHONE_CALL_MICROPHONE + "," + OP_PHONE_CALL_CAMERA + ","
-            + OP_RECEIVE_AMBIENT_TRIGGER_AUDIO + "," + OP_RECEIVE_SANDBOX_TRIGGER_AUDIO
-            + "," + OP_RESERVED_FOR_TESTING;
-    private static final int[] sDiscreteOpsToLog =
-            new int[]{OP_FINE_LOCATION, OP_COARSE_LOCATION, OP_EMERGENCY_LOCATION, OP_CAMERA,
-                    OP_RECORD_AUDIO, OP_PHONE_CALL_MICROPHONE, OP_PHONE_CALL_CAMERA,
-                    OP_RECEIVE_AMBIENT_TRIGGER_AUDIO, OP_RECEIVE_SANDBOX_TRIGGER_AUDIO, OP_READ_SMS,
-                    OP_WRITE_SMS, OP_SEND_SMS, OP_READ_ICC_SMS, OP_WRITE_ICC_SMS,
-                    OP_SMS_FINANCIAL_TRANSACTIONS, OP_SYSTEM_ALERT_WINDOW, OP_MONITOR_LOCATION,
-                    OP_MONITOR_HIGH_POWER_LOCATION, OP_PROCESS_OUTGOING_CALLS,
-            };
-    private static final long DEFAULT_DISCRETE_HISTORY_CUTOFF = Duration.ofDays(7).toMillis();
-    private static final long MAXIMUM_DISCRETE_HISTORY_CUTOFF = Duration.ofDays(30).toMillis();
-    private static final long DEFAULT_DISCRETE_HISTORY_QUANTIZATION =
-            Duration.ofMinutes(1).toMillis();
-
-    static final int ACCESS_TYPE_NOTE_OP =
-            FrameworkStatsLog.APP_OP_ACCESS_TRACKED__ACCESS_TYPE__NOTE_OP;
-    static final int ACCESS_TYPE_START_OP =
-            FrameworkStatsLog.APP_OP_ACCESS_TRACKED__ACCESS_TYPE__START_OP;
-    static final int ACCESS_TYPE_FINISH_OP =
-            FrameworkStatsLog.APP_OP_ACCESS_TRACKED__ACCESS_TYPE__FINISH_OP;
-    static final int ACCESS_TYPE_PAUSE_OP =
-            FrameworkStatsLog.APP_OP_ACCESS_TRACKED__ACCESS_TYPE__PAUSE_OP;
-    static final int ACCESS_TYPE_RESUME_OP =
-            FrameworkStatsLog.APP_OP_ACCESS_TRACKED__ACCESS_TYPE__RESUME_OP;
-
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef(prefix = {"ACCESS_TYPE_"}, value = {
-            ACCESS_TYPE_NOTE_OP,
-            ACCESS_TYPE_START_OP,
-            ACCESS_TYPE_FINISH_OP,
-            ACCESS_TYPE_PAUSE_OP,
-            ACCESS_TYPE_RESUME_OP
-    })
-    public @interface AccessType {}
-
-    private static long sDiscreteHistoryCutoff;
-    private static long sDiscreteHistoryQuantization;
-    private static int[] sDiscreteOps;
-    private static int sDiscreteFlags;
+    private static final String TAG = DiscreteOpsXmlRegistry.class.getSimpleName();
 
     private static final String TAG_HISTORY = "h";
     private static final String ATTR_VERSION = "v";
@@ -221,9 +120,6 @@
     private static final String ATTR_ATTRIBUTION_FLAGS = "af";
     private static final String ATTR_CHAIN_ID = "ci";
 
-    private static final int OP_FLAGS_DISCRETE = OP_FLAG_SELF | OP_FLAG_TRUSTED_PROXIED
-            | OP_FLAG_TRUSTED_PROXY;
-
     // Lock for read/write access to on disk state
     private final Object mOnDiskLock = new Object();
 
@@ -239,14 +135,12 @@
     @GuardedBy("mOnDiskLock")
     private DiscreteOps mCachedOps = null;
 
-    private boolean mDebugMode = false;
-
-    DiscreteRegistry(Object inMemoryLock) {
-        this(inMemoryLock, new File(new File(Environment.getDataSystemDirectory(), "appops"),
-                "discrete"));
+    DiscreteOpsXmlRegistry(Object inMemoryLock) {
+        this(inMemoryLock, getDiscreteOpsDir());
     }
 
-    DiscreteRegistry(Object inMemoryLock, File discreteAccessDir) {
+    // constructor for tests.
+    DiscreteOpsXmlRegistry(Object inMemoryLock, File discreteAccessDir) {
         mInMemoryLock = inMemoryLock;
         synchronized (mOnDiskLock) {
             mDiscreteAccessDir = discreteAccessDir;
@@ -258,40 +152,8 @@
         }
     }
 
-    void systemReady() {
-        DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_PRIVACY,
-                AsyncTask.THREAD_POOL_EXECUTOR, (DeviceConfig.Properties p) -> {
-                    setDiscreteHistoryParameters(p);
-                });
-        setDiscreteHistoryParameters(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_PRIVACY));
-    }
-
-    private void setDiscreteHistoryParameters(DeviceConfig.Properties p) {
-        if (p.getKeyset().contains(PROPERTY_DISCRETE_HISTORY_CUTOFF)) {
-            sDiscreteHistoryCutoff = p.getLong(PROPERTY_DISCRETE_HISTORY_CUTOFF,
-                    DEFAULT_DISCRETE_HISTORY_CUTOFF);
-            if (!Build.IS_DEBUGGABLE && !mDebugMode) {
-                sDiscreteHistoryCutoff = min(MAXIMUM_DISCRETE_HISTORY_CUTOFF,
-                        sDiscreteHistoryCutoff);
-            }
-        } else {
-            sDiscreteHistoryCutoff = DEFAULT_DISCRETE_HISTORY_CUTOFF;
-        }
-        if (p.getKeyset().contains(PROPERTY_DISCRETE_HISTORY_QUANTIZATION)) {
-            sDiscreteHistoryQuantization = p.getLong(PROPERTY_DISCRETE_HISTORY_QUANTIZATION,
-                    DEFAULT_DISCRETE_HISTORY_QUANTIZATION);
-            if (!Build.IS_DEBUGGABLE && !mDebugMode) {
-                sDiscreteHistoryQuantization = max(DEFAULT_DISCRETE_HISTORY_QUANTIZATION,
-                        sDiscreteHistoryQuantization);
-            }
-        } else {
-            sDiscreteHistoryQuantization = DEFAULT_DISCRETE_HISTORY_QUANTIZATION;
-        }
-        sDiscreteFlags = p.getKeyset().contains(PROPERTY_DISCRETE_FLAGS) ? sDiscreteFlags =
-                p.getInt(PROPERTY_DISCRETE_FLAGS, OP_FLAGS_DISCRETE) : OP_FLAGS_DISCRETE;
-        sDiscreteOps = p.getKeyset().contains(PROPERTY_DISCRETE_OPS_LIST) ? parseOpsList(
-                p.getString(PROPERTY_DISCRETE_OPS_LIST, DEFAULT_DISCRETE_OPS)) : parseOpsList(
-                DEFAULT_DISCRETE_OPS);
+    static File getDiscreteOpsDir() {
+        return new File(new File(Environment.getDataSystemDirectory(), "appops"), "discrete");
     }
 
     void recordDiscreteAccess(int uid, String packageName, @NonNull String deviceId, int op,
@@ -300,17 +162,9 @@
             @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId,
             @AccessType int accessType) {
         if (shouldLogAccess(op)) {
-            int firstChar = 0;
-            if (attributionTag != null && attributionTag.startsWith(packageName)) {
-                firstChar = packageName.length();
-                if (firstChar < attributionTag.length() && attributionTag.charAt(firstChar)
-                        == '.') {
-                    firstChar++;
-                }
-            }
             FrameworkStatsLog.write(FrameworkStatsLog.APP_OP_ACCESS_TRACKED, uid, op, accessType,
                     uidState, flags, attributionFlags,
-                    attributionTag == null ? null : attributionTag.substring(firstChar),
+                    getAttributionTag(attributionTag, packageName),
                     attributionChainId);
         }
 
@@ -331,7 +185,7 @@
         }
     }
 
-    void writeAndClearAccessHistory() {
+    void writeAndClearOldAccessHistory() {
         synchronized (mOnDiskLock) {
             if (mDiscreteAccessDir == null) {
                 Slog.d(TAG, "State not saved - persistence not initialized.");
@@ -350,6 +204,22 @@
         }
     }
 
+    void migrateSqliteData(DiscreteOps sqliteOps) {
+        synchronized (mOnDiskLock) {
+            if (mDiscreteAccessDir == null) {
+                Slog.d(TAG, "State not saved - persistence not initialized.");
+                return;
+            }
+            synchronized (mInMemoryLock) {
+                mDiscreteOps.mLargestChainId = sqliteOps.mLargestChainId;
+                mDiscreteOps.mChainIdOffset = sqliteOps.mChainIdOffset;
+            }
+            if (!sqliteOps.isEmpty()) {
+                persistDiscreteOpsLocked(sqliteOps);
+            }
+        }
+    }
+
     void addFilteredDiscreteOpsToHistoricalOps(AppOpsManager.HistoricalOps result,
             long beginTimeMillis, long endTimeMillis,
             @AppOpsManager.HistoricalOpsRequestFilter int filter, int uidFilter,
@@ -369,7 +239,7 @@
         discreteOps.applyToHistoricalOps(result, attributionChains);
     }
 
-    private int readLargestChainIdFromDiskLocked() {
+    int readLargestChainIdFromDiskLocked() {
         final File[] files = mDiscreteAccessDir.listFiles();
         if (files != null && files.length > 0) {
             File latestFile = null;
@@ -497,6 +367,13 @@
         }
     }
 
+    void deleteDiscreteOpsDir() {
+        synchronized (mOnDiskLock) {
+            mCachedOps = null;
+            FileUtils.deleteContentsAndDir(mDiscreteAccessDir);
+        }
+    }
+
     void clearHistory(int uid, String packageName) {
         synchronized (mOnDiskLock) {
             DiscreteOps discreteOps;
@@ -1506,26 +1383,6 @@
         }
     }
 
-    private static int[] parseOpsList(String opsList) {
-        String[] strArr;
-        if (opsList.isEmpty()) {
-            strArr = new String[0];
-        } else {
-            strArr = opsList.split(",");
-        }
-        int nOps = strArr.length;
-        int[] result = new int[nOps];
-        try {
-            for (int i = 0; i < nOps; i++) {
-                result[i] = Integer.parseInt(strArr[i]);
-            }
-        } catch (NumberFormatException e) {
-            Slog.e(TAG, "Failed to parse Discrete ops list: " + e.getMessage());
-            return parseOpsList(DEFAULT_DISCRETE_OPS);
-        }
-        return result;
-    }
-
     private static List<DiscreteOpEvent> stableListMerge(List<DiscreteOpEvent> a,
             List<DiscreteOpEvent> b) {
         int nA = a.size();
@@ -1570,34 +1427,4 @@
         }
         return result;
     }
-
-    private static boolean isDiscreteOp(int op, @AppOpsManager.OpFlags int flags) {
-        if (!ArrayUtils.contains(sDiscreteOps, op)) {
-            return false;
-        }
-        if ((flags & (sDiscreteFlags)) == 0) {
-            return false;
-        }
-        return true;
-    }
-
-    private static boolean shouldLogAccess(int op) {
-        return Flags.appopAccessTrackingLoggingEnabled()
-                && ArrayUtils.contains(sDiscreteOpsToLog, op);
-    }
-
-    private static long discretizeTimeStamp(long timeStamp) {
-        return timeStamp / sDiscreteHistoryQuantization * sDiscreteHistoryQuantization;
-
-    }
-
-    private static long discretizeDuration(long duration) {
-        return duration == -1 ? -1 : (duration + sDiscreteHistoryQuantization - 1)
-                / sDiscreteHistoryQuantization * sDiscreteHistoryQuantization;
-    }
-
-    void setDebugMode(boolean debugMode) {
-        this.mDebugMode = debugMode;
-    }
 }
-
diff --git a/services/core/java/com/android/server/appop/HistoricalRegistry.java b/services/core/java/com/android/server/appop/HistoricalRegistry.java
index 5e67f26..ba391d0 100644
--- a/services/core/java/com/android/server/appop/HistoricalRegistry.java
+++ b/services/core/java/com/android/server/appop/HistoricalRegistry.java
@@ -35,6 +35,7 @@
 import android.app.AppOpsManager.OpHistoryFlags;
 import android.app.AppOpsManager.UidState;
 import android.content.ContentResolver;
+import android.content.Context;
 import android.database.ContentObserver;
 import android.net.Uri;
 import android.os.Build;
@@ -45,6 +46,7 @@
 import android.os.Process;
 import android.os.RemoteCallback;
 import android.os.UserHandle;
+import android.permission.flags.Flags;
 import android.provider.Settings;
 import android.util.ArraySet;
 import android.util.LongSparseArray;
@@ -135,7 +137,7 @@
     private static final String PARAMETER_DELIMITER = ",";
     private static final String PARAMETER_ASSIGNMENT = "=";
 
-    private volatile @NonNull DiscreteRegistry mDiscreteRegistry;
+    private volatile @NonNull DiscreteOpsRegistry mDiscreteRegistry;
 
     @GuardedBy("mLock")
     private @NonNull LinkedList<HistoricalOps> mPendingWrites = new LinkedList<>();
@@ -196,13 +198,30 @@
     @GuardedBy("mOnDiskLock")
     private Persistence mPersistence;
 
-    HistoricalRegistry(@NonNull Object lock) {
+    private final Context mContext;
+
+    HistoricalRegistry(@NonNull Object lock, Context context) {
         mInMemoryLock = lock;
-        mDiscreteRegistry = new DiscreteRegistry(lock);
+        mContext = context;
+        if (Flags.enableSqliteAppopsAccesses()) {
+            mDiscreteRegistry = new DiscreteOpsSqlRegistry(context);
+            if (DiscreteOpsXmlRegistry.getDiscreteOpsDir().exists()) {
+                DiscreteOpsSqlRegistry sqlRegistry = (DiscreteOpsSqlRegistry) mDiscreteRegistry;
+                DiscreteOpsXmlRegistry xmlRegistry = new DiscreteOpsXmlRegistry(context);
+                DiscreteOpsMigrationHelper.migrateDiscreteOpsToSqlite(xmlRegistry, sqlRegistry);
+            }
+        } else {
+            mDiscreteRegistry = new DiscreteOpsXmlRegistry(context);
+            if (DiscreteOpsDbHelper.getDatabaseFile().exists()) { // roll-back sqlite
+                DiscreteOpsSqlRegistry sqlRegistry = new DiscreteOpsSqlRegistry(context);
+                DiscreteOpsXmlRegistry xmlRegistry = (DiscreteOpsXmlRegistry) mDiscreteRegistry;
+                DiscreteOpsMigrationHelper.migrateDiscreteOpsToXml(sqlRegistry, xmlRegistry);
+            }
+        }
     }
 
     HistoricalRegistry(@NonNull HistoricalRegistry other) {
-        this(other.mInMemoryLock);
+        this(other.mInMemoryLock, other.mContext);
         mMode = other.mMode;
         mBaseSnapshotInterval = other.mBaseSnapshotInterval;
         mIntervalCompressionMultiplier = other.mIntervalCompressionMultiplier;
@@ -475,7 +494,7 @@
             @NonNull String deviceId, @Nullable String attributionTag, @UidState int uidState,
             @OpFlags int flags, long accessTime,
             @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId,
-            @DiscreteRegistry.AccessType int accessType, int accessCount) {
+            @DiscreteOpsRegistry.AccessType int accessType, int accessCount) {
         synchronized (mInMemoryLock) {
             if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) {
                 if (!isPersistenceInitializedMLocked()) {
@@ -512,7 +531,7 @@
             @NonNull String deviceId, @Nullable String attributionTag, @UidState int uidState,
             @OpFlags int flags, long eventStartTime, long increment,
             @AppOpsManager.AttributionFlags int attributionFlags, int attributionChainId,
-            @DiscreteRegistry.AccessType int accessType) {
+            @DiscreteOpsRegistry.AccessType int accessType) {
         synchronized (mInMemoryLock) {
             if (mMode == AppOpsManager.HISTORICAL_MODE_ENABLED_ACTIVE) {
                 if (!isPersistenceInitializedMLocked()) {
@@ -648,7 +667,7 @@
     }
 
     void writeAndClearDiscreteHistory() {
-        mDiscreteRegistry.writeAndClearAccessHistory();
+        mDiscreteRegistry.writeAndClearOldAccessHistory();
     }
 
     void clearAllHistory() {
@@ -743,7 +762,7 @@
             }
             persistPendingHistory(pendingWrites);
         }
-        mDiscreteRegistry.writeAndClearAccessHistory();
+        mDiscreteRegistry.writeAndClearOldAccessHistory();
     }
 
     private void persistPendingHistory(@NonNull List<HistoricalOps> pendingWrites) {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 0fd4716..3f6484f 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -242,6 +242,7 @@
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
+import android.util.SystemPropertySetter;
 import android.view.Display;
 import android.view.KeyEvent;
 import android.view.accessibility.AccessibilityManager;
@@ -8570,6 +8571,12 @@
         return true;
     }
 
+    private boolean shouldPreserveVolume(boolean userSwitch, VolumeGroupState vgs) {
+        // as for STREAM_MUSIC, preserve volume from one user to the next except
+        // Android Automotive platform
+        return (userSwitch && vgs.isMusic()) && !isPlatformAutomotive();
+    }
+
     private void readVolumeGroupsSettings(boolean userSwitch) {
         synchronized (mSettingsLock) {
             synchronized (VolumeStreamState.class) {
@@ -8578,8 +8585,7 @@
                 }
                 for (int i = 0; i < sVolumeGroupStates.size(); i++) {
                     VolumeGroupState vgs = sVolumeGroupStates.valueAt(i);
-                    // as for STREAM_MUSIC, preserve volume from one user to the next.
-                    if (!(userSwitch && vgs.isMusic())) {
+                    if (!shouldPreserveVolume(userSwitch, vgs)) {
                         vgs.clearIndexCache();
                         vgs.readSettings();
                     }
@@ -9018,6 +9024,11 @@
             mIndexMap.clear();
         }
 
+        private @UserIdInt int getVolumePersistenceUserId() {
+            return isMusic() && !isPlatformAutomotive()
+                    ? UserHandle.USER_SYSTEM : UserHandle.USER_CURRENT;
+        }
+
         private void persistVolumeGroup(int device) {
             // No need to persist the index if the volume group is backed up
             // by a public stream type as this is redundant
@@ -9035,7 +9046,7 @@
             boolean success = mSettings.putSystemIntForUser(mContentResolver,
                     getSettingNameForDevice(device),
                     getIndex(device),
-                    isMusic() ? UserHandle.USER_SYSTEM : UserHandle.USER_CURRENT);
+                    getVolumePersistenceUserId());
             if (!success) {
                 Log.e(TAG, "persistVolumeGroup failed for group " +  mAudioVolumeGroup.name());
             }
@@ -9058,7 +9069,7 @@
                     String name = getSettingNameForDevice(device);
                     index = mSettings.getSystemIntForUser(
                             mContentResolver, name, defaultIndex,
-                            isMusic() ? UserHandle.USER_SYSTEM : UserHandle.USER_CURRENT);
+                            getVolumePersistenceUserId());
                     if (index == -1) {
                         continue;
                     }
@@ -11032,7 +11043,7 @@
         @GuardedBy("mLock")
         private void updateLocked() {
             String n = Long.toString(mToken++);
-            SystemProperties.set(PermissionManager.CACHE_KEY_PACKAGE_INFO_NOTIFY, n);
+            SystemPropertySetter.setWithRetry(PermissionManager.CACHE_KEY_PACKAGE_INFO_NOTIFY, n);
         }
 
         private void trigger() {
diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java
index e89f43b..20c3327 100644
--- a/services/core/java/com/android/server/compat/CompatConfig.java
+++ b/services/core/java/com/android/server/compat/CompatConfig.java
@@ -876,7 +876,28 @@
     }
 
     @Nullable
+    @android.ravenwood.annotation.RavenwoodReplace(
+            blockedBy = PackageManager.class,
+            reason = "PackageManager.getApplicationInfo() isn't supported yet")
     private Long getVersionCodeOrNull(String packageName) {
+        return getVersionCodeOrNullImpl(packageName);
+    }
+
+    @SuppressWarnings("unused")
+    @Nullable
+    private Long getVersionCodeOrNull$ravenwood(String packageName) {
+        try {
+            // It's possible that the context is mocked, try the real method first
+            return getVersionCodeOrNullImpl(packageName);
+        } catch (Throwable e) {
+            // For now, Ravenwood doesn't support the concept of "app updates", so let's
+            // just use a fixed version code for all packages.
+            return 1L;
+        }
+    }
+
+    @Nullable
+    private Long getVersionCodeOrNullImpl(String packageName) {
         try {
             ApplicationInfo applicationInfo = mContext.getPackageManager().getApplicationInfo(
                     packageName, MATCH_ANY_USER);
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
index 5d9db65..d89db8d 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -312,6 +312,13 @@
                 mProcessObserver);
     }
 
+    @Override
+    public void onBootPhase(int phase) {
+        if (phase == PHASE_SYSTEM_SERVICES_READY) {
+            mDeviceStatePolicy.getDeviceStateProvider().onSystemReady();
+        }
+    }
+
     @VisibleForTesting
     Handler getHandler() {
         return mHandler;
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateProvider.java b/services/core/java/com/android/server/devicestate/DeviceStateProvider.java
index 8d07609..8a8ebc2 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateProvider.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateProvider.java
@@ -91,6 +91,11 @@
     @interface SupportedStatesUpdatedReason {}
 
     /**
+     * Called when the system boot phase advances to PHASE_SYSTEM_SERVICES_READY.
+     */
+    default void onSystemReady() {};
+
+    /**
      * Registers a listener for changes in provider state.
      * <p>
      * It is <b>required</b> that
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 805357e..88f5c81 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -57,6 +57,7 @@
 import com.android.internal.os.BackgroundThread;
 import com.android.server.EventLogTags;
 import com.android.server.display.brightness.BrightnessEvent;
+import com.android.server.display.brightness.clamper.BrightnessClamperController;
 import com.android.server.display.config.HysteresisLevels;
 import com.android.server.display.feature.DisplayManagerFlags;
 
@@ -265,7 +266,7 @@
     private final BrightnessRangeController mBrightnessRangeController;
 
     // Throttles (caps) maximum allowed brightness
-    private final BrightnessThrottler mBrightnessThrottler;
+    private final BrightnessClamperController mBrightnessClamperController;
     private boolean mIsBrightnessThrottled;
 
     // Context-sensitive brightness configurations require keeping track of the foreground app's
@@ -299,7 +300,7 @@
             HysteresisLevels ambientBrightnessThresholdsIdle,
             HysteresisLevels screenBrightnessThresholdsIdle, Context context,
             BrightnessRangeController brightnessModeController,
-            BrightnessThrottler brightnessThrottler, int ambientLightHorizonShort,
+            BrightnessClamperController clamperController, int ambientLightHorizonShort,
             int ambientLightHorizonLong, float userLux, float userNits,
             DisplayManagerFlags displayManagerFlags) {
         this(new Injector(), callbacks, looper, sensorManager, lightSensor,
@@ -310,7 +311,7 @@
                 resetAmbientLuxAfterWarmUpConfig, ambientBrightnessThresholds,
                 screenBrightnessThresholds, ambientBrightnessThresholdsIdle,
                 screenBrightnessThresholdsIdle, context, brightnessModeController,
-                brightnessThrottler, ambientLightHorizonShort, ambientLightHorizonLong, userLux,
+                clamperController, ambientLightHorizonShort, ambientLightHorizonLong, userLux,
                 userNits, displayManagerFlags
         );
     }
@@ -328,7 +329,7 @@
             HysteresisLevels ambientBrightnessThresholdsIdle,
             HysteresisLevels screenBrightnessThresholdsIdle, Context context,
             BrightnessRangeController brightnessRangeController,
-            BrightnessThrottler brightnessThrottler, int ambientLightHorizonShort,
+            BrightnessClamperController clamperController, int ambientLightHorizonShort,
             int ambientLightHorizonLong, float userLux, float userNits,
             DisplayManagerFlags displayManagerFlags) {
         mInjector = injector;
@@ -374,7 +375,7 @@
         mForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
         mPendingForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
         mBrightnessRangeController = brightnessRangeController;
-        mBrightnessThrottler = brightnessThrottler;
+        mBrightnessClamperController = clamperController;
         mBrightnessMappingStrategyMap = brightnessMappingStrategyMap;
         mDisplayManagerFlags = displayManagerFlags;
 
@@ -472,9 +473,10 @@
         }
         changed |= setLightSensorEnabled(enable);
 
-        if (mIsBrightnessThrottled != mBrightnessThrottler.isThrottled()) {
+        boolean isBrightnessThrottled = mBrightnessClamperController.isThrottled();
+        if (mIsBrightnessThrottled != isBrightnessThrottled) {
             // Maximum brightness has changed, so recalculate display brightness.
-            mIsBrightnessThrottled = mBrightnessThrottler.isThrottled();
+            mIsBrightnessThrottled = isBrightnessThrottled;
             changed = true;
         }
 
@@ -1038,10 +1040,9 @@
 
     // Clamps values with float range [0.0-1.0]
     private float clampScreenBrightness(float value) {
-        final float minBrightness = Math.min(mBrightnessRangeController.getCurrentBrightnessMin(),
-                mBrightnessThrottler.getBrightnessCap());
+        final float minBrightness = mBrightnessRangeController.getCurrentBrightnessMin();
         final float maxBrightness = Math.min(mBrightnessRangeController.getCurrentBrightnessMax(),
-                mBrightnessThrottler.getBrightnessCap());
+                mBrightnessClamperController.getMaxBrightness());
         return MathUtils.constrain(value, minBrightness, maxBrightness);
     }
 
diff --git a/services/core/java/com/android/server/display/BrightnessThrottler.java b/services/core/java/com/android/server/display/BrightnessThrottler.java
deleted file mode 100644
index b56a234..0000000
--- a/services/core/java/com/android/server/display/BrightnessThrottler.java
+++ /dev/null
@@ -1,476 +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 static com.android.server.display.DisplayDeviceConfig.DEFAULT_ID;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.Context;
-import android.hardware.display.BrightnessInfo;
-import android.os.Handler;
-import android.os.HandlerExecutor;
-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.DeviceConfig;
-import android.provider.DeviceConfigInterface;
-import android.util.Slog;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData;
-import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData.ThrottlingLevel;
-import com.android.server.display.config.SensorData;
-import com.android.server.display.feature.DeviceConfigParameterProvider;
-import com.android.server.display.utils.DebugUtils;
-import com.android.server.display.utils.DeviceConfigParsingUtils;
-import com.android.server.display.utils.SensorUtils;
-
-import java.io.PrintWriter;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.Executor;
-import java.util.function.BiFunction;
-import java.util.function.Function;
-
-/**
- * This class monitors various conditions, such as skin temperature throttling status, and limits
- * the allowed brightness range accordingly.
- *
- * @deprecated will be replaced by
- * {@link com.android.server.display.brightness.clamper.BrightnessThermalClamper}
- */
-@Deprecated
-class BrightnessThrottler {
-    private static final String TAG = "BrightnessThrottler";
-
-    // To enable these logs, run:
-    // 'adb shell setprop persist.log.tag.BrightnessThrottler DEBUG && adb reboot'
-    private static final boolean DEBUG = DebugUtils.isDebuggable(TAG);
-    private static final int THROTTLING_INVALID = -1;
-
-    private final Injector mInjector;
-    private final Handler mHandler;
-    // We need a separate handler for unit testing. These two handlers are the same throughout the
-    // non-test code.
-    private final Handler mDeviceConfigHandler;
-    private final Runnable mThrottlingChangeCallback;
-    private final SkinThermalStatusObserver mSkinThermalStatusObserver;
-    private final DeviceConfigListener mDeviceConfigListener;
-    private final DeviceConfigParameterProvider mConfigParameterProvider;
-
-    private int mThrottlingStatus;
-
-    // Maps the throttling ID to the data. Sourced from DisplayDeviceConfig.
-    @NonNull
-    private Map<String, ThermalBrightnessThrottlingData> mDdcThermalThrottlingDataMap;
-
-    // Current throttling data being used.
-    // Null if we do not support throttling.
-    @Nullable
-    private ThermalBrightnessThrottlingData mThermalThrottlingData;
-
-    private float mBrightnessCap = PowerManager.BRIGHTNESS_MAX;
-    private @BrightnessInfo.BrightnessMaxReason int mBrightnessMaxReason =
-        BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE;
-    private String mUniqueDisplayId;
-
-    // The most recent string that has been set from DeviceConfig
-    private String mThermalBrightnessThrottlingDataString;
-
-    // The brightness throttling configuration that should be used.
-    private String mThermalBrightnessThrottlingDataId;
-
-    // Temperature Sensor to be monitored for throttling.
-    @NonNull
-    private SensorData mTempSensor;
-
-    // This is a collection of brightness throttling data that has been written as overrides from
-    // the DeviceConfig. This will always take priority over the display device config data.
-    // We need to store the data for every display device, so we do not need to update this each
-    // time the underlying display device changes.
-    // This map is indexed by uniqueDisplayId, to provide maps for throttlingId -> throttlingData.
-    // HashMap< uniqueDisplayId, HashMap< throttlingDataId, ThermalBrightnessThrottlingData >>
-    private final Map<String, Map<String, ThermalBrightnessThrottlingData>>
-            mThermalBrightnessThrottlingDataOverride = new HashMap<>();
-
-    private final BiFunction<String, String, ThrottlingLevel> mDataPointMapper = (key, value) -> {
-        try {
-            int status = DeviceConfigParsingUtils.parseThermalStatus(key);
-            float brightnessPoint = DeviceConfigParsingUtils.parseBrightness(value);
-            return new ThrottlingLevel(status, brightnessPoint);
-        } catch (IllegalArgumentException iae) {
-            return null;
-        }
-    };
-
-    private final Function<List<ThrottlingLevel>, ThermalBrightnessThrottlingData>
-            mDataSetMapper = ThermalBrightnessThrottlingData::create;
-
-    BrightnessThrottler(Handler handler, Runnable throttlingChangeCallback, String uniqueDisplayId,
-            String throttlingDataId,
-            @NonNull DisplayDeviceConfig displayDeviceConfig) {
-        this(new Injector(), handler, handler, throttlingChangeCallback, uniqueDisplayId,
-                throttlingDataId,
-                displayDeviceConfig.getThermalBrightnessThrottlingDataMapByThrottlingId(),
-                displayDeviceConfig.getTempSensor());
-    }
-
-    @VisibleForTesting
-    BrightnessThrottler(Injector injector, Handler handler, Handler deviceConfigHandler,
-            Runnable throttlingChangeCallback, String uniqueDisplayId, String throttlingDataId,
-            @NonNull Map<String, ThermalBrightnessThrottlingData>
-                    thermalBrightnessThrottlingDataMap,
-            @NonNull SensorData tempSensor) {
-        mInjector = injector;
-
-        mHandler = handler;
-        mDeviceConfigHandler = deviceConfigHandler;
-        mDdcThermalThrottlingDataMap = thermalBrightnessThrottlingDataMap;
-        mThrottlingChangeCallback = throttlingChangeCallback;
-        mSkinThermalStatusObserver = new SkinThermalStatusObserver(mInjector, mHandler);
-
-        mUniqueDisplayId = uniqueDisplayId;
-        mConfigParameterProvider = new DeviceConfigParameterProvider(injector.getDeviceConfig());
-        mDeviceConfigListener = new DeviceConfigListener();
-        mThermalBrightnessThrottlingDataId = throttlingDataId;
-        mDdcThermalThrottlingDataMap = thermalBrightnessThrottlingDataMap;
-        loadThermalBrightnessThrottlingDataFromDeviceConfig();
-        loadThermalBrightnessThrottlingDataFromDisplayDeviceConfig(mDdcThermalThrottlingDataMap,
-                tempSensor, mThermalBrightnessThrottlingDataId, mUniqueDisplayId);
-    }
-
-    boolean deviceSupportsThrottling() {
-        return mThermalThrottlingData != null;
-    }
-
-    float getBrightnessCap() {
-        return mBrightnessCap;
-    }
-
-    int getBrightnessMaxReason() {
-        return mBrightnessMaxReason;
-    }
-
-    boolean isThrottled() {
-        return mBrightnessMaxReason != BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE;
-    }
-
-    void stop() {
-        mSkinThermalStatusObserver.stopObserving();
-        mConfigParameterProvider.removeOnPropertiesChangedListener(mDeviceConfigListener);
-        // We're asked to stop throttling, so reset brightness restrictions.
-        mBrightnessCap = PowerManager.BRIGHTNESS_MAX;
-        mBrightnessMaxReason = BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE;
-
-        // We set throttling status to an invalid value here so that we act on the first throttling
-        // value received from the thermal service after registration, even if that throttling value
-        // is THROTTLING_NONE.
-        mThrottlingStatus = THROTTLING_INVALID;
-    }
-
-    void loadThermalBrightnessThrottlingDataFromDisplayDeviceConfig(
-            Map<String, ThermalBrightnessThrottlingData> ddcThrottlingDataMap,
-            SensorData tempSensor,
-            String brightnessThrottlingDataId,
-            String uniqueDisplayId) {
-        mDdcThermalThrottlingDataMap = ddcThrottlingDataMap;
-        mThermalBrightnessThrottlingDataId = brightnessThrottlingDataId;
-        mUniqueDisplayId = uniqueDisplayId;
-        mTempSensor = tempSensor;
-        resetThermalThrottlingData();
-    }
-
-    private float verifyAndConstrainBrightnessCap(float brightness) {
-        if (brightness < PowerManager.BRIGHTNESS_MIN) {
-            Slog.e(TAG, "brightness " + brightness + " is lower than the minimum possible "
-                    + "brightness " + PowerManager.BRIGHTNESS_MIN);
-            brightness = PowerManager.BRIGHTNESS_MIN;
-        }
-
-        if (brightness > PowerManager.BRIGHTNESS_MAX) {
-            Slog.e(TAG, "brightness " + brightness + " is higher than the maximum possible "
-                    + "brightness " + PowerManager.BRIGHTNESS_MAX);
-            brightness = PowerManager.BRIGHTNESS_MAX;
-        }
-
-        return brightness;
-    }
-
-    private void thermalStatusChanged(@Temperature.ThrottlingStatus int newStatus) {
-        if (mThrottlingStatus != newStatus) {
-            mThrottlingStatus = newStatus;
-            updateThermalThrottling();
-        }
-    }
-
-    private void updateThermalThrottling() {
-        if (!deviceSupportsThrottling()) {
-            return;
-        }
-
-        float brightnessCap = PowerManager.BRIGHTNESS_MAX;
-        int brightnessMaxReason = BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE;
-
-        if (mThrottlingStatus != THROTTLING_INVALID && mThermalThrottlingData != null) {
-            // Throttling levels are sorted by increasing severity
-            for (ThrottlingLevel level : mThermalThrottlingData.throttlingLevels) {
-                if (level.thermalStatus <= mThrottlingStatus) {
-                    brightnessCap = level.brightness;
-                    brightnessMaxReason = BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL;
-                } else {
-                    // Throttling levels that are greater than the current status are irrelevant
-                    break;
-                }
-            }
-        }
-
-        if (mBrightnessCap != brightnessCap || mBrightnessMaxReason != brightnessMaxReason) {
-            mBrightnessCap = verifyAndConstrainBrightnessCap(brightnessCap);
-            mBrightnessMaxReason = brightnessMaxReason;
-
-            if (DEBUG) {
-                Slog.d(TAG, "State changed: mBrightnessCap = " + mBrightnessCap
-                        + ", mBrightnessMaxReason = "
-                        + BrightnessInfo.briMaxReasonToString(mBrightnessMaxReason));
-            }
-
-            if (mThrottlingChangeCallback != null) {
-                mThrottlingChangeCallback.run();
-            }
-        }
-    }
-
-    void dump(PrintWriter pw) {
-        mHandler.runWithScissors(() -> dumpLocal(pw), 1000);
-    }
-
-    private void dumpLocal(PrintWriter pw) {
-        pw.println("BrightnessThrottler:");
-        pw.println("--------------------");
-        pw.println("  mThermalBrightnessThrottlingDataId=" + mThermalBrightnessThrottlingDataId);
-        pw.println("  mThermalThrottlingData=" + mThermalThrottlingData);
-        pw.println("  mUniqueDisplayId=" + mUniqueDisplayId);
-        pw.println("  mThrottlingStatus=" + mThrottlingStatus);
-        pw.println("  mBrightnessCap=" + mBrightnessCap);
-        pw.println("  mBrightnessMaxReason=" +
-            BrightnessInfo.briMaxReasonToString(mBrightnessMaxReason));
-        pw.println("  mDdcThermalThrottlingDataMap=" + mDdcThermalThrottlingDataMap);
-        pw.println("  mThermalBrightnessThrottlingDataOverride="
-                + mThermalBrightnessThrottlingDataOverride);
-        pw.println("  mThermalBrightnessThrottlingDataString="
-                + mThermalBrightnessThrottlingDataString);
-
-        mSkinThermalStatusObserver.dump(pw);
-    }
-
-    // The brightness throttling data id may or may not be specified in the string that is passed
-    // in, if there is none specified, we assume it is for the default case. Each string passed in
-    // here must be for one display and one throttling id.
-    // 123,1,critical,0.8
-    // 456,2,moderate,0.9,critical,0.7
-    // 456,2,moderate,0.9,critical,0.7,default
-    // 456,2,moderate,0.9,critical,0.7,id_2
-    // displayId, number, <state, val> * number
-    // displayId, <number, <state, val> * number>, throttlingId
-    private void loadThermalBrightnessThrottlingDataFromDeviceConfig() {
-        mThermalBrightnessThrottlingDataString =
-                mConfigParameterProvider.getBrightnessThrottlingData();
-        mThermalBrightnessThrottlingDataOverride.clear();
-        if (mThermalBrightnessThrottlingDataString != null) {
-            Map<String, Map<String, ThermalBrightnessThrottlingData>> tempThrottlingData =
-                    DeviceConfigParsingUtils.parseDeviceConfigMap(
-                    mThermalBrightnessThrottlingDataString, mDataPointMapper, mDataSetMapper);
-            mThermalBrightnessThrottlingDataOverride.putAll(tempThrottlingData);
-        } else {
-            Slog.w(TAG, "DeviceConfig ThermalBrightnessThrottlingData is null");
-        }
-    }
-
-    private void resetThermalThrottlingData() {
-        stop();
-
-        mDeviceConfigListener.startListening();
-
-        // Get throttling data for this id, if it exists
-        mThermalThrottlingData = getConfigFromId(mThermalBrightnessThrottlingDataId);
-
-        // Fallback to default id otherwise.
-        if (!DEFAULT_ID.equals(mThermalBrightnessThrottlingDataId)
-                && mThermalThrottlingData == null) {
-            mThermalThrottlingData = getConfigFromId(DEFAULT_ID);
-            Slog.d(TAG, "Falling back to default throttling Id");
-        }
-
-        if (deviceSupportsThrottling()) {
-            mSkinThermalStatusObserver.startObserving(mTempSensor);
-        }
-    }
-
-    private ThermalBrightnessThrottlingData getConfigFromId(String id) {
-        ThermalBrightnessThrottlingData returnValue;
-
-        // Fallback pattern for fetching correct throttling data for this display and id.
-        // 1) throttling data from device config for this throttling data id
-        returnValue =  mThermalBrightnessThrottlingDataOverride.get(mUniqueDisplayId) == null
-                ? null
-                : mThermalBrightnessThrottlingDataOverride.get(mUniqueDisplayId).get(id);
-        // 2) throttling data from ddc for this throttling data id
-        returnValue = returnValue == null
-                ? mDdcThermalThrottlingDataMap.get(id)
-                : returnValue;
-
-        return returnValue;
-    }
-
-    /**
-     * Listens to config data change and updates the brightness throttling data using
-     * DisplayManager#KEY_BRIGHTNESS_THROTTLING_DATA.
-     * The format should be a string similar to: "local:4619827677550801152,2,moderate,0.5,severe,
-     * 0.379518072;local:4619827677550801151,1,moderate,0.75"
-     * In this order:
-     * <displayId>,<no of throttling levels>,[<severity as string>,<brightness cap>][,throttlingId]?
-     * Where [<severity as string>,<brightness cap>] is repeated for each throttling level, and the
-     * entirety is repeated for each display & throttling data id, separated by a semicolon.
-     */
-    public class DeviceConfigListener implements DeviceConfig.OnPropertiesChangedListener {
-        public Executor mExecutor = new HandlerExecutor(mDeviceConfigHandler);
-
-        public void startListening() {
-            mConfigParameterProvider.addOnPropertiesChangedListener(mExecutor, this);
-        }
-
-        @Override
-        public void onPropertiesChanged(DeviceConfig.Properties properties) {
-            loadThermalBrightnessThrottlingDataFromDeviceConfig();
-            resetThermalThrottlingData();
-        }
-    }
-
-    private final class SkinThermalStatusObserver extends IThermalEventListener.Stub {
-        private final Injector mInjector;
-        private final Handler mHandler;
-        private SensorData mObserverTempSensor;
-
-        private IThermalService mThermalService;
-        private boolean mStarted;
-
-        SkinThermalStatusObserver(Injector injector, Handler handler) {
-            mInjector = injector;
-            mHandler = handler;
-        }
-
-        @Override
-        public void notifyThrottling(Temperature temp) {
-            if (DEBUG) {
-                Slog.d(TAG, "New thermal throttling status = " + temp.getStatus());
-            }
-
-            if (mObserverTempSensor.name != null
-                    && !mObserverTempSensor.name.equals(temp.getName())) {
-                Slog.i(TAG, "Skipping thermal throttling notification as monitored sensor: "
-                            + mObserverTempSensor.name
-                            + " != notified sensor: "
-                            + temp.getName());
-                return;
-            }
-            mHandler.post(() -> {
-                final @Temperature.ThrottlingStatus int status = temp.getStatus();
-                thermalStatusChanged(status);
-            });
-        }
-
-        void startObserving(SensorData tempSensor) {
-            if (!mStarted || mObserverTempSensor == null) {
-                mObserverTempSensor = tempSensor;
-                registerThermalListener();
-                return;
-            }
-
-            String curType = mObserverTempSensor.type;
-            mObserverTempSensor = tempSensor;
-            if (curType.equals(tempSensor.type)) {
-                if (DEBUG) {
-                    Slog.d(TAG, "Thermal status observer already started");
-                }
-                return;
-            }
-            stopObserving();
-            registerThermalListener();
-        }
-
-        void registerThermalListener() {
-            mThermalService = mInjector.getThermalService();
-            if (mThermalService == null) {
-                Slog.e(TAG, "Could not observe thermal status. Service not available");
-                return;
-            }
-            int temperatureType = SensorUtils.getSensorTemperatureType(mObserverTempSensor);
-            try {
-                // We get a callback immediately upon registering so there's no need to query
-                // for the current value.
-                mThermalService.registerThermalEventListenerWithType(this, temperatureType);
-                mStarted = true;
-            } catch (RemoteException e) {
-                Slog.e(TAG, "Failed to register thermal status listener", e);
-            }
-        }
-
-        void stopObserving() {
-            if (!mStarted) {
-                if (DEBUG) {
-                    Slog.d(TAG, "Stop skipped because thermal status observer not started");
-                }
-                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("  SkinThermalStatusObserver:");
-            writer.println("    mStarted: " + mStarted);
-            writer.println("    mObserverTempSensor: " + mObserverTempSensor);
-            if (mThermalService != null) {
-                writer.println("    ThermalService available");
-            } else {
-                writer.println("    ThermalService not available");
-            }
-        }
-    }
-
-    public static class Injector {
-        public IThermalService getThermalService() {
-            return IThermalService.Stub.asInterface(
-                    ServiceManager.getService(Context.THERMAL_SERVICE));
-        }
-
-        @NonNull
-        public DeviceConfigInterface getDeviceConfig() {
-            return DeviceConfigInterface.REAL;
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index c8192e5..b530da2 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -2246,10 +2246,6 @@
 
     @GuardedBy("mSyncRoot")
     private void handleLogicalDisplayDisconnectedLocked(LogicalDisplay display) {
-        if (!mFlags.isConnectedDisplayManagementEnabled()) {
-            Slog.e(TAG, "DisplayDisconnected shouldn't be received when the flag is off");
-            return;
-        }
         releaseDisplayAndEmitEvent(display, DisplayManagerGlobal.EVENT_DISPLAY_DISCONNECTED);
         mExternalDisplayPolicy.handleLogicalDisplayDisconnectedLocked(display);
     }
@@ -2315,11 +2311,6 @@
 
     @SuppressLint("AndroidFrameworkRequiresPermission")
     private void handleLogicalDisplayConnectedLocked(LogicalDisplay display) {
-        if (!mFlags.isConnectedDisplayManagementEnabled()) {
-            Slog.e(TAG, "DisplayConnected shouldn't be received when the flag is off");
-            return;
-        }
-
         setupLogicalDisplay(display);
 
         if (ExternalDisplayPolicy.isExternalDisplayLocked(display)) {
@@ -2346,9 +2337,6 @@
     private void handleLogicalDisplayAddedLocked(LogicalDisplay display) {
         final int displayId = display.getDisplayIdLocked();
         final boolean isDefault = displayId == Display.DEFAULT_DISPLAY;
-        if (!mFlags.isConnectedDisplayManagementEnabled()) {
-            setupLogicalDisplay(display);
-        }
 
         // Wake up waitForDefaultDisplay.
         if (isDefault) {
@@ -2443,21 +2431,17 @@
     }
 
     private void handleLogicalDisplayRemovedLocked(@NonNull LogicalDisplay display) {
-        // With display management, the display is removed when disabled, and it might still exist.
+        // The display is removed when disabled, and it might still exist.
         // Resources must only be released when the disconnected signal is received.
-        if (mFlags.isConnectedDisplayManagementEnabled()) {
-            if (display.isValidLocked()) {
-                updateViewportPowerStateLocked(display);
-            }
+        if (display.isValidLocked()) {
+            updateViewportPowerStateLocked(display);
+        }
 
-            // Note: This method is only called if the display was enabled before being removed.
-            sendDisplayEventLocked(display, DisplayManagerGlobal.EVENT_DISPLAY_REMOVED);
+        // Note: This method is only called if the display was enabled before being removed.
+        sendDisplayEventLocked(display, DisplayManagerGlobal.EVENT_DISPLAY_REMOVED);
 
-            if (display.isValidLocked()) {
-                applyDisplayChangedLocked(display);
-            }
-        } else {
-            releaseDisplayAndEmitEvent(display, DisplayManagerGlobal.EVENT_DISPLAY_REMOVED);
+        if (display.isValidLocked()) {
+            applyDisplayChangedLocked(display);
         }
         if (mDisplayTopologyCoordinator != null) {
             mDisplayTopologyCoordinator.onDisplayRemoved(display.getDisplayIdLocked());
@@ -4565,13 +4549,11 @@
             final int callingPid = Binder.getCallingPid();
             final int callingUid = Binder.getCallingUid();
 
-            if (mFlags.isConnectedDisplayManagementEnabled()) {
-                if ((internalEventFlagsMask
-                        & DisplayManagerGlobal
-                        .INTERNAL_EVENT_FLAG_DISPLAY_CONNECTION_CHANGED) != 0) {
-                    mContext.enforceCallingOrSelfPermission(MANAGE_DISPLAYS,
-                            "Permission required to get signals about connection events.");
-                }
+            if ((internalEventFlagsMask
+                    & DisplayManagerGlobal
+                    .INTERNAL_EVENT_FLAG_DISPLAY_CONNECTION_CHANGED) != 0) {
+                mContext.enforceCallingOrSelfPermission(MANAGE_DISPLAYS,
+                        "Permission required to get signals about connection events.");
             }
 
             final long token = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
index e46397b..f6b2591 100644
--- a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
+++ b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
@@ -179,12 +179,10 @@
         pw.println("    Sets brightness to docked + idle screen brightness mode");
         pw.println("  undock");
         pw.println("    Sets brightness to active (normal) screen brightness mode");
-        if (mFlags.isConnectedDisplayManagementEnabled()) {
-            pw.println("  enable-display DISPLAY_ID");
-            pw.println("    Enable the DISPLAY_ID. Only possible if this is a connected display.");
-            pw.println("  disable-display DISPLAY_ID");
-            pw.println("    Disable the DISPLAY_ID. Only possible if this is a connected display.");
-        }
+        pw.println("  enable-display DISPLAY_ID");
+        pw.println("    Enable the DISPLAY_ID. Only possible if this is a connected display.");
+        pw.println("  disable-display DISPLAY_ID");
+        pw.println("    Disable the DISPLAY_ID. Only possible if this is a connected display.");
         pw.println("  power-reset DISPLAY_ID");
         pw.println("    Turn the DISPLAY_ID power to a state the display supposed to have.");
         pw.println("  power-off DISPLAY_ID");
@@ -601,11 +599,6 @@
     }
 
     private int setDisplayEnabled(boolean enable) {
-        if (!mFlags.isConnectedDisplayManagementEnabled()) {
-            getErrPrintWriter()
-                    .println("Error: external display management is not available on this device.");
-            return 1;
-        }
         final String displayIdText = getNextArg();
         if (displayIdText == null) {
             getErrPrintWriter().println("Error: no displayId specified");
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 92f5cab..49aedf5 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -104,7 +104,6 @@
 import com.android.server.policy.WindowManagerPolicy;
 
 import java.io.PrintWriter;
-import java.util.Objects;
 
 /**
  * Controls the power state of the display.
@@ -416,8 +415,6 @@
 
     private final BrightnessRangeController mBrightnessRangeController;
 
-    private final BrightnessThrottler mBrightnessThrottler;
-
     private final BrightnessClamperController mBrightnessClamperController;
 
     private final Runnable mOnBrightnessChangeRunnable;
@@ -482,9 +479,6 @@
     private boolean mIsInTransition;
     private boolean mIsDisplayInternal;
 
-    // The id of the thermal brightness throttling policy that should be used.
-    private String mThermalBrightnessThrottlingDataId;
-
     // 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")
@@ -546,8 +540,6 @@
             mDisplayPowerProximityStateController,
             resources.getBoolean(R.bool.config_skipScreenOffTransition));
         mTag = TAG + "[" + mDisplayId + "]";
-        mThermalBrightnessThrottlingDataId =
-                logicalDisplay.getDisplayInfoLocked().thermalBrightnessThrottlingDataId;
 
         mUniqueDisplayId = mDisplayDevice.getUniqueId();
         mDisplayStatsId = mUniqueDisplayId.hashCode();
@@ -598,7 +590,6 @@
 
         HighBrightnessModeController hbmController = createHbmControllerLocked(hbmMetadata,
                 modeChangeCallback);
-        mBrightnessThrottler = createBrightnessThrottlerLocked();
 
         mBrightnessRangeController = mInjector.getBrightnessRangeController(hbmController,
                 modeChangeCallback, mDisplayDeviceConfig, mHandler, flags,
@@ -610,11 +601,13 @@
                         brightnessSetting, () -> postBrightnessChangeRunnable(),
                         new HandlerExecutor(mHandler), flags);
 
+        String thermalBrightnessThrottlingDataId =
+                logicalDisplay.getDisplayInfoLocked().thermalBrightnessThrottlingDataId;
         mBrightnessClamperController = mInjector.getBrightnessClamperController(
                 mHandler, modeChangeCallback::run,
                 new BrightnessClamperController.DisplayDeviceData(
                         mUniqueDisplayId,
-                        mThermalBrightnessThrottlingDataId,
+                        thermalBrightnessThrottlingDataId,
                         logicalDisplay.getPowerThrottlingDataIdLocked(),
                         mDisplayDeviceConfig, displayDeviceInfo.width, displayDeviceInfo.height,
                         displayToken, mDisplayId), mContext, flags, mSensorManager,
@@ -928,7 +921,6 @@
                 mDisplayStatsId = mUniqueDisplayId.hashCode();
                 mDisplayDeviceConfig = config;
                 mIdleStylusTimeoutMillisConfig = mDisplayDeviceConfig.getIdleStylusTimeoutMillis();
-                mThermalBrightnessThrottlingDataId = thermalBrightnessThrottlingDataId;
                 loadFromDisplayDeviceConfig(token, info, hbmMetadata);
                 mDisplayPowerProximityStateController.notifyDisplayDeviceChanged(config);
 
@@ -936,22 +928,10 @@
                 // last command that was sent to change it's state. Let's assume it is unknown so
                 // that we trigger a change immediately.
                 mPowerState.resetScreenState();
-            } else if (!Objects.equals(mThermalBrightnessThrottlingDataId,
-                    thermalBrightnessThrottlingDataId)) {
-                changed = true;
-                mThermalBrightnessThrottlingDataId = thermalBrightnessThrottlingDataId;
-                mBrightnessThrottler.loadThermalBrightnessThrottlingDataFromDisplayDeviceConfig(
-                        config.getThermalBrightnessThrottlingDataMapByThrottlingId(),
-                        config.getTempSensor(),
-                        mThermalBrightnessThrottlingDataId,
-                        mUniqueDisplayId);
             }
 
             mIsDisplayInternal = isDisplayInternal;
-            // using local variables here, when mBrightnessThrottler is removed,
-            // mThermalBrightnessThrottlingDataId could be removed as well
-            // changed = true will be not needed - clampers are maintaining their state and
-            // will call updatePowerState if needed.
+
             mBrightnessClamperController.onDisplayChanged(
                     new BrightnessClamperController.DisplayDeviceData(uniqueId,
                             thermalBrightnessThrottlingDataId, powerThrottlingDataId,
@@ -1000,10 +980,6 @@
         setAnimatorRampSpeeds(/* isIdleMode= */ false);
 
         mBrightnessRangeController.loadFromConfig(hbmMetadata, token, info, mDisplayDeviceConfig);
-        mBrightnessThrottler.loadThermalBrightnessThrottlingDataFromDisplayDeviceConfig(
-                mDisplayDeviceConfig.getThermalBrightnessThrottlingDataMapByThrottlingId(),
-                mDisplayDeviceConfig.getTempSensor(),
-                mThermalBrightnessThrottlingDataId, mUniqueDisplayId);
     }
 
     private void sendUpdatePowerState() {
@@ -1188,7 +1164,7 @@
                     autoBrightnessResetAmbientLuxAfterWarmUp, ambientBrightnessThresholds,
                     screenBrightnessThresholds, ambientBrightnessThresholdsIdle,
                     screenBrightnessThresholdsIdle, mContext, mBrightnessRangeController,
-                    mBrightnessThrottler, mDisplayDeviceConfig.getAmbientHorizonShort(),
+                    mBrightnessClamperController, mDisplayDeviceConfig.getAmbientHorizonShort(),
                     mDisplayDeviceConfig.getAmbientHorizonLong(), userLux, userNits, mFlags);
             mDisplayBrightnessController.setUpAutoBrightness(
                     mAutomaticBrightnessController, mSensorManager, mDisplayDeviceConfig, mHandler,
@@ -1309,7 +1285,6 @@
     private void cleanupHandlerThreadAfterStop() {
         mDisplayPowerProximityStateController.cleanup();
         mBrightnessRangeController.stop();
-        mBrightnessThrottler.stop();
         mBrightnessClamperController.stop();
         mHandler.removeCallbacksAndMessages(null);
 
@@ -2104,18 +2079,6 @@
                                 maxDesiredHdrSdrRatio), modeChangeCallback, hbmMetadata, mContext);
     }
 
-    private BrightnessThrottler createBrightnessThrottlerLocked() {
-        final DisplayDevice device = mLogicalDisplay.getPrimaryDisplayDeviceLocked();
-        final DisplayDeviceConfig ddConfig = device.getDisplayDeviceConfig();
-        return new BrightnessThrottler(mHandler,
-                () -> {
-                    sendUpdatePowerState();
-                    postBrightnessChangeRunnable();
-                }, mUniqueDisplayId,
-                mLogicalDisplay.getDisplayInfoLocked().thermalBrightnessThrottlingDataId,
-                ddConfig);
-    }
-
     private void blockScreenOn() {
         if (mPendingScreenOnUnblocker == null) {
             Trace.asyncTraceBegin(Trace.TRACE_TAG_POWER, SCREEN_ON_BLOCKED_TRACE_NAME, 0);
@@ -2780,11 +2743,6 @@
         }
 
         pw.println();
-        if (mBrightnessThrottler != null) {
-            mBrightnessThrottler.dump(pw);
-        }
-
-        pw.println();
         if (mDisplayWhiteBalanceController != null) {
             mDisplayWhiteBalanceController.dump(pw);
             mDisplayWhiteBalanceSettings.dump(pw);
@@ -3330,7 +3288,7 @@
                 HysteresisLevels ambientBrightnessThresholdsIdle,
                 HysteresisLevels screenBrightnessThresholdsIdle, Context context,
                 BrightnessRangeController brightnessModeController,
-                BrightnessThrottler brightnessThrottler, int ambientLightHorizonShort,
+                BrightnessClamperController clamperController, int ambientLightHorizonShort,
                 int ambientLightHorizonLong, float userLux, float userNits,
                 DisplayManagerFlags displayManagerFlags) {
 
@@ -3342,7 +3300,7 @@
                     resetAmbientLuxAfterWarmUpConfig, ambientBrightnessThresholds,
                     screenBrightnessThresholds, ambientBrightnessThresholdsIdle,
                     screenBrightnessThresholdsIdle, context, brightnessModeController,
-                    brightnessThrottler, ambientLightHorizonShort, ambientLightHorizonLong, userLux,
+                    clamperController, ambientLightHorizonShort, ambientLightHorizonLong, userLux,
                     userNits, displayManagerFlags);
         }
 
diff --git a/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java b/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java
index 461a9f3..8865039 100644
--- a/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java
+++ b/services/core/java/com/android/server/display/DisplayTopologyCoordinator.java
@@ -16,8 +16,9 @@
 
 package com.android.server.display;
 
+import static android.hardware.display.DisplayTopology.pxToDp;
+
 import android.hardware.display.DisplayTopology;
-import android.util.DisplayMetrics;
 import android.view.Display;
 import android.view.DisplayInfo;
 
@@ -140,8 +141,7 @@
      * @return The width of the display in dp
      */
     private float getWidth(DisplayInfo info) {
-        return info.logicalWidth * (float) DisplayMetrics.DENSITY_DEFAULT
-                / info.logicalDensityDpi;
+        return pxToDp(info.logicalWidth, info.logicalDensityDpi);
     }
 
     /**
@@ -149,8 +149,7 @@
      * @return The height of the display in dp
      */
     private float getHeight(DisplayInfo info) {
-        return info.logicalHeight * (float) DisplayMetrics.DENSITY_DEFAULT
-                / info.logicalDensityDpi;
+        return pxToDp(info.logicalHeight, info.logicalDensityDpi);
     }
 
     private boolean isDisplayAllowedInTopology(DisplayInfo info) {
diff --git a/services/core/java/com/android/server/display/ExternalDisplayPolicy.java b/services/core/java/com/android/server/display/ExternalDisplayPolicy.java
index 519763a..a47853c 100644
--- a/services/core/java/com/android/server/display/ExternalDisplayPolicy.java
+++ b/services/core/java/com/android/server/display/ExternalDisplayPolicy.java
@@ -142,14 +142,6 @@
             mDisplayIdsWaitingForBootCompletion.clear();
         }
 
-        if (!mFlags.isConnectedDisplayManagementEnabled()) {
-            if (DEBUG) {
-                Slog.d(TAG, "External display management is not enabled on your device:"
-                                    + " cannot register thermal listener.");
-            }
-            return;
-        }
-
         if (!mFlags.isConnectedDisplayErrorHandlingEnabled()) {
             if (DEBUG) {
                 Slog.d(TAG, "ConnectedDisplayErrorHandlingEnabled is not enabled on your device:"
@@ -173,14 +165,6 @@
             return;
         }
 
-        if (!mFlags.isConnectedDisplayManagementEnabled()) {
-            if (DEBUG) {
-                Slog.d(TAG, "setExternalDisplayEnabledLocked: External display management is not"
-                                    + " enabled on your device, cannot enable/disable display.");
-            }
-            return;
-        }
-
         if (enabled && !isExternalDisplayAllowed()) {
             Slog.w(TAG, "setExternalDisplayEnabledLocked: External display can not be enabled"
                                 + " because it is currently not allowed.");
@@ -202,14 +186,6 @@
             return;
         }
 
-        if (!mFlags.isConnectedDisplayManagementEnabled()) {
-            if (DEBUG) {
-                Slog.d(TAG, "handleExternalDisplayConnectedLocked connected display management"
-                                    + " flag is off");
-            }
-            return;
-        }
-
         if (!mIsBootCompleted) {
             mDisplayIdsWaitingForBootCompletion.add(logicalDisplay.getDisplayIdLocked());
             return;
@@ -251,10 +227,6 @@
     void handleLogicalDisplayDisconnectedLocked(@NonNull final LogicalDisplay logicalDisplay) {
         // Type of the display here is always UNKNOWN, so we can't verify it is an external display
 
-        if (!mFlags.isConnectedDisplayManagementEnabled()) {
-            return;
-        }
-
         var displayId = logicalDisplay.getDisplayIdLocked();
         if (mDisplayIdsWaitingForBootCompletion.remove(displayId)) {
             return;
@@ -271,10 +243,6 @@
             return;
         }
 
-        if (!mFlags.isConnectedDisplayManagementEnabled()) {
-            return;
-        }
-
         mExternalDisplayStatsService.onDisplayAdded(logicalDisplay.getDisplayIdLocked());
     }
 
@@ -289,10 +257,6 @@
             }
         }
 
-        if (!mFlags.isConnectedDisplayManagementEnabled()) {
-            return;
-        }
-
         if (isShown) {
             mExternalDisplayStatsService.onPresentationWindowAdded(displayId);
         } else {
@@ -306,12 +270,6 @@
             return;
         }
 
-        if (!mFlags.isConnectedDisplayManagementEnabled()) {
-            Slog.e(TAG, "disableExternalDisplayLocked shouldn't be called when the"
-                                + " connected display management flag is off");
-            return;
-        }
-
         if (!mFlags.isConnectedDisplayErrorHandlingEnabled()) {
             if (DEBUG) {
                 Slog.d(TAG, "disableExternalDisplayLocked shouldn't be called when the"
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index 0069215..ecc8896 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -823,18 +823,13 @@
                 if (wasPreviouslyUpdated) {
                     // The display isn't actually removed from our internal data structures until
                     // after the notification is sent; see {@link #sendUpdatesForDisplaysLocked}.
-                    if (mFlags.isConnectedDisplayManagementEnabled()) {
-                        if (mDisplaysEnabledCache.get(displayId)) {
-                            // We still need to send LOGICAL_DISPLAY_EVENT_DISCONNECTED
-                            reloop = true;
-                            logicalDisplayEventMask |= LOGICAL_DISPLAY_EVENT_REMOVED;
-                        } else {
-                            mUpdatedLogicalDisplays.delete(displayId);
-                            logicalDisplayEventMask |= LOGICAL_DISPLAY_EVENT_DISCONNECTED;
-                        }
+                    if (mDisplaysEnabledCache.get(displayId)) {
+                        // We still need to send LOGICAL_DISPLAY_EVENT_DISCONNECTED
+                        reloop = true;
+                        logicalDisplayEventMask |= LOGICAL_DISPLAY_EVENT_REMOVED;
                     } else {
                         mUpdatedLogicalDisplays.delete(displayId);
-                        logicalDisplayEventMask |= LOGICAL_DISPLAY_EVENT_REMOVED;
+                        logicalDisplayEventMask |= LOGICAL_DISPLAY_EVENT_DISCONNECTED;
                     }
                 } else {
                     // This display never left this class, safe to remove without notification
@@ -845,20 +840,15 @@
 
             // The display is new.
             } else if (!wasPreviouslyUpdated) {
-                if (mFlags.isConnectedDisplayManagementEnabled()) {
-                    // We still need to send LOGICAL_DISPLAY_EVENT_ADDED
-                    reloop = true;
-                    logicalDisplayEventMask |= LOGICAL_DISPLAY_EVENT_CONNECTED;
-                } else {
-                    logicalDisplayEventMask |= LOGICAL_DISPLAY_EVENT_ADDED;
-                }
+                // We still need to send LOGICAL_DISPLAY_EVENT_ADDED
+                reloop = true;
+                logicalDisplayEventMask |= LOGICAL_DISPLAY_EVENT_CONNECTED;
             // Underlying displays device has changed to a different one.
             } else if (!TextUtils.equals(mTempDisplayInfo.uniqueId, newDisplayInfo.uniqueId)) {
                 logicalDisplayEventMask |= LOGICAL_DISPLAY_EVENT_SWAPPED;
 
             // Something about the display device has changed.
-            } else if (mFlags.isConnectedDisplayManagementEnabled()
-                    && wasPreviouslyEnabled != isCurrentlyEnabled) {
+            } else if (wasPreviouslyEnabled != isCurrentlyEnabled) {
                 int event = isCurrentlyEnabled ? LOGICAL_DISPLAY_EVENT_ADDED :
                         LOGICAL_DISPLAY_EVENT_REMOVED;
                 logicalDisplayEventMask |= event;
@@ -936,17 +926,13 @@
         sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_DEVICE_STATE_TRANSITION);
         sendUpdatesForGroupsLocked(DISPLAY_GROUP_EVENT_ADDED);
         sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_REMOVED);
-        if (mFlags.isConnectedDisplayManagementEnabled()) {
-            sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_DISCONNECTED);
-        }
+        sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_DISCONNECTED);
         sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_BASIC_CHANGED);
         sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_REFRESH_RATE_CHANGED);
         sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_STATE_CHANGED);
         sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_FRAME_RATE_OVERRIDES_CHANGED);
         sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_SWAPPED);
-        if (mFlags.isConnectedDisplayManagementEnabled()) {
-            sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_CONNECTED);
-        }
+        sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_CONNECTED);
         sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_ADDED);
         sendUpdatesForDisplaysLocked(LOGICAL_DISPLAY_EVENT_HDR_SDR_RATIO_CHANGED);
         sendUpdatesForGroupsLocked(DISPLAY_GROUP_EVENT_CHANGED);
@@ -996,23 +982,15 @@
                         + "display=" + id + " with device=" + uniqueId);
             }
 
-            if (mFlags.isConnectedDisplayManagementEnabled()) {
-                if (logicalDisplayEvent == LOGICAL_DISPLAY_EVENT_ADDED) {
-                    mDisplaysEnabledCache.put(id, true);
-                } else if (logicalDisplayEvent == LOGICAL_DISPLAY_EVENT_REMOVED) {
-                    mDisplaysEnabledCache.delete(id);
-                }
+            if (logicalDisplayEvent == LOGICAL_DISPLAY_EVENT_ADDED) {
+                mDisplaysEnabledCache.put(id, true);
+            } else if (logicalDisplayEvent == LOGICAL_DISPLAY_EVENT_REMOVED) {
+                mDisplaysEnabledCache.delete(id);
             }
 
             mListener.onLogicalDisplayEventLocked(display, logicalDisplayEvent);
 
-            if (mFlags.isConnectedDisplayManagementEnabled()) {
-                if (logicalDisplayEvent == LOGICAL_DISPLAY_EVENT_DISCONNECTED) {
-                    mLogicalDisplays.delete(id);
-                }
-            } else if (logicalDisplayEvent == LOGICAL_DISPLAY_EVENT_REMOVED) {
-                // We wait until we sent the EVENT_REMOVED event before actually removing the
-                // display.
+            if (logicalDisplayEvent == LOGICAL_DISPLAY_EVENT_DISCONNECTED) {
                 mLogicalDisplays.delete(id);
             }
         }
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 860be20..204d74d 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
@@ -195,6 +195,19 @@
         mModifiers.forEach(BrightnessStateModifier::stop);
     }
 
+    /**
+     * returns max allowed brightness.
+     * TODO(b/387452517): introduce constrainBrightness method
+     */
+    public float getMaxBrightness() {
+        return mModifiersAggregatedState.mMaxBrightness;
+    }
+
+    public boolean isThrottled() {
+        return mModifiersAggregatedState.mMaxBrightnessReason
+                != BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE;
+    }
+
 
     // Called in DisplayControllerHandler
     private void recalculateModifiersState() {
@@ -306,7 +319,7 @@
             BrightnessWearBedtimeModeModifier.WearBedtimeModeData {
         @NonNull
         private final String mUniqueDisplayId;
-        @NonNull
+        @Nullable
         private final String mThermalThrottlingDataId;
         @NonNull
         private final String mPowerThrottlingDataId;
@@ -322,7 +335,7 @@
         final int mDisplayId;
 
         public DisplayDeviceData(@NonNull String uniqueDisplayId,
-                @NonNull String thermalThrottlingDataId,
+                @Nullable String thermalThrottlingDataId,
                 @NonNull String powerThrottlingDataId,
                 @NonNull DisplayDeviceConfig displayDeviceConfig,
                 int width,
@@ -345,7 +358,7 @@
             return mUniqueDisplayId;
         }
 
-        @NonNull
+        @Nullable
         @Override
         public String getThermalThrottlingDataId() {
             return mThermalThrottlingDataId;
@@ -354,6 +367,9 @@
         @Nullable
         @Override
         public ThermalBrightnessThrottlingData getThermalBrightnessThrottlingData() {
+            if (mThermalThrottlingDataId == null) {
+                return null;
+            }
             return mDisplayDeviceConfig.getThermalBrightnessThrottlingDataMapByThrottlingId().get(
                     mThermalThrottlingDataId);
         }
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessThermalModifier.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessThermalModifier.java
index 21ef309..593bba2 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessThermalModifier.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessThermalModifier.java
@@ -347,7 +347,7 @@
         @NonNull
         String getUniqueDisplayId();
 
-        @NonNull
+        @Nullable
         String getThermalThrottlingDataId();
 
         @Nullable
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 addfbf1..4e57d67 100644
--- a/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
+++ b/services/core/java/com/android/server/display/feature/DisplayManagerFlags.java
@@ -42,10 +42,6 @@
             Flags.FLAG_ENABLE_PORT_IN_DISPLAY_LAYOUT,
             Flags::enablePortInDisplayLayout);
 
-    private final FlagState mConnectedDisplayManagementFlagState = new FlagState(
-            Flags.FLAG_ENABLE_CONNECTED_DISPLAY_MANAGEMENT,
-            Flags::enableConnectedDisplayManagement);
-
     private final FlagState mAdaptiveToneImprovements1 = new FlagState(
             Flags.FLAG_ENABLE_ADAPTIVE_TONE_IMPROVEMENTS_1,
             Flags::enableAdaptiveToneImprovements1);
@@ -269,11 +265,6 @@
         return mPortInDisplayLayoutFlagState.isEnabled();
     }
 
-    /** Returns whether connected display management is enabled or not. */
-    public boolean isConnectedDisplayManagementEnabled() {
-        return mConnectedDisplayManagementFlagState.isEnabled();
-    }
-
     /** Returns whether power throttling clamper is enabled on not. */
     public boolean isPowerThrottlingClamperEnabled() {
         return mPowerThrottlingClamperFlagState.isEnabled();
@@ -572,7 +563,6 @@
         pw.println(" " + mAdaptiveToneImprovements2);
         pw.println(" " + mBackUpSmoothDisplayAndForcePeakRefreshRateFlagState);
         pw.println(" " + mConnectedDisplayErrorHandlingFlagState);
-        pw.println(" " + mConnectedDisplayManagementFlagState);
         pw.println(" " + mDisplayOffloadFlagState);
         pw.println(" " + mExternalDisplayLimitModeState);
         pw.println(" " + mDisplayTopology);
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 eccbbb1..afae07c 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
@@ -29,14 +29,6 @@
 }
 
 flag {
-    name: "enable_connected_display_management"
-    namespace: "display_manager"
-    description: "Feature flag for Connected Display management"
-    bug: "280739508"
-    is_fixed_read_only: true
-}
-
-flag {
     name: "enable_power_throttling_clamper"
     namespace: "display_manager"
     description: "Feature flag for Power Throttling Clamper"
diff --git a/services/core/java/com/android/server/flags/services.aconfig b/services/core/java/com/android/server/flags/services.aconfig
index eea5c98..4505d0e 100644
--- a/services/core/java/com/android/server/flags/services.aconfig
+++ b/services/core/java/com/android/server/flags/services.aconfig
@@ -78,3 +78,11 @@
         purpose: PURPOSE_BUGFIX
     }
 }
+
+flag {
+    name: "datetime_notifications"
+    # "location" is used by the Android System Time team for feature flags.
+    namespace: "location"
+    description: "Enable the time notifications feature, a toggle to enable/disable time-related notifications in Date & Time Settings"
+    bug: "283267917"
+}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index c384b54..9349ea5 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -1030,10 +1030,20 @@
     }
 
     @ServiceThreadOnly
+    void addAndStartAction(final HdmiCecFeatureAction action, final boolean remove) {
+        assertRunOnServiceThread();
+        if (hasAction(action.getClass()) && remove) {
+            // If the action is currently running, remove it and restart it.
+            Slog.i(TAG, action.getClass().getName() + " is in progress. Restarting.");
+            removeAction(action.getClass());
+        }
+        addAndStartAction(action);
+    }
+
+    @ServiceThreadOnly
     void startNewAvbAudioStatusAction(int targetAddress) {
         assertRunOnServiceThread();
-        removeAction(AbsoluteVolumeAudioStatusAction.class);
-        addAndStartAction(new AbsoluteVolumeAudioStatusAction(this, targetAddress));
+        addAndStartAction(new AbsoluteVolumeAudioStatusAction(this, targetAddress), true);
     }
 
     @ServiceThreadOnly
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
index 1e90ab2..510e4f5 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
@@ -317,11 +317,7 @@
         if ((systemAudioOnPowerOnProp == ALWAYS_SYSTEM_AUDIO_CONTROL_ON_POWER_ON)
                 || ((systemAudioOnPowerOnProp == USE_LAST_STATE_SYSTEM_AUDIO_CONTROL_ON_POWER_ON)
                 && lastSystemAudioControlStatus && isSystemAudioControlFeatureEnabled())) {
-            if (hasAction(SystemAudioInitiationActionFromAvr.class)) {
-                Slog.i(TAG, "SystemAudioInitiationActionFromAvr is in progress. Restarting.");
-                removeAction(SystemAudioInitiationActionFromAvr.class);
-            }
-            addAndStartAction(new SystemAudioInitiationActionFromAvr(this));
+            addAndStartAction(new SystemAudioInitiationActionFromAvr(this), true);
         }
     }
 
@@ -457,6 +453,7 @@
             HdmiLogger.debug("AVR device is not directly connected with TV");
             return Constants.ABORT_NOT_IN_CORRECT_MODE;
         } else {
+            // Action has been removed if it existed, do not attempt to remove again before start.
             addAndStartAction(new ArcInitiationActionFromAvr(this));
             return Constants.HANDLED;
         }
@@ -477,11 +474,9 @@
                     && !getActions(ArcTerminationActionFromAvr.class).get(0).mCallbacks.isEmpty()) {
                 IHdmiControlCallback callback =
                         getActions(ArcTerminationActionFromAvr.class).get(0).mCallbacks.get(0);
-                removeAction(ArcTerminationActionFromAvr.class);
-                addAndStartAction(new ArcTerminationActionFromAvr(this, callback));
+                addAndStartAction(new ArcTerminationActionFromAvr(this, callback), true);
             } else {
-                removeAction(ArcTerminationActionFromAvr.class);
-                addAndStartAction(new ArcTerminationActionFromAvr(this));
+                addAndStartAction(new ArcTerminationActionFromAvr(this), true);
             }
             return Constants.HANDLED;
         }
@@ -1036,11 +1031,7 @@
     void onSystemAudioControlFeatureSupportChanged(boolean enabled) {
         setSystemAudioControlFeatureEnabled(enabled);
         if (enabled) {
-            if (hasAction(SystemAudioInitiationActionFromAvr.class)) {
-                Slog.i(TAG, "SystemAudioInitiationActionFromAvr is in progress. Restarting.");
-                removeAction(SystemAudioInitiationActionFromAvr.class);
-            }
-            addAndStartAction(new SystemAudioInitiationActionFromAvr(this));
+            addAndStartAction(new SystemAudioInitiationActionFromAvr(this), true);
         }
     }
 
@@ -1221,8 +1212,7 @@
         removeAction(ArcTerminationActionFromAvr.class);
         if (SystemProperties.getBoolean(Constants.PROPERTY_ARC_SUPPORT, true)
                 && isDirectConnectToTv() && !isArcEnabled()) {
-            removeAction(ArcInitiationActionFromAvr.class);
-            addAndStartAction(new ArcInitiationActionFromAvr(this));
+            addAndStartAction(new ArcInitiationActionFromAvr(this), true);
         }
     }
 
@@ -1367,10 +1357,6 @@
         if (mService.isDeviceDiscoveryHandledByPlayback()) {
             return;
         }
-        if (hasAction(DeviceDiscoveryAction.class)) {
-            Slog.i(TAG, "Device Discovery Action is in progress. Restarting.");
-            removeAction(DeviceDiscoveryAction.class);
-        }
         DeviceDiscoveryAction action = new DeviceDiscoveryAction(this,
                 new DeviceDiscoveryCallback() {
                     @Override
@@ -1380,7 +1366,7 @@
                         }
                     }
                 });
-        addAndStartAction(action);
+        addAndStartAction(action, true);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index 0b667fc..86abbc4 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -21,7 +21,6 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.hardware.display.DeviceProductInfo;
 import android.hardware.hdmi.HdmiControlManager;
 import android.hardware.hdmi.HdmiDeviceInfo;
 import android.hardware.hdmi.IHdmiControlCallback;
@@ -32,7 +31,6 @@
 import android.os.SystemProperties;
 import android.sysprop.HdmiProperties;
 import android.util.Slog;
-import android.view.Display;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.LocalePicker;
@@ -151,10 +149,6 @@
     private void launchDeviceDiscovery() {
         assertRunOnServiceThread();
         clearDeviceInfoList();
-        if (hasAction(DeviceDiscoveryAction.class)) {
-            Slog.i(TAG, "Device Discovery Action is in progress. Restarting.");
-            removeAction(DeviceDiscoveryAction.class);
-        }
         DeviceDiscoveryAction action = new DeviceDiscoveryAction(this,
                 new DeviceDiscoveryAction.DeviceDiscoveryCallback() {
                     @Override
@@ -163,25 +157,21 @@
                             mService.getHdmiCecNetwork().addCecDevice(info);
                         }
 
-                        // Since we removed all devices when it starts and device discovery action
-                        // does not poll local devices, we should put device info of local device
-                        // manually here.
+                        // Since we removed all devices when it starts and device discovery
+                        // action does not poll local devices, we should put device info of
+                        // local device manually here.
                         for (HdmiCecLocalDevice device : mService.getAllCecLocalDevices()) {
                             mService.getHdmiCecNetwork().addCecDevice(device.getDeviceInfo());
                         }
 
-                        List<HotplugDetectionAction> hotplugActions =
-                                getActions(HotplugDetectionAction.class);
-                        if (hotplugActions.isEmpty()) {
+                        if (!hasAction(HotplugDetectionAction.class)) {
                             addAndStartAction(
-                                    new HotplugDetectionAction(HdmiCecLocalDevicePlayback.this));
+                                    new HotplugDetectionAction(
+                                            HdmiCecLocalDevicePlayback.this));
                         }
 
                         if (mService.isHdmiControlEnhancedBehaviorFlagEnabled()) {
-                            List<PowerStatusMonitorActionFromPlayback>
-                                    powerStatusMonitorActionsFromPlayback =
-                                    getActions(PowerStatusMonitorActionFromPlayback.class);
-                            if (powerStatusMonitorActionsFromPlayback.isEmpty()) {
+                            if (!hasAction(PowerStatusMonitorActionFromPlayback.class)) {
                                 addAndStartAction(
                                         new PowerStatusMonitorActionFromPlayback(
                                                 HdmiCecLocalDevicePlayback.this));
@@ -189,7 +179,7 @@
                         }
                     }
                 });
-        addAndStartAction(action);
+        addAndStartAction(action, true);
     }
 
     @Override
@@ -235,8 +225,16 @@
             invokeCallback(callback, HdmiControlManager.RESULT_INCORRECT_MODE);
             return;
         }
-        removeAction(DeviceSelectActionFromPlayback.class);
-        addAndStartAction(new DeviceSelectActionFromPlayback(this, targetDevice, callback));
+        List<DeviceSelectActionFromPlayback> actions = getActions(
+                DeviceSelectActionFromPlayback.class);
+        if (!actions.isEmpty()) {
+            DeviceSelectActionFromPlayback action = actions.get(0);
+            if (action.getTargetAddress() == targetDevice.getLogicalAddress()) {
+                return;
+            }
+        }
+        addAndStartAction(new DeviceSelectActionFromPlayback(this, targetDevice, callback),
+                true);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 424102c..3d6d34b 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -220,10 +220,6 @@
         List<HdmiCecMessage> bufferedActiveSource = mDelayedMessageBuffer
                 .getBufferedMessagesWithOpcode(Constants.MESSAGE_ACTIVE_SOURCE);
         if (bufferedActiveSource.isEmpty()) {
-            if (hasAction(RequestActiveSourceAction.class)) {
-                Slog.i(TAG, "RequestActiveSourceAction is in progress. Restarting.");
-                removeAction(RequestActiveSourceAction.class);
-            }
             addAndStartAction(new RequestActiveSourceAction(this, new IHdmiControlCallback.Stub() {
                 @Override
                 public void onComplete(int result) {
@@ -231,7 +227,7 @@
                         launchRoutingControl(routingForBootup);
                     }
                 }
-            }));
+            }), true);
         } else {
             addCecDeviceForBufferedActiveSource(bufferedActiveSource.get(0));
         }
@@ -328,8 +324,15 @@
             invokeCallback(callback, HdmiControlManager.RESULT_INCORRECT_MODE);
             return;
         }
-        removeAction(DeviceSelectActionFromTv.class);
-        addAndStartAction(new DeviceSelectActionFromTv(this, targetDevice, callback));
+        List<DeviceSelectActionFromTv> actions = getActions(DeviceSelectActionFromTv.class);
+        if (!actions.isEmpty()) {
+            DeviceSelectActionFromTv action = actions.get(0);
+            if (action.getTargetAddress() == targetDevice.getLogicalAddress()) {
+                return;
+            }
+        }
+        addAndStartAction(new DeviceSelectActionFromTv(this, targetDevice, callback),
+                true);
     }
 
     @ServiceThreadOnly
@@ -475,9 +478,8 @@
                 HdmiCecMessageBuilder.buildRoutingChange(
                         getDeviceInfo().getLogicalAddress(), oldPath, newPath);
         mService.sendCecCommand(routingChange);
-        removeAction(RoutingControlAction.class);
         addAndStartAction(
-                new RoutingControlAction(this, newPath, callback));
+                new RoutingControlAction(this, newPath, callback), true);
     }
 
     @ServiceThreadOnly
@@ -801,16 +803,12 @@
                         mSelectRequestBuffer.process();
                         resetSelectRequestBuffer();
 
-                        List<HotplugDetectionAction> hotplugActions
-                                = getActions(HotplugDetectionAction.class);
-                        if (hotplugActions.isEmpty()) {
+                        if (!hasAction(HotplugDetectionAction.class)) {
                             addAndStartAction(
                                     new HotplugDetectionAction(HdmiCecLocalDeviceTv.this));
                         }
 
-                        List<PowerStatusMonitorAction> powerStatusActions
-                                = getActions(PowerStatusMonitorAction.class);
-                        if (powerStatusActions.isEmpty()) {
+                        if (!hasAction(PowerStatusMonitorAction.class)) {
                             addAndStartAction(
                                     new PowerStatusMonitorAction(HdmiCecLocalDeviceTv.this));
                         }
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 35ef18b..bd8b67b9 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -17,7 +17,6 @@
 package com.android.server.hdmi;
 
 import static android.media.tv.flags.Flags.hdmiControlEnhancedBehavior;
-
 import static android.hardware.hdmi.HdmiControlManager.DEVICE_EVENT_ADD_DEVICE;
 import static android.hardware.hdmi.HdmiControlManager.DEVICE_EVENT_REMOVE_DEVICE;
 import static android.hardware.hdmi.HdmiControlManager.EARC_FEATURE_DISABLED;
@@ -107,7 +106,6 @@
 import android.util.SparseArray;
 import android.view.Display;
 import android.view.KeyEvent;
-import android.view.WindowManager;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
@@ -1218,9 +1216,6 @@
                 audioSystem.terminateSystemAudioMode();
             }
             if (isArcEnabled) {
-                if (audioSystem.hasAction(ArcTerminationActionFromAvr.class)) {
-                    audioSystem.removeAction(ArcTerminationActionFromAvr.class);
-                }
                 audioSystem.addAndStartAction(new ArcTerminationActionFromAvr(audioSystem,
                         new IHdmiControlCallback.Stub() {
                             @Override
@@ -1228,7 +1223,7 @@
                                 mAddressAllocated = false;
                                 initializeCecLocalDevices(INITIATED_BY_SOUNDBAR_MODE);
                             }
-                        }));
+                        }), true);
             }
         }
         if (!isArcEnabled) {
diff --git a/services/core/java/com/android/server/hdmi/PowerStatusMonitorActionFromPlayback.java b/services/core/java/com/android/server/hdmi/PowerStatusMonitorActionFromPlayback.java
index 9a3cde1..d05ded5 100644
--- a/services/core/java/com/android/server/hdmi/PowerStatusMonitorActionFromPlayback.java
+++ b/services/core/java/com/android/server/hdmi/PowerStatusMonitorActionFromPlayback.java
@@ -68,6 +68,8 @@
 
     private boolean handleReportPowerStatusFromTv(HdmiCecMessage cmd) {
         int powerStatus = cmd.getParams()[0] & 0xFF;
+        mState = STATE_WAIT_FOR_NEXT_MONITORING;
+        addTimer(mState, MONITORING_INTERVAL_MS);
         if (powerStatus == POWER_STATUS_STANDBY) {
             Slog.d(TAG, "TV reported it turned off, going to sleep.");
             source().getService().standby();
diff --git a/services/core/java/com/android/server/input/InputGestureManager.java b/services/core/java/com/android/server/input/InputGestureManager.java
index 9f785ac..2429640 100644
--- a/services/core/java/com/android/server/input/InputGestureManager.java
+++ b/services/core/java/com/android/server/input/InputGestureManager.java
@@ -19,6 +19,7 @@
 import static android.hardware.input.InputGestureData.createKeyTrigger;
 
 import static com.android.hardware.input.Flags.enableTalkbackAndMagnifierKeyGestures;
+import static com.android.hardware.input.Flags.enableVoiceAccessKeyGestures;
 import static com.android.hardware.input.Flags.keyboardA11yShortcutControl;
 import static com.android.server.flags.Flags.newBugreportKeyboardShortcut;
 import static com.android.window.flags.Flags.enableMoveToNextDisplayShortcut;
@@ -240,6 +241,13 @@
                     KeyEvent.META_META_ON | KeyEvent.META_ALT_ON,
                     KeyGestureEvent.KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK));
         }
+        if (enableVoiceAccessKeyGestures()) {
+            systemShortcuts.add(
+                    createKeyGesture(
+                            KeyEvent.KEYCODE_V,
+                            KeyEvent.META_META_ON | KeyEvent.META_ALT_ON,
+                            KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_VOICE_ACCESS));
+        }
         if (enableTaskResizingKeyboardShortcuts()) {
             systemShortcuts.add(createKeyGesture(
                     KeyEvent.KEYCODE_LEFT_BRACKET,
diff --git a/services/core/java/com/android/server/input/InputManagerInternal.java b/services/core/java/com/android/server/input/InputManagerInternal.java
index bc44fed..4e5c720 100644
--- a/services/core/java/com/android/server/input/InputManagerInternal.java
+++ b/services/core/java/com/android/server/input/InputManagerInternal.java
@@ -104,13 +104,16 @@
     public abstract PointF getCursorPosition(int displayId);
 
     /**
-     * Enables or disables pointer acceleration for mouse movements.
+     * Set whether all pointer scaling, including linear scaling based on the
+     * user's pointer speed setting, should be enabled or disabled for mice.
      *
      * Note that this only affects pointer movements from mice (that is, pointing devices which send
      * relative motions, including trackballs and pointing sticks), not from other pointer devices
      * such as touchpads and styluses.
+     *
+     * Scaling is enabled by default on new displays until it is explicitly disabled.
      */
-    public abstract void setMousePointerAccelerationEnabled(boolean enabled, int displayId);
+    public abstract void setMouseScalingEnabled(boolean enabled, int displayId);
 
     /**
      * Sets the eligibility of windows on a given display for pointer capture. If a display is
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 559b4ae..b2c35e1 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -1382,9 +1382,9 @@
         mNative.setPointerSpeed(speed);
     }
 
-    private void setMousePointerAccelerationEnabled(boolean enabled, int displayId) {
+    private void setMouseScalingEnabled(boolean enabled, int displayId) {
         updateAdditionalDisplayInputProperties(displayId,
-                properties -> properties.mousePointerAccelerationEnabled = enabled);
+                properties -> properties.mouseScalingEnabled = enabled);
     }
 
     private void setPointerIconVisible(boolean visible, int displayId) {
@@ -2232,8 +2232,8 @@
                     pw.println("displayId: " + mAdditionalDisplayInputProperties.keyAt(i));
                     final AdditionalDisplayInputProperties properties =
                             mAdditionalDisplayInputProperties.valueAt(i);
-                    pw.println("mousePointerAccelerationEnabled: "
-                            + properties.mousePointerAccelerationEnabled);
+                    pw.println("mouseScalingEnabled: "
+                            + properties.mouseScalingEnabled);
                     pw.println("pointerIconVisible: " + properties.pointerIconVisible);
                 }
             } finally {
@@ -3575,8 +3575,8 @@
         }
 
         @Override
-        public void setMousePointerAccelerationEnabled(boolean enabled, int displayId) {
-            InputManagerService.this.setMousePointerAccelerationEnabled(enabled, displayId);
+        public void setMouseScalingEnabled(boolean enabled, int displayId) {
+            InputManagerService.this.setMouseScalingEnabled(enabled, displayId);
         }
 
         @Override
@@ -3716,15 +3716,15 @@
     private static class AdditionalDisplayInputProperties {
 
         static final boolean DEFAULT_POINTER_ICON_VISIBLE = true;
-        static final boolean DEFAULT_MOUSE_POINTER_ACCELERATION_ENABLED = true;
+        static final boolean DEFAULT_MOUSE_SCALING_ENABLED = true;
 
         /**
-         * Whether to enable mouse pointer acceleration on this display. Note that this only affects
+         * Whether to enable mouse pointer scaling on this display. Note that this only affects
          * pointer movements from mice (that is, pointing devices which send relative motions,
          * including trackballs and pointing sticks), not from other pointer devices such as
          * touchpads and styluses.
          */
-        public boolean mousePointerAccelerationEnabled;
+        public boolean mouseScalingEnabled;
 
         // Whether the pointer icon should be visible or hidden on this display.
         public boolean pointerIconVisible;
@@ -3734,12 +3734,12 @@
         }
 
         public boolean allDefaults() {
-            return mousePointerAccelerationEnabled == DEFAULT_MOUSE_POINTER_ACCELERATION_ENABLED
+            return mouseScalingEnabled == DEFAULT_MOUSE_SCALING_ENABLED
                     && pointerIconVisible == DEFAULT_POINTER_ICON_VISIBLE;
         }
 
         public void reset() {
-            mousePointerAccelerationEnabled = DEFAULT_MOUSE_POINTER_ACCELERATION_ENABLED;
+            mouseScalingEnabled = DEFAULT_MOUSE_SCALING_ENABLED;
             pointerIconVisible = DEFAULT_POINTER_ICON_VISIBLE;
         }
     }
@@ -3754,14 +3754,14 @@
                 mAdditionalDisplayInputProperties.put(displayId, properties);
             }
             final boolean oldPointerIconVisible = properties.pointerIconVisible;
-            final boolean oldMouseAccelerationEnabled = properties.mousePointerAccelerationEnabled;
+            final boolean oldMouseScalingEnabled = properties.mouseScalingEnabled;
             updater.accept(properties);
             if (oldPointerIconVisible != properties.pointerIconVisible) {
                 mNative.setPointerIconVisibility(displayId, properties.pointerIconVisible);
             }
-            if (oldMouseAccelerationEnabled != properties.mousePointerAccelerationEnabled) {
-                mNative.setMousePointerAccelerationEnabled(displayId,
-                        properties.mousePointerAccelerationEnabled);
+            if (oldMouseScalingEnabled != properties.mouseScalingEnabled) {
+                mNative.setMouseScalingEnabled(displayId,
+                        properties.mouseScalingEnabled);
             }
             if (properties.allDefaults()) {
                 mAdditionalDisplayInputProperties.remove(displayId);
diff --git a/services/core/java/com/android/server/input/InputSettingsObserver.java b/services/core/java/com/android/server/input/InputSettingsObserver.java
index 56cb6f49..e25ea4b 100644
--- a/services/core/java/com/android/server/input/InputSettingsObserver.java
+++ b/services/core/java/com/android/server/input/InputSettingsObserver.java
@@ -71,6 +71,11 @@
                         (reason) -> updateMouseSwapPrimaryButton()),
                 Map.entry(Settings.System.getUriFor(Settings.System.MOUSE_SCROLLING_ACCELERATION),
                         (reason) -> updateMouseScrollingAcceleration()),
+                Map.entry(Settings.System.getUriFor(
+                                Settings.System.MOUSE_POINTER_ACCELERATION_ENABLED),
+                        (reason) -> updateMouseAccelerationEnabled()),
+                Map.entry(Settings.System.getUriFor(Settings.System.MOUSE_SCROLLING_SPEED),
+                        (reason) -> updateMouseScrollingSpeed()),
                 Map.entry(Settings.System.getUriFor(Settings.System.TOUCHPAD_POINTER_SPEED),
                         (reason) -> updateTouchpadPointerSpeed()),
                 Map.entry(Settings.System.getUriFor(Settings.System.TOUCHPAD_NATURAL_SCROLLING),
@@ -191,6 +196,16 @@
                 InputSettings.isMouseScrollingAccelerationEnabled(mContext));
     }
 
+    private void updateMouseAccelerationEnabled() {
+        mNative.setMouseAccelerationEnabled(
+                InputSettings.isMousePointerAccelerationEnabled(mContext));
+    }
+
+    private void updateMouseScrollingSpeed() {
+        mNative.setMouseScrollingSpeed(
+                constrainPointerSpeedValue(InputSettings.getMouseScrollingSpeed(mContext)));
+    }
+
     private void updateTouchpadPointerSpeed() {
         mNative.setTouchpadPointerSpeed(
                 constrainPointerSpeedValue(InputSettings.getTouchpadPointerSpeed(mContext)));
diff --git a/services/core/java/com/android/server/input/NativeInputManagerService.java b/services/core/java/com/android/server/input/NativeInputManagerService.java
index ab5a680..4d38c84 100644
--- a/services/core/java/com/android/server/input/NativeInputManagerService.java
+++ b/services/core/java/com/android/server/input/NativeInputManagerService.java
@@ -130,14 +130,18 @@
 
     void setPointerSpeed(int speed);
 
-    void setMousePointerAccelerationEnabled(int displayId, boolean enabled);
+    void setMouseScalingEnabled(int displayId, boolean enabled);
 
     void setMouseReverseVerticalScrollingEnabled(boolean enabled);
 
     void setMouseScrollingAccelerationEnabled(boolean enabled);
 
+    void setMouseScrollingSpeed(int speed);
+
     void setMouseSwapPrimaryButtonEnabled(boolean enabled);
 
+    void setMouseAccelerationEnabled(boolean enabled);
+
     void setTouchpadPointerSpeed(int speed);
 
     void setTouchpadNaturalScrollingEnabled(boolean enabled);
@@ -417,7 +421,7 @@
         public native void setPointerSpeed(int speed);
 
         @Override
-        public native void setMousePointerAccelerationEnabled(int displayId, boolean enabled);
+        public native void setMouseScalingEnabled(int displayId, boolean enabled);
 
         @Override
         public native void setMouseReverseVerticalScrollingEnabled(boolean enabled);
@@ -426,9 +430,15 @@
         public native void setMouseScrollingAccelerationEnabled(boolean enabled);
 
         @Override
+        public native void setMouseScrollingSpeed(int speed);
+
+        @Override
         public native void setMouseSwapPrimaryButtonEnabled(boolean enabled);
 
         @Override
+        public native void setMouseAccelerationEnabled(boolean enabled);
+
+        @Override
         public native void setTouchpadPointerSpeed(int speed);
 
         @Override
diff --git a/services/core/java/com/android/server/input/debug/TouchpadDebugViewController.java b/services/core/java/com/android/server/input/debug/TouchpadDebugViewController.java
index 9cfbfa64..2c1d68e 100644
--- a/services/core/java/com/android/server/input/debug/TouchpadDebugViewController.java
+++ b/services/core/java/com/android/server/input/debug/TouchpadDebugViewController.java
@@ -163,9 +163,15 @@
                                             int deviceId) {
         mHandler.post(() -> {
             if (mTouchpadDebugView != null) {
-                mTouchpadDebugView.post(
-                        () -> mTouchpadDebugView.updateHardwareState(touchpadHardwareState,
-                                deviceId));
+                mTouchpadDebugView.post(() -> {
+                    // hideDebugView might have been called since we posted the action (e.g. if the
+                    // developer option toggle is clicked using the same touchpad currently being
+                    // visualized, b/376018148), so we need to check for null again.
+                    if (mTouchpadDebugView != null) {
+                        mTouchpadDebugView.updateHardwareState(touchpadHardwareState,
+                                deviceId);
+                    }
+                });
             }
         });
     }
@@ -177,8 +183,14 @@
     public void updateTouchpadGestureInfo(int gestureType, int deviceId) {
         mHandler.post(() -> {
             if (mTouchpadDebugView != null) {
-                mTouchpadDebugView.post(
-                        () -> mTouchpadDebugView.updateGestureInfo(gestureType, deviceId));
+                mTouchpadDebugView.post(() -> {
+                    // hideDebugView might have been called since we posted the action (e.g. if the
+                    // developer option toggle is clicked using the same touchpad currently being
+                    // visualized, b/376018148), so we need to check for null again.
+                    if (mTouchpadDebugView != null) {
+                        mTouchpadDebugView.updateGestureInfo(gestureType, deviceId);
+                    }
+                });
             }
         });
     }
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index 6053557..6780866 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -103,6 +103,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.DumpUtils;
+import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.util.Preconditions;
 import com.android.server.FgThread;
 import com.android.server.LocalServices;
@@ -537,11 +538,16 @@
         }
 
         if (Flags.populationDensityProvider()) {
+            long startTime = System.currentTimeMillis();
             setProxyPopulationDensityProvider(
                     ProxyPopulationDensityProvider.createAndRegister(mContext));
+            int duration = (int) (System.currentTimeMillis() - startTime);
             if (mPopulationDensityProvider == null) {
                 Log.e(TAG, "no population density provider found");
             }
+            FrameworkStatsLog.write(FrameworkStatsLog.POPULATION_DENSITY_PROVIDER_LOADING_REPORTED,
+                /* provider_null= */ (mPopulationDensityProvider == null),
+                /* provider_start_time_millis= */ duration);
         }
         if (mPopulationDensityProvider != null && Flags.densityBasedCoarseLocations()) {
             setLocationFudgerCache(new LocationFudgerCache(mPopulationDensityProvider));
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java
index 2c072d0..f5ed4d5 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java
@@ -16,6 +16,7 @@
 
 package com.android.server.location.contexthub;
 
+import android.app.AppOpsManager;
 import android.content.Context;
 import android.hardware.contexthub.EndpointInfo;
 import android.hardware.contexthub.ErrorCode;
@@ -23,18 +24,24 @@
 import android.hardware.contexthub.HubMessage;
 import android.hardware.contexthub.IContextHubEndpoint;
 import android.hardware.contexthub.IContextHubEndpointCallback;
+import android.hardware.contexthub.IEndpointCommunication;
 import android.hardware.contexthub.Message;
 import android.hardware.contexthub.MessageDeliveryStatus;
+import android.hardware.contexthub.Reason;
 import android.hardware.location.ContextHubTransaction;
 import android.hardware.location.IContextHubTransactionCallback;
+import android.os.Binder;
 import android.os.IBinder;
+import android.os.PowerManager;
+import android.os.PowerManager.WakeLock;
 import android.os.RemoteException;
+import android.os.WorkSource;
 import android.util.Log;
+import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
 
-import java.util.HashSet;
-import java.util.Set;
+import java.util.Collection;
 import java.util.concurrent.atomic.AtomicBoolean;
 
 /**
@@ -44,18 +51,34 @@
  * @hide
  */
 public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub
-        implements IBinder.DeathRecipient {
+        implements IBinder.DeathRecipient, AppOpsManager.OnOpChangedListener {
     private static final String TAG = "ContextHubEndpointBroker";
 
+    /** Message used by noteOp when this client receives a message from an endpoint. */
+    private static final String RECEIVE_MSG_NOTE = "ContextHubEndpointMessageDelivery";
+
+    /** The duration of wakelocks acquired during HAL callbacks */
+    private static final long WAKELOCK_TIMEOUT_MILLIS = 5 * 1000;
+
+    /*
+     * Internal interface used to invoke client callbacks.
+     */
+    interface CallbackConsumer {
+        void accept(IContextHubEndpointCallback callback) throws RemoteException;
+    }
+
     /** The context of the service. */
     private final Context mContext;
 
-    /** The proxy to talk to the Context Hub HAL. */
-    private final IContextHubWrapper mContextHubProxy;
+    /** The proxy to talk to the Context Hub HAL for endpoint communication. */
+    private final IEndpointCommunication mHubInterface;
 
     /** The manager that registered this endpoint. */
     private final ContextHubEndpointManager mEndpointManager;
 
+    /** Manager used for noting permissions usage of this broker. */
+    private final AppOpsManager mAppOpsManager;
+
     /** Metadata about this endpoint (app-facing container). */
     private final HubEndpointInfo mEndpointInfo;
 
@@ -70,40 +93,92 @@
 
     private final Object mOpenSessionLock = new Object();
 
-    /** The set of session IDs that are pending remote acceptance */
-    @GuardedBy("mOpenSessionLock")
-    private final Set<Integer> mPendingSessionIds = new HashSet<>();
+    static class SessionInfo {
+        enum SessionState {
+            /* The session is pending acceptance from the remote endpoint. */
+            PENDING,
+            /* The session is active and can transport messages. */
+            ACTIVE,
+        };
 
-    /** The set of session IDs that are actively enabled by this endpoint */
-    @GuardedBy("mOpenSessionLock")
-    private final Set<Integer> mActiveSessionIds = new HashSet<>();
+        private final HubEndpointInfo mRemoteEndpointInfo;
 
-    /** The set of session IDs that are actively enabled by the remote endpoint */
+        private SessionState mSessionState = SessionState.PENDING;
+
+        private final boolean mRemoteInitiated;
+
+        SessionInfo(HubEndpointInfo remoteEndpointInfo, boolean remoteInitiated) {
+            mRemoteEndpointInfo = remoteEndpointInfo;
+            mRemoteInitiated = remoteInitiated;
+        }
+
+        public boolean isRemoteInitiated() {
+            return mRemoteInitiated;
+        }
+
+        public HubEndpointInfo getRemoteEndpointInfo() {
+            return mRemoteEndpointInfo;
+        }
+
+        public void setSessionState(SessionState state) {
+            mSessionState = state;
+        }
+
+        public boolean isActive() {
+            return mSessionState == SessionState.ACTIVE;
+        }
+    }
+
+    /** A map between a session ID which maps to its current state. */
     @GuardedBy("mOpenSessionLock")
-    private final Set<Integer> mActiveRemoteSessionIds = new HashSet<>();
+    private final SparseArray<SessionInfo> mSessionInfoMap = new SparseArray<>();
 
     /** The package name of the app that created the endpoint */
     private final String mPackageName;
 
-    /* Transaction manager used for sending reliable messages */
+    /** The attribution tag of the module that created the endpoint */
+    private final String mAttributionTag;
+
+    /** Transaction manager used for sending reliable messages */
     private final ContextHubTransactionManager mTransactionManager;
 
+    /** The PID/UID of the endpoint package providing IContextHubEndpointCallback */
+    private final int mPid;
+
+    private final int mUid;
+
+    /** Wakelock held while nanoapp message are in flight to the client */
+    private final WakeLock mWakeLock;
+
     /* package */ ContextHubEndpointBroker(
             Context context,
-            IContextHubWrapper contextHubProxy,
+            IEndpointCommunication hubInterface,
             ContextHubEndpointManager endpointManager,
             EndpointInfo halEndpointInfo,
             IContextHubEndpointCallback callback,
             String packageName,
+            String attributionTag,
             ContextHubTransactionManager transactionManager) {
         mContext = context;
-        mContextHubProxy = contextHubProxy;
+        mHubInterface = hubInterface;
         mEndpointManager = endpointManager;
         mEndpointInfo = new HubEndpointInfo(halEndpointInfo);
         mHalEndpointInfo = halEndpointInfo;
         mContextHubEndpointCallback = callback;
         mPackageName = packageName;
+        mAttributionTag = attributionTag;
         mTransactionManager = transactionManager;
+
+        mPid = Binder.getCallingPid();
+        mUid = Binder.getCallingUid();
+
+        mAppOpsManager = context.getSystemService(AppOpsManager.class);
+        mAppOpsManager.startWatchingMode(AppOpsManager.OP_NONE, mPackageName, this);
+
+        PowerManager powerManager = context.getSystemService(PowerManager.class);
+        mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
+        mWakeLock.setWorkSource(new WorkSource(mUid, mPackageName));
+        mWakeLock.setReferenceCounted(true);
     }
 
     @Override
@@ -117,21 +192,25 @@
             throws RemoteException {
         super.openSession_enforcePermission();
         if (!mIsRegistered.get()) throw new IllegalStateException("Endpoint is not registered");
+        if (!hasEndpointPermissions(destination)) {
+            throw new SecurityException(
+                    "Insufficient permission to open a session with endpoint: " + destination);
+        }
+
         int sessionId = mEndpointManager.reserveSessionId();
         EndpointInfo halEndpointInfo = ContextHubServiceUtil.convertHalEndpointInfo(destination);
 
         synchronized (mOpenSessionLock) {
             try {
-                mPendingSessionIds.add(sessionId);
-                mContextHubProxy.openEndpointSession(
+                mSessionInfoMap.put(sessionId, new SessionInfo(destination, false));
+                mHubInterface.openEndpointSession(
                         sessionId,
                         halEndpointInfo.id,
                         mHalEndpointInfo.id,
                         serviceDescriptor);
             } catch (RemoteException | IllegalArgumentException | UnsupportedOperationException e) {
                 Log.e(TAG, "Exception while calling HAL openEndpointSession", e);
-                mPendingSessionIds.remove(sessionId);
-                mEndpointManager.returnSessionId(sessionId);
+                cleanupSessionResources(sessionId);
                 throw e;
             }
 
@@ -144,13 +223,11 @@
     public void closeSession(int sessionId, int reason) throws RemoteException {
         super.closeSession_enforcePermission();
         if (!mIsRegistered.get()) throw new IllegalStateException("Endpoint is not registered");
-        try {
-            mContextHubProxy.closeEndpointSession(
-                    sessionId, ContextHubServiceUtil.toHalReason(reason));
-        } catch (RemoteException | IllegalArgumentException | UnsupportedOperationException e) {
-            Log.e(TAG, "Exception while calling HAL closeEndpointSession", e);
-            throw e;
+        if (!cleanupSessionResources(sessionId)) {
+            throw new IllegalArgumentException(
+                    "Unknown session ID in closeSession: id=" + sessionId);
         }
+        halCloseEndpointSession(sessionId, ContextHubServiceUtil.toHalReason(reason));
     }
 
     @Override
@@ -159,20 +236,16 @@
         super.unregister_enforcePermission();
         mIsRegistered.set(false);
         try {
-            mContextHubProxy.unregisterEndpoint(mHalEndpointInfo);
+            mHubInterface.unregisterEndpoint(mHalEndpointInfo);
         } catch (RemoteException e) {
             Log.e(TAG, "RemoteException while calling HAL unregisterEndpoint", e);
         }
         synchronized (mOpenSessionLock) {
-            for (int id : mPendingSessionIds) {
-                mEndpointManager.returnSessionId(id);
+            // Iterate in reverse since cleanupSessionResources will remove the entry
+            for (int i = mSessionInfoMap.size() - 1; i >= 0; i--) {
+                int id = mSessionInfoMap.keyAt(i);
+                cleanupSessionResources(id);
             }
-            for (int id : mActiveSessionIds) {
-                mEndpointManager.returnSessionId(id);
-            }
-            mPendingSessionIds.clear();
-            mActiveSessionIds.clear();
-            mActiveRemoteSessionIds.clear();
         }
         mEndpointManager.unregisterEndpoint(mEndpointInfo.getIdentifier().getEndpoint());
     }
@@ -182,9 +255,14 @@
     public void openSessionRequestComplete(int sessionId) {
         super.openSessionRequestComplete_enforcePermission();
         synchronized (mOpenSessionLock) {
+            SessionInfo info = mSessionInfoMap.get(sessionId);
+            if (info == null) {
+                throw new IllegalArgumentException(
+                        "openSessionRequestComplete for invalid session id=" + sessionId);
+            }
             try {
-                mContextHubProxy.endpointSessionOpenComplete(sessionId);
-                mActiveRemoteSessionIds.add(sessionId);
+                mHubInterface.endpointSessionOpenComplete(sessionId);
+                info.setSessionState(SessionInfo.SessionState.ACTIVE);
             } catch (RemoteException | IllegalArgumentException | UnsupportedOperationException e) {
                 Log.e(TAG, "Exception while calling endpointSessionOpenComplete", e);
             }
@@ -197,25 +275,21 @@
             int sessionId, HubMessage message, IContextHubTransactionCallback callback) {
         super.sendMessage_enforcePermission();
         Message halMessage = ContextHubServiceUtil.createHalMessage(message);
-        synchronized (mOpenSessionLock) {
-            if (!mActiveSessionIds.contains(sessionId)
-                    && !mActiveRemoteSessionIds.contains(sessionId)) {
-                throw new SecurityException(
-                        "sendMessage called on inactive session (id= " + sessionId + ")");
-            }
+        if (!isSessionActive(sessionId)) {
+            throw new SecurityException(
+                    "sendMessage called on inactive session (id= " + sessionId + ")");
         }
 
-        // TODO(b/381102453): Handle permissions
         if (callback == null) {
             try {
-                mContextHubProxy.sendMessageToEndpoint(sessionId, halMessage);
+                mHubInterface.sendMessageToEndpoint(sessionId, halMessage);
             } catch (RemoteException e) {
                 Log.w(TAG, "Exception while sending message on session " + sessionId, e);
             }
         } else {
             ContextHubServiceTransaction transaction =
                     mTransactionManager.createSessionMessageTransaction(
-                            sessionId, halMessage, mPackageName, callback);
+                            mHubInterface, sessionId, halMessage, mPackageName, callback);
             try {
                 mTransactionManager.addTransaction(transaction);
             } catch (IllegalStateException e) {
@@ -240,7 +314,7 @@
         status.messageSequenceNumber = messageSeqNumber;
         status.errorCode = errorCode;
         try {
-            mContextHubProxy.sendMessageDeliveryStatusToEndpoint(sessionId, status);
+            mHubInterface.sendMessageDeliveryStatusToEndpoint(sessionId, status);
         } catch (RemoteException e) {
             Log.w(
                     TAG,
@@ -249,6 +323,13 @@
         }
     }
 
+    @Override
+    @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
+    public void onCallbackFinished() {
+        super.onCallbackFinished_enforcePermission();
+        releaseWakeLock();
+    }
+
     /** Invoked when the underlying binder of this broker has died at the client process. */
     @Override
     public void binderDied() {
@@ -257,6 +338,31 @@
         }
     }
 
+    @Override
+    public void onOpChanged(String op, String packageName) {
+        if (!packageName.equals(mPackageName)) {
+            Log.w(
+                    TAG,
+                    "onOpChanged called with invalid package "
+                            + packageName
+                            + " expected "
+                            + mPackageName);
+        } else {
+            synchronized (mOpenSessionLock) {
+                // Iterate in reverse since cleanupSessionResources will remove the entry
+                for (int i = mSessionInfoMap.size() - 1; i >= 0; i--) {
+                    int id = mSessionInfoMap.keyAt(i);
+                    HubEndpointInfo target = mSessionInfoMap.get(id).getRemoteEndpointInfo();
+                    if (!hasEndpointPermissions(target)) {
+                        halCloseEndpointSessionNoThrow(id, Reason.PERMISSION_DENIED);
+                        onCloseEndpointSession(id, Reason.PERMISSION_DENIED);
+                        // Resource cleanup is done in onCloseEndpointSession
+                    }
+                }
+            }
+        }
+    }
+
     /* package */ void attachDeathRecipient() throws RemoteException {
         if (mContextHubEndpointCallback != null) {
             mContextHubEndpointCallback.asBinder().linkToDeath(this, 0 /* flags */);
@@ -265,53 +371,98 @@
 
     /* package */ void onEndpointSessionOpenRequest(
             int sessionId, HubEndpointInfo initiator, String serviceDescriptor) {
-        if (mContextHubEndpointCallback != null) {
-            try {
-                mContextHubEndpointCallback.onSessionOpenRequest(
-                        sessionId, initiator, serviceDescriptor);
-            } catch (RemoteException e) {
-                Log.e(TAG, "RemoteException while calling onSessionOpenRequest", e);
+        if (!hasEndpointPermissions(initiator)) {
+            halCloseEndpointSessionNoThrow(sessionId, Reason.PERMISSION_DENIED);
+            return;
+        }
+
+        synchronized (mOpenSessionLock) {
+            if (hasSessionId(sessionId)) {
+                Log.e(TAG, "Existing session in onEndpointSessionOpenRequest: id=" + sessionId);
+                halCloseEndpointSessionNoThrow(sessionId, Reason.UNSPECIFIED);
+                return;
             }
+            mSessionInfoMap.put(sessionId, new SessionInfo(initiator, true));
+        }
+
+        boolean success =
+                invokeCallback(
+                        (consumer) ->
+                                consumer.onSessionOpenRequest(
+                                        sessionId, initiator, serviceDescriptor));
+        if (!success) {
+            cleanupSessionResources(sessionId);
         }
     }
 
     /* package */ void onCloseEndpointSession(int sessionId, byte reason) {
-        synchronized (mOpenSessionLock) {
-            mPendingSessionIds.remove(sessionId);
-            mActiveSessionIds.remove(sessionId);
-            mActiveRemoteSessionIds.remove(sessionId);
+        if (!cleanupSessionResources(sessionId)) {
+            Log.w(TAG, "Unknown session ID in onCloseEndpointSession: id=" + sessionId);
+            return;
         }
-        if (mContextHubEndpointCallback != null) {
-            try {
-                mContextHubEndpointCallback.onSessionClosed(
-                        sessionId, ContextHubServiceUtil.toAppHubEndpointReason(reason));
-            } catch (RemoteException e) {
-                Log.e(TAG, "RemoteException while calling onSessionClosed", e);
-            }
-        }
+
+        invokeCallback(
+                (consumer) ->
+                        consumer.onSessionClosed(
+                                sessionId, ContextHubServiceUtil.toAppHubEndpointReason(reason)));
     }
 
     /* package */ void onEndpointSessionOpenComplete(int sessionId) {
         synchronized (mOpenSessionLock) {
-            mPendingSessionIds.remove(sessionId);
-            mActiveSessionIds.add(sessionId);
-        }
-        if (mContextHubEndpointCallback != null) {
-            try {
-                mContextHubEndpointCallback.onSessionOpenComplete(sessionId);
-            } catch (RemoteException e) {
-                Log.e(TAG, "RemoteException while calling onSessionClosed", e);
+            if (!hasSessionId(sessionId)) {
+                Log.w(TAG, "Unknown session ID in onEndpointSessionOpenComplete: id=" + sessionId);
+                return;
             }
+            mSessionInfoMap.get(sessionId).setSessionState(SessionInfo.SessionState.ACTIVE);
         }
+
+        invokeCallback((consumer) -> consumer.onSessionOpenComplete(sessionId));
     }
 
     /* package */ void onMessageReceived(int sessionId, HubMessage message) {
-        if (mContextHubEndpointCallback != null) {
-            try {
-                mContextHubEndpointCallback.onMessageReceived(sessionId, message);
-            } catch (RemoteException e) {
-                Log.e(TAG, "RemoteException while calling onMessageReceived", e);
+        HubEndpointInfo remote;
+        synchronized (mOpenSessionLock) {
+            if (!isSessionActive(sessionId)) {
+                Log.e(
+                        TAG,
+                        "Dropping message for inactive session (id="
+                                + sessionId
+                                + ") with message: "
+                                + message);
+                sendMessageDeliveryStatus(
+                        sessionId, message.getMessageSequenceNumber(), ErrorCode.PERMANENT_ERROR);
+                return;
             }
+            remote = mSessionInfoMap.get(sessionId).getRemoteEndpointInfo();
+        }
+        if (!ContextHubServiceUtil.notePermissions(
+                mAppOpsManager,
+                mUid,
+                mPackageName,
+                mAttributionTag,
+                remote.getRequiredPermissions(),
+                RECEIVE_MSG_NOTE
+                        + "-0x"
+                        + Long.toHexString(remote.getIdentifier().getHub())
+                        + "-0x"
+                        + Long.toHexString(remote.getIdentifier().getEndpoint()))) {
+            Log.e(
+                    TAG,
+                    "Dropping message from "
+                            + remote
+                            + ". "
+                            + mPackageName
+                            + " doesn't have permission");
+            sendMessageDeliveryStatus(
+                    sessionId, message.getMessageSequenceNumber(), ErrorCode.PERMISSION_DENIED);
+            return;
+        }
+
+        boolean success =
+                invokeCallback((consumer) -> consumer.onMessageReceived(sessionId, message));
+        if (!success) {
+            sendMessageDeliveryStatus(
+                    sessionId, message.getMessageSequenceNumber(), ErrorCode.TRANSIENT_ERROR);
         }
     }
 
@@ -322,9 +473,108 @@
 
     /* package */ boolean hasSessionId(int sessionId) {
         synchronized (mOpenSessionLock) {
-            return mPendingSessionIds.contains(sessionId)
-                    || mActiveSessionIds.contains(sessionId)
-                    || mActiveRemoteSessionIds.contains(sessionId);
+            return mSessionInfoMap.contains(sessionId);
         }
     }
+
+    /**
+     * Calls the HAL closeEndpointSession API.
+     *
+     * @param sessionId The session ID to close
+     * @param halReason The HAL reason
+     */
+    private void halCloseEndpointSession(int sessionId, byte halReason) throws RemoteException {
+        try {
+            mHubInterface.closeEndpointSession(sessionId, halReason);
+        } catch (RemoteException | IllegalArgumentException | UnsupportedOperationException e) {
+            throw e;
+        }
+    }
+
+    /** Same as halCloseEndpointSession but does not throw the exception */
+    private void halCloseEndpointSessionNoThrow(int sessionId, byte halReason) {
+        try {
+            halCloseEndpointSession(sessionId, halReason);
+        } catch (RemoteException | IllegalArgumentException | UnsupportedOperationException e) {
+            Log.e(TAG, "Exception while calling HAL closeEndpointSession", e);
+        }
+    }
+
+    /**
+     * Cleans up resources related to a session with the provided ID.
+     *
+     * @param sessionId The session ID to clean up resources for
+     * @return false if the session ID was invalid
+     */
+    private boolean cleanupSessionResources(int sessionId) {
+        synchronized (mOpenSessionLock) {
+            SessionInfo info = mSessionInfoMap.get(sessionId);
+            if (info != null && !info.isRemoteInitiated()) {
+                mEndpointManager.returnSessionId(sessionId);
+                mSessionInfoMap.remove(sessionId);
+            }
+            return info != null;
+        }
+    }
+
+    /**
+     * @param sessionId The ID of the session to check
+     * @return true if the session with the given ID is currently active
+     */
+    private boolean isSessionActive(int sessionId) {
+        synchronized (mOpenSessionLock) {
+            return hasSessionId(sessionId) && mSessionInfoMap.get(sessionId).isActive();
+        }
+    }
+
+    /**
+     * @param targetEndpointInfo The target endpoint to check permissions for
+     * @return true if this endpoint has sufficient permission to the provided target endpoint
+     */
+    private boolean hasEndpointPermissions(HubEndpointInfo targetEndpointInfo) {
+        Collection<String> requiredPermissions = targetEndpointInfo.getRequiredPermissions();
+        return ContextHubServiceUtil.hasPermissions(mContext, mPid, mUid, requiredPermissions);
+    }
+
+    private void acquireWakeLock() {
+        Binder.withCleanCallingIdentity(
+                () -> {
+                    if (mIsRegistered.get()) {
+                        mWakeLock.acquire(WAKELOCK_TIMEOUT_MILLIS);
+                    }
+                });
+    }
+
+    private void releaseWakeLock() {
+        Binder.withCleanCallingIdentity(
+                () -> {
+                    if (mWakeLock.isHeld()) {
+                        try {
+                            mWakeLock.release();
+                        } catch (RuntimeException e) {
+                            Log.e(TAG, "Releasing the wakelock fails - ", e);
+                        }
+                    }
+                });
+    }
+
+    /**
+     * Invokes a callback and acquires a wakelock.
+     *
+     * @param consumer The callback invoke
+     * @return false if the callback threw a RemoteException
+     */
+    private boolean invokeCallback(CallbackConsumer consumer) {
+        if (mContextHubEndpointCallback != null) {
+            acquireWakeLock();
+            try {
+                consumer.accept(mContextHubEndpointCallback);
+            } catch (RemoteException e) {
+                Log.e(TAG, "RemoteException while calling endpoint callback", e);
+                releaseWakeLock();
+                return false;
+            }
+        }
+        return true;
+    }
 }
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java
index 07df7f9..740c4f1 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointManager.java
@@ -17,11 +17,14 @@
 package com.android.server.location.contexthub;
 
 import android.content.Context;
+import android.hardware.contexthub.ContextHubInfo;
 import android.hardware.contexthub.EndpointInfo;
 import android.hardware.contexthub.HubEndpointInfo;
+import android.hardware.contexthub.HubInfo;
 import android.hardware.contexthub.HubMessage;
 import android.hardware.contexthub.IContextHubEndpoint;
 import android.hardware.contexthub.IContextHubEndpointCallback;
+import android.hardware.contexthub.IEndpointCommunication;
 import android.os.RemoteException;
 import android.os.ServiceSpecificException;
 import android.util.Log;
@@ -33,6 +36,7 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.function.Consumer;
 
 /**
  * A class that manages registration/unregistration of clients and manages messages to/from clients.
@@ -75,11 +79,11 @@
     @GuardedBy("mEndpointLock")
     private long mNextEndpointId = -2;
 
-    /** The minimum session ID reservable by endpoints (retrieved from HAL) */
-    private final int mMinSessionId;
+    /** The minimum session ID reservable by endpoints (retrieved from HAL in init()) */
+    private int mMinSessionId = -1;
 
-    /** The minimum session ID reservable by endpoints (retrieved from HAL) */
-    private final int mMaxSessionId;
+    /** The minimum session ID reservable by endpoints (retrieved from HAL in init()) */
+    private int mMaxSessionId = -1;
 
     /** Variables for managing session ID creation */
     private final Object mSessionIdLock = new Object();
@@ -92,8 +96,11 @@
     @GuardedBy("mSessionIdLock")
     private int mNextSessionId = 0;
 
-    /** Initialized to true if all initialization in the constructor succeeds. */
-    private final boolean mSessionIdsValid;
+    /** Set true if init() succeeds */
+    private boolean mSessionIdsValid = false;
+
+    /** The interface for endpoint communication (retrieved from HAL in init()) */
+    private IEndpointCommunication mHubInterface = null;
 
     /* package */ ContextHubEndpointManager(
             Context context,
@@ -104,34 +111,73 @@
         mContextHubProxy = contextHubProxy;
         mHubInfoRegistry = hubInfoRegistry;
         mTransactionManager = transactionManager;
-        int[] range = null;
+    }
+
+    /**
+     * Initializes this class.
+     *
+     * This is separate from the constructor so that this may be passed into the callback registered
+     * with the HAL.
+     *
+     * @throws InstantiationException on any failure
+     */
+    /* package */ void init() throws InstantiationException {
+        if (mSessionIdsValid) {
+            throw new IllegalStateException("Already initialized");
+        }
         try {
-            range = mContextHubProxy.requestSessionIdRange(SERVICE_SESSION_RANGE);
-            if (range != null && range.length < SERVICE_SESSION_RANGE_LENGTH) {
-                Log.e(TAG, "Invalid session ID range: range array size = " + range.length);
-                range = null;
+            HubInfo info = new HubInfo();
+            info.hubId = SERVICE_HUB_ID;
+            // TODO(b/387291125): Populate the ContextHubInfo with real values.
+            ContextHubInfo contextHubInfo = new ContextHubInfo();
+            contextHubInfo.name = "";
+            contextHubInfo.vendor = "";
+            contextHubInfo.toolchain = "";
+            contextHubInfo.supportedPermissions = new String[0];
+            info.hubDetails = HubInfo.HubDetails.contextHubInfo(contextHubInfo);
+            mHubInterface = mContextHubProxy.registerEndpointHub(
+                    new ContextHubHalEndpointCallback(mHubInfoRegistry, this),
+                    info);
+            if (mHubInterface == null) {
+                throw new IllegalStateException("Received null IEndpointCommunication");
             }
-        } catch (RemoteException | IllegalArgumentException | ServiceSpecificException e) {
-            Log.e(TAG, "Exception while calling HAL requestSessionIdRange", e);
+        } catch (RemoteException | IllegalStateException | ServiceSpecificException
+                 | UnsupportedOperationException e) {
+            String error = "Failed to register ContextHubService as message hub";
+            Log.e(TAG, error, e);
+            throw new InstantiationException(error);
         }
 
-        if (range == null) {
-            mMinSessionId = -1;
-            mMaxSessionId = -1;
-            mSessionIdsValid = false;
-        } else {
-            mMinSessionId = range[0];
-            mMaxSessionId = range[1];
-            if (!isSessionIdRangeValid(mMinSessionId, mMaxSessionId)) {
-                Log.e(
-                        TAG,
-                        "Invalid session ID range: max=" + mMaxSessionId + " min=" + mMinSessionId);
-                mSessionIdsValid = false;
-            } else {
-                mNextSessionId = mMinSessionId;
-                mSessionIdsValid = true;
+        int[] range = null;
+        try {
+            range = mHubInterface.requestSessionIdRange(SERVICE_SESSION_RANGE);
+            if (range != null && range.length < SERVICE_SESSION_RANGE_LENGTH) {
+                String error = "Invalid session ID range: range array size = " + range.length;
+                Log.e(TAG, error);
+                unregisterHub();
+                throw new InstantiationException(error);
             }
+        } catch (RemoteException | IllegalArgumentException | ServiceSpecificException e) {
+            String error = "Exception while calling HAL requestSessionIdRange";
+            Log.e(TAG, error, e);
+            unregisterHub();
+            throw new InstantiationException(error);
         }
+
+        mMinSessionId = range[0];
+        mMaxSessionId = range[1];
+        if (!isSessionIdRangeValid(mMinSessionId, mMaxSessionId)) {
+            String error =
+                    "Invalid session ID range: max=" + mMaxSessionId + " min=" + mMinSessionId;
+            Log.e(TAG, error);
+            unregisterHub();
+            throw new InstantiationException(error);
+        }
+
+        synchronized (mSessionIdLock) {
+            mNextSessionId = mMinSessionId;
+        }
+        mSessionIdsValid = true;
     }
 
     /**
@@ -146,7 +192,8 @@
     /* package */ IContextHubEndpoint registerEndpoint(
             HubEndpointInfo pendingEndpointInfo,
             IContextHubEndpointCallback callback,
-            String packageName)
+            String packageName,
+            String attributionTag)
             throws RemoteException {
         if (!mSessionIdsValid) {
             throw new IllegalStateException("ContextHubEndpointManager failed to initialize");
@@ -157,7 +204,7 @@
                 ContextHubServiceUtil.createHalEndpointInfo(
                         pendingEndpointInfo, endpointId, SERVICE_HUB_ID);
         try {
-            mContextHubProxy.registerEndpoint(halEndpointInfo);
+            mHubInterface.registerEndpoint(halEndpointInfo);
         } catch (RemoteException e) {
             Log.e(TAG, "RemoteException while calling HAL registerEndpoint", e);
             throw e;
@@ -165,11 +212,12 @@
         broker =
                 new ContextHubEndpointBroker(
                         mContext,
-                        mContextHubProxy,
+                        mHubInterface,
                         this /* endpointManager */,
                         halEndpointInfo,
                         callback,
                         packageName,
+                        attributionTag,
                         mTransactionManager);
         mEndpointMap.put(endpointId, broker);
 
@@ -265,15 +313,9 @@
 
     @Override
     public void onCloseEndpointSession(int sessionId, byte reason) {
-        boolean callbackInvoked = false;
-        for (ContextHubEndpointBroker broker : mEndpointMap.values()) {
-            if (broker.hasSessionId(sessionId)) {
-                broker.onCloseEndpointSession(sessionId, reason);
-                callbackInvoked = true;
-                break;
-            }
-        }
-
+        boolean callbackInvoked =
+                invokeCallbackForMatchingSession(
+                        sessionId, (broker) -> broker.onCloseEndpointSession(sessionId, reason));
         if (!callbackInvoked) {
             Log.w(TAG, "onCloseEndpointSession: unknown session ID " + sessionId);
         }
@@ -281,15 +323,9 @@
 
     @Override
     public void onEndpointSessionOpenComplete(int sessionId) {
-        boolean callbackInvoked = false;
-        for (ContextHubEndpointBroker broker : mEndpointMap.values()) {
-            if (broker.hasSessionId(sessionId)) {
-                broker.onEndpointSessionOpenComplete(sessionId);
-                callbackInvoked = true;
-                break;
-            }
-        }
-
+        boolean callbackInvoked =
+                invokeCallbackForMatchingSession(
+                        sessionId, (broker) -> broker.onEndpointSessionOpenComplete(sessionId));
         if (!callbackInvoked) {
             Log.w(TAG, "onEndpointSessionOpenComplete: unknown session ID " + sessionId);
         }
@@ -297,15 +333,9 @@
 
     @Override
     public void onMessageReceived(int sessionId, HubMessage message) {
-        boolean callbackInvoked = false;
-        for (ContextHubEndpointBroker broker : mEndpointMap.values()) {
-            if (broker.hasSessionId(sessionId)) {
-                broker.onMessageReceived(sessionId, message);
-                callbackInvoked = true;
-                break;
-            }
-        }
-
+        boolean callbackInvoked =
+                invokeCallbackForMatchingSession(
+                        sessionId, (broker) -> broker.onMessageReceived(sessionId, message));
         if (!callbackInvoked) {
             Log.w(TAG, "onMessageReceived: unknown session ID " + sessionId);
         }
@@ -313,20 +343,47 @@
 
     @Override
     public void onMessageDeliveryStatusReceived(int sessionId, int sequenceNumber, byte errorCode) {
-        boolean callbackInvoked = false;
-        for (ContextHubEndpointBroker broker : mEndpointMap.values()) {
-            if (broker.hasSessionId(sessionId)) {
-                broker.onMessageDeliveryStatusReceived(sessionId, sequenceNumber, errorCode);
-                callbackInvoked = true;
-                break;
-            }
-        }
-
+        boolean callbackInvoked =
+                invokeCallbackForMatchingSession(
+                        sessionId,
+                        (broker) ->
+                                broker.onMessageDeliveryStatusReceived(
+                                        sessionId, sequenceNumber, errorCode));
         if (!callbackInvoked) {
             Log.w(TAG, "onMessageDeliveryStatusReceived: unknown session ID " + sessionId);
         }
     }
 
+    /**
+     * Invokes a callback for a session with matching ID.
+     *
+     * @param callback The callback to execute
+     * @return true if a callback was executed
+     */
+    private boolean invokeCallbackForMatchingSession(
+            int sessionId, Consumer<ContextHubEndpointBroker> callback) {
+        for (ContextHubEndpointBroker broker : mEndpointMap.values()) {
+            if (broker.hasSessionId(sessionId)) {
+                try {
+                    callback.accept(broker);
+                } catch (RuntimeException e) {
+                    Log.e(TAG, "Exception while invoking callback", e);
+                }
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /** Unregister the hub (called during init() failure). Silence errors. */
+    private void unregisterHub() {
+        try {
+            mHubInterface.unregister();
+        } catch (RemoteException | IllegalStateException e) {
+            Log.e(TAG, "Failed to unregister from HAL on init failure", e);
+        }
+    }
+
     /** @return an available endpoint ID */
     private long getNewEndpointId() {
         synchronized (mEndpointLock) {
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubHalEndpointCallback.java b/services/core/java/com/android/server/location/contexthub/ContextHubHalEndpointCallback.java
index f1f2217..88764b6 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubHalEndpointCallback.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubHalEndpointCallback.java
@@ -21,6 +21,9 @@
 import android.hardware.contexthub.IEndpointCallback;
 import android.hardware.contexthub.Message;
 import android.hardware.contexthub.MessageDeliveryStatus;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Process;
 import android.os.RemoteException;
 
 /** IEndpointCallback implementation. */
@@ -29,6 +32,11 @@
     private final IEndpointLifecycleCallback mEndpointLifecycleCallback;
     private final IEndpointSessionCallback mEndpointSessionCallback;
 
+    // Use this thread in case where the execution requires to be on an async service thread.
+    private final HandlerThread mHandlerThread =
+            new HandlerThread("Context Hub endpoint callback", Process.THREAD_PRIORITY_BACKGROUND);
+    private Handler mHandler;
+
     /** Interface for listening for endpoint start and stop events. */
     public interface IEndpointLifecycleCallback {
         /** Called when a batch of endpoints started. */
@@ -65,6 +73,9 @@
             IEndpointSessionCallback endpointSessionCallback) {
         mEndpointLifecycleCallback = endpointLifecycleCallback;
         mEndpointSessionCallback = endpointSessionCallback;
+
+        mHandlerThread.start();
+        mHandler = new Handler(mHandlerThread.getLooper());
     }
 
     @Override
@@ -77,7 +88,7 @@
         for (int i = 0; i < halEndpointInfos.length; i++) {
             endpointInfos[i] = new HubEndpointInfo(halEndpointInfos[i]);
         }
-        mEndpointLifecycleCallback.onEndpointStarted(endpointInfos);
+        mHandler.post(() -> mEndpointLifecycleCallback.onEndpointStarted(endpointInfos));
     }
 
     @Override
@@ -87,40 +98,48 @@
         for (int i = 0; i < halEndpointIds.length; i++) {
             endpointIds[i] = new HubEndpointInfo.HubEndpointIdentifier(halEndpointIds[i]);
         }
-        mEndpointLifecycleCallback.onEndpointStopped(endpointIds, reason);
+        mHandler.post(() -> mEndpointLifecycleCallback.onEndpointStopped(endpointIds, reason));
     }
 
     @Override
     public void onEndpointSessionOpenRequest(
-            int i, EndpointId destination, EndpointId initiator, String s) throws RemoteException {
+            int sessionId, EndpointId destination, EndpointId initiator, String serviceDescriptor)
+            throws RemoteException {
         HubEndpointInfo.HubEndpointIdentifier destinationId =
                 new HubEndpointInfo.HubEndpointIdentifier(destination.hubId, destination.id);
         HubEndpointInfo.HubEndpointIdentifier initiatorId =
                 new HubEndpointInfo.HubEndpointIdentifier(initiator.hubId, initiator.id);
-        mEndpointSessionCallback.onEndpointSessionOpenRequest(i, destinationId, initiatorId, s);
+        mHandler.post(
+                () ->
+                        mEndpointSessionCallback.onEndpointSessionOpenRequest(
+                                sessionId, destinationId, initiatorId, serviceDescriptor));
     }
 
     @Override
-    public void onCloseEndpointSession(int i, byte b) throws RemoteException {
-        mEndpointSessionCallback.onCloseEndpointSession(i, b);
+    public void onCloseEndpointSession(int sessionId, byte reason) throws RemoteException {
+        mHandler.post(() -> mEndpointSessionCallback.onCloseEndpointSession(sessionId, reason));
     }
 
     @Override
-    public void onEndpointSessionOpenComplete(int i) throws RemoteException {
-        mEndpointSessionCallback.onEndpointSessionOpenComplete(i);
+    public void onEndpointSessionOpenComplete(int sessionId) throws RemoteException {
+        mHandler.post(() -> mEndpointSessionCallback.onEndpointSessionOpenComplete(sessionId));
     }
 
     @Override
-    public void onMessageReceived(int i, Message message) throws RemoteException {
+    public void onMessageReceived(int sessionId, Message message) throws RemoteException {
         HubMessage hubMessage = ContextHubServiceUtil.createHubMessage(message);
-        mEndpointSessionCallback.onMessageReceived(i, hubMessage);
+        mHandler.post(() -> mEndpointSessionCallback.onMessageReceived(sessionId, hubMessage));
     }
 
     @Override
-    public void onMessageDeliveryStatusReceived(int i, MessageDeliveryStatus messageDeliveryStatus)
-            throws RemoteException {
-        mEndpointSessionCallback.onMessageDeliveryStatusReceived(
-                i, messageDeliveryStatus.messageSequenceNumber, messageDeliveryStatus.errorCode);
+    public void onMessageDeliveryStatusReceived(
+            int sessionId, MessageDeliveryStatus messageDeliveryStatus) throws RemoteException {
+        mHandler.post(
+                () ->
+                        mEndpointSessionCallback.onMessageDeliveryStatusReceived(
+                                sessionId,
+                                messageDeliveryStatus.messageSequenceNumber,
+                                messageDeliveryStatus.errorCode));
     }
 
     @Override
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
index 165f9d3..2615a76 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
@@ -256,7 +256,9 @@
         public void handleServiceRestart() {
             Log.i(TAG, "Recovering from Context Hub HAL restart...");
             initExistingCallbacks();
-            mHubInfoRegistry.onHalRestart();
+            if (mHubInfoRegistry != null) {
+                mHubInfoRegistry.onHalRestart();
+            }
             resetSettings();
             if (Flags.reconnectHostEndpointsAfterHalRestart()) {
                 mClientManager.forEachClientOfHub(mContextHubId,
@@ -336,8 +338,9 @@
                 mEndpointManager =
                         new ContextHubEndpointManager(
                                 mContext, mContextHubWrapper, registry, mTransactionManager);
+                mEndpointManager.init();
                 Log.i(TAG, "Enabling generic offload API");
-            } catch (UnsupportedOperationException e) {
+            } catch (InstantiationException e) {
                 mEndpointManager = null;
                 registry = null;
                 Log.w(TAG, "Generic offload API not supported, disabling");
@@ -350,7 +353,6 @@
         }
 
         initDefaultClientMap();
-        initEndpointCallback();
 
         initLocationSettingNotifications();
         initWifiSettingNotifications();
@@ -529,18 +531,6 @@
         mDefaultClientMap = Collections.unmodifiableMap(defaultClientMap);
     }
 
-    private void initEndpointCallback() {
-        if (mHubInfoRegistry == null) {
-            return;
-        }
-        try {
-            mContextHubWrapper.registerEndpointCallback(
-                    new ContextHubHalEndpointCallback(mHubInfoRegistry, mEndpointManager));
-        } catch (RemoteException | UnsupportedOperationException e) {
-            Log.e(TAG, "Exception while registering IEndpointCallback", e);
-        }
-    }
-
     /**
      * Initializes existing callbacks with the mContextHubWrapper for every context hub
      */
@@ -766,9 +756,7 @@
     @Override
     public List<HubInfo> getHubs() throws RemoteException {
         super.getHubs_enforcePermission();
-        if (mHubInfoRegistry == null) {
-            return Collections.emptyList();
-        }
+        checkHubDiscoveryPreconditions();
         return mHubInfoRegistry.getHubs();
     }
 
@@ -776,9 +764,7 @@
     @Override
     public List<HubEndpointInfo> findEndpoints(long endpointId) {
         super.findEndpoints_enforcePermission();
-        if (mHubInfoRegistry == null) {
-            return Collections.emptyList();
-        }
+        checkEndpointDiscoveryPreconditions();
         return mHubInfoRegistry.findEndpoints(endpointId);
     }
 
@@ -786,9 +772,7 @@
     @Override
     public List<HubEndpointInfo> findEndpointsWithService(String serviceDescriptor) {
         super.findEndpointsWithService_enforcePermission();
-        if (mHubInfoRegistry == null) {
-            return Collections.emptyList();
-        }
+        checkEndpointDiscoveryPreconditions();
         return mHubInfoRegistry.findEndpointsWithService(serviceDescriptor);
     }
 
@@ -797,14 +781,16 @@
     public IContextHubEndpoint registerEndpoint(
             HubEndpointInfo pendingHubEndpointInfo,
             IContextHubEndpointCallback callback,
-            String packageName)
+            String packageName,
+            String attributionTag)
             throws RemoteException {
         super.registerEndpoint_enforcePermission();
         if (mEndpointManager == null) {
             Log.e(TAG, "Endpoint manager failed to initialize");
             throw new UnsupportedOperationException("Endpoint registration is not supported");
         }
-        return mEndpointManager.registerEndpoint(pendingHubEndpointInfo, callback, packageName);
+        return mEndpointManager.registerEndpoint(
+                pendingHubEndpointInfo, callback, packageName, attributionTag);
     }
 
     @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
@@ -842,6 +828,13 @@
         }
     }
 
+    private void checkHubDiscoveryPreconditions() {
+        if (mHubInfoRegistry == null) {
+            Log.e(TAG, "Hub registry failed to initialize");
+            throw new UnsupportedOperationException("Hub discovery is not supported");
+        }
+    }
+
     /**
      * Creates an internal load transaction callback to be used for old API clients
      *
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java b/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java
index 7b59d6f1..a41194b 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubServiceUtil.java
@@ -601,7 +601,7 @@
             int uid,
             String packageName,
             String attributionTag,
-            List<String> permissions,
+            Collection<String> permissions,
             String noteMessage) {
         for (String permission : permissions) {
             int opCode = AppOpsManager.permissionToOpCode(permission);
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java b/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java
index 5dd40ea..a430a82 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubTransactionManager.java
@@ -17,6 +17,7 @@
 package com.android.server.location.contexthub;
 
 import android.chre.flags.Flags;
+import android.hardware.contexthub.IEndpointCommunication;
 import android.hardware.contexthub.Message;
 import android.hardware.location.ContextHubTransaction;
 import android.hardware.location.IContextHubTransactionCallback;
@@ -402,12 +403,14 @@
     /**
      * Creates a transaction to send a message through a session.
      *
+     * @param hubInterface Interface for interacting with other endpoint hubs.
      * @param sessionId The ID of the endpoint session the message should be sent through.
      * @param message The message to send.
      * @param transactionCallback The callback of the transactions.
      * @return The generated transaction.
      */
     /* package */ ContextHubServiceTransaction createSessionMessageTransaction(
+            IEndpointCommunication hubInterface,
             int sessionId,
             Message message,
             String packageName,
@@ -422,7 +425,7 @@
             /* package */ int onTransact() {
                 try {
                     message.sequenceNumber = getMessageSequenceNumber();
-                    mContextHubProxy.sendMessageToEndpoint(sessionId, message);
+                    hubInterface.sendMessageToEndpoint(sessionId, message);
                     return ContextHubTransaction.RESULT_SUCCESS;
                 } catch (RemoteException e) {
                     Log.e(TAG, "RemoteException while trying to send a session message", e);
diff --git a/services/core/java/com/android/server/location/contexthub/HubInfoRegistry.java b/services/core/java/com/android/server/location/contexthub/HubInfoRegistry.java
index 503f1ac..6e650c2 100644
--- a/services/core/java/com/android/server/location/contexthub/HubInfoRegistry.java
+++ b/services/core/java/com/android/server/location/contexthub/HubInfoRegistry.java
@@ -98,10 +98,16 @@
 
     private final Object mCallbackLock = new Object();
 
-    HubInfoRegistry(IContextHubWrapper contextHubWrapper) {
+    HubInfoRegistry(IContextHubWrapper contextHubWrapper) throws InstantiationException {
         mContextHubWrapper = contextHubWrapper;
-        refreshCachedHubs();
-        refreshCachedEndpoints();
+        try {
+            refreshCachedHubs();
+            refreshCachedEndpoints();
+        } catch (UnsupportedOperationException e) {
+            String error = "Failed to update hub and endpoint cache";
+            Log.e(TAG, error, e);
+            throw new InstantiationException(error);
+        }
     }
 
     /** Retrieve the list of hubs available. */
diff --git a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
index e1df503..a9bd38f 100644
--- a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
+++ b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
@@ -18,10 +18,8 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.chre.flags.Flags;
-import android.hardware.contexthub.EndpointId;
 import android.hardware.contexthub.HostEndpointInfo;
 import android.hardware.contexthub.HubEndpointInfo;
-import android.hardware.contexthub.Message;
 import android.hardware.contexthub.MessageDeliveryStatus;
 import android.hardware.contexthub.NanSessionRequest;
 import android.hardware.contexthub.V1_0.ContextHub;
@@ -238,40 +236,13 @@
     }
 
     /** Calls the appropriate registerEndpointCallback function depending on the HAL version. */
-    public void registerEndpointCallback(android.hardware.contexthub.IEndpointCallback cb)
-            throws RemoteException {}
-
-    /** Registers the endpoint with the ContextHub HAL */
-    public void registerEndpoint(android.hardware.contexthub.EndpointInfo info)
-            throws RemoteException {}
-
-    /** Unregisters a previously registered endpoint */
-    public int[] requestSessionIdRange(int size) throws RemoteException {
-        return null;
+    public android.hardware.contexthub.IEndpointCommunication registerEndpointHub(
+            android.hardware.contexthub.IEndpointCallback cb,
+            android.hardware.contexthub.HubInfo hubInfo)
+                    throws RemoteException {
+        throw new UnsupportedOperationException();
     }
 
-    /** Opens an endpoint session between two endpoints */
-    public void openEndpointSession(
-            int sessionId, EndpointId destination, EndpointId initiator, String serviceDescriptor)
-            throws RemoteException {}
-
-    /** Closes a previously opened endpoint */
-    public void closeEndpointSession(int sessionId, byte reason) throws RemoteException {}
-
-    /** Unregisters a previously registered endpoint */
-    public void unregisterEndpoint(android.hardware.contexthub.EndpointInfo info)
-            throws RemoteException {}
-
-    /** Notifies the completion of a session opened by the HAL */
-    public void endpointSessionOpenComplete(int sessionId) throws RemoteException {}
-
-    /** Sends a message to a remote endpoint */
-    public void sendMessageToEndpoint(int sessionId, Message msg) throws RemoteException {}
-
-    /** Sends a message delivery status to a remote endpoint */
-    public void sendMessageDeliveryStatusToEndpoint(int sessionId, MessageDeliveryStatus msgStatus)
-            throws RemoteException {}
-
     /**
      * @return True if this version of the Contexthub HAL supports Location setting notifications.
      */
@@ -691,97 +662,19 @@
         }
 
         @Override
-        public void registerEndpointCallback(android.hardware.contexthub.IEndpointCallback cb)
-                throws RemoteException {
-            android.hardware.contexthub.IContextHub hub = getHub();
-            if (hub == null) {
-                return;
-            }
-
-            if (DEBUG) {
-                Log.i(TAG, "registerEndpointCallback: cb=" + cb);
-            }
-            hub.registerEndpointCallback(cb);
-        }
-
-        @Override
-        public void registerEndpoint(android.hardware.contexthub.EndpointInfo info)
-                throws RemoteException {
-            android.hardware.contexthub.IContextHub hub = getHub();
-            if (hub == null) {
-                return;
-            }
-            hub.registerEndpoint(info);
-        }
-
-        @Override
-        public int[] requestSessionIdRange(int size) throws RemoteException {
+        public android.hardware.contexthub.IEndpointCommunication registerEndpointHub(
+                android.hardware.contexthub.IEndpointCallback cb,
+                android.hardware.contexthub.HubInfo hubInfo)
+                        throws RemoteException {
             android.hardware.contexthub.IContextHub hub = getHub();
             if (hub == null) {
                 return null;
             }
-            return hub.requestSessionIdRange(size);
-        }
 
-        @Override
-        public void openEndpointSession(
-                int sessionId,
-                EndpointId destination,
-                EndpointId initiator,
-                String serviceDescriptor)
-                throws RemoteException {
-            android.hardware.contexthub.IContextHub hub = getHub();
-            if (hub == null) {
-                return;
+            if (DEBUG) {
+                Log.i(TAG, "registerEndpointHub: cb=" + cb);
             }
-            hub.openEndpointSession(sessionId, destination, initiator, serviceDescriptor);
-        }
-
-        @Override
-        public void closeEndpointSession(int sessionId, byte reason) throws RemoteException {
-            android.hardware.contexthub.IContextHub hub = getHub();
-            if (hub == null) {
-                return;
-            }
-            hub.closeEndpointSession(sessionId, reason);
-        }
-
-        @Override
-        public void unregisterEndpoint(android.hardware.contexthub.EndpointInfo info)
-                throws RemoteException {
-            android.hardware.contexthub.IContextHub hub = getHub();
-            if (hub == null) {
-                return;
-            }
-            hub.unregisterEndpoint(info);
-        }
-
-        @Override
-        public void endpointSessionOpenComplete(int sessionId) throws RemoteException {
-            android.hardware.contexthub.IContextHub hub = getHub();
-            if (hub == null) {
-                return;
-            }
-            hub.endpointSessionOpenComplete(sessionId);
-        }
-
-        @Override
-        public void sendMessageToEndpoint(int sessionId, Message msg) throws RemoteException {
-            android.hardware.contexthub.IContextHub hub = getHub();
-            if (hub == null) {
-                return;
-            }
-            hub.sendMessageToEndpoint(sessionId, msg);
-        }
-
-        @Override
-        public void sendMessageDeliveryStatusToEndpoint(
-                int sessionId, MessageDeliveryStatus msgStatus) throws RemoteException {
-            android.hardware.contexthub.IContextHub hub = getHub();
-            if (hub == null) {
-                return;
-            }
-            hub.sendMessageDeliveryStatusToEndpoint(sessionId, msgStatus);
+            return hub.registerEndpointHub(cb, hubInfo);
         }
 
         public boolean supportsLocationSettingNotifications() {
diff --git a/services/core/java/com/android/server/location/fudger/LocationFudger.java b/services/core/java/com/android/server/location/fudger/LocationFudger.java
index bbd8aa1..2757764 100644
--- a/services/core/java/com/android/server/location/fudger/LocationFudger.java
+++ b/services/core/java/com/android/server/location/fudger/LocationFudger.java
@@ -203,7 +203,7 @@
             } else {
                 // Try to fetch the default value. The answer won't come in time, but will be used
                 // for the next location to coarsen.
-                cacheCopy.fetchDefaultCoarseningLevelIfNeeded();
+                cacheCopy.onDefaultCoarseningLevelNotSet();
                 // Previous algorithm that snaps to a grid of width mAccuracyM.
                 coarsened = snapToGrid(latitude, longitude);
             }
diff --git a/services/core/java/com/android/server/location/fudger/LocationFudgerCache.java b/services/core/java/com/android/server/location/fudger/LocationFudgerCache.java
index 19ec38c..3ef2b56 100644
--- a/services/core/java/com/android/server/location/fudger/LocationFudgerCache.java
+++ b/services/core/java/com/android/server/location/fudger/LocationFudgerCache.java
@@ -26,6 +26,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.location.geometry.S2CellIdUtils;
+import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.location.provider.proxy.ProxyPopulationDensityProvider;
 
 import java.util.Objects;
@@ -68,6 +69,15 @@
     // The provider that asynchronously provides what is stored in the cache.
     private final ProxyPopulationDensityProvider mPopulationDensityProvider;
 
+    // If two calls to logDensityBasedLocsUsed are made in an interval shorter than this value,
+    // the second is dropped.
+    protected static final int LOG_DENSITY_BASED_LOCS_USED_RATE_LIMIT_MS = 1000 * 60 * 10; // 10 min
+
+    // The system time at which the last query to logDensityBasedLocsUsed was made.
+    // Initialized to -LOG_DENSITY_BASED_LOCS_USED_RATE_LIMIT_MS, so even if made at time 0, the
+    // first call succeeds.
+    private long mLastQueryToLogDensityBasedLocsUsedMs = -LOG_DENSITY_BASED_LOCS_USED_RATE_LIMIT_MS;
+
     private static String sTAG = "LocationFudgerCache";
 
     public LocationFudgerCache(@NonNull ProxyPopulationDensityProvider provider) {
@@ -76,11 +86,17 @@
         asyncFetchDefaultCoarseningLevel();
     }
 
-    /** If the cache's default coarsening value hasn't been set, asynchronously fetches it. */
-    public void fetchDefaultCoarseningLevelIfNeeded() {
+    /**
+     * Called by the LocationFudger when a query couldn't be fulfilled because the cache isn't set.
+     */
+    public void onDefaultCoarseningLevelNotSet() {
         if (!hasDefaultValue()) {
             asyncFetchDefaultCoarseningLevel();
         }
+        logDensityBasedLocsUsed(/* nowMs=*/ System.currentTimeMillis(),
+            /* skippedNoDefault= */ true,
+            /* isCacheHit= */ false,
+            /* defaultCoarseningLevel= */ -1);
     }
 
     /** Returns true if the cache has successfully received a default value from the provider. */
@@ -101,17 +117,46 @@
             asyncFetchDefaultCoarseningLevel();
         }
         Long s2CellId = readCacheForLatLng(latitudeDegrees, longitudeDegrees);
+        int defaultLevel = getDefaultCoarseningLevel();
         if (s2CellId == null) {
             // Asynchronously queries the density from the provider. The answer won't come in time,
             // but it will update the cache for the following queries.
             refreshCache(latitudeDegrees, longitudeDegrees);
 
-            return getDefaultCoarseningLevel();
+            logDensityBasedLocsUsed(/* nowMs=*/ System.currentTimeMillis(),
+                    /* skippedNoDefault= */ false,
+                    /* isCacheHit= */ false,
+                    /* defaultCoarseningLevel= */ defaultLevel);
+            return defaultLevel;
         }
+        logDensityBasedLocsUsed(/* nowMs=*/ System.currentTimeMillis(),
+            /* skippedNoDefault= */ false,
+            /* isCacheHit= */ true,
+            /* defaultCoarseningLevel= */ defaultLevel);
         return S2CellIdUtils.getLevel(s2CellId);
     }
 
     /**
+     * A simple wrapper around FrameworkStatsLog.write() that rate-limits the calls.
+     * Returns true on success, false if the call was dropped.
+     */
+    protected boolean logDensityBasedLocsUsed(long nowMs, boolean skippedNoDefault,
+            boolean isCacheHit, int defaultCoarseningLevel) {
+
+        if (nowMs - mLastQueryToLogDensityBasedLocsUsedMs
+                < LOG_DENSITY_BASED_LOCS_USED_RATE_LIMIT_MS) {
+            return false;
+        }
+        mLastQueryToLogDensityBasedLocsUsedMs = nowMs;
+
+        FrameworkStatsLog.write(FrameworkStatsLog.DENSITY_BASED_COARSE_LOCATIONS_USAGE_REPORTED,
+                /* skipped_no_default= */ skippedNoDefault,
+                /* is_cache_hit= */ isCacheHit,
+                /* default_coarsening_level= */ defaultCoarseningLevel);
+        return true;
+    }
+
+    /**
      * If the cache contains the current location, returns the corresponding S2 cell id.
      * Otherwise, returns null.
      */
@@ -176,15 +221,26 @@
      *  Queries the population density provider and store the result in the cache.
      */
     private void refreshCache(double latitude, double longitude) {
+        long startTime = System.currentTimeMillis();
         IS2CellIdsCallback callback = new IS2CellIdsCallback.Stub() {
             @Override
             public void onResult(long[] s2CellIds) {
+                int durationMs = (int) (System.currentTimeMillis() - startTime);
+                FrameworkStatsLog.write(
+                        FrameworkStatsLog.DENSITY_BASED_COARSE_LOCATIONS_PROVIDER_QUERY_REPORTED,
+                        /* query_duration_millis= */ durationMs,
+                        /* is_error= */ false);
                 addToCache(s2CellIds);
             }
 
             @Override
             public void onError() {
                 Log.e(sTAG, "could not get population density");
+                int durationMs = (int) (System.currentTimeMillis() - startTime);
+                FrameworkStatsLog.write(
+                        FrameworkStatsLog.DENSITY_BASED_COARSE_LOCATIONS_PROVIDER_QUERY_REPORTED,
+                        /* query_duration_millis= */ durationMs,
+                        /* is_error= */ true);
             }
         };
         mPopulationDensityProvider.getCoarsenedS2Cells(latitude, longitude, MAX_CACHE_SIZE - 1,
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index c314ab0..286238e 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -369,16 +369,7 @@
         @Override
         public void onBootPhase(int phase) {
             super.onBootPhase(phase);
-            if (phase == PHASE_ACTIVITY_MANAGER_READY) {
-                mLockSettingsService.migrateOldDataAfterSystemReady();
-                mLockSettingsService.deleteRepairModePersistentDataIfNeeded();
-            } else if (phase == PHASE_BOOT_COMPLETED) {
-                // In the case of an upgrade, PHASE_BOOT_COMPLETED means that a rollback to the old
-                // build can no longer occur.  This is the time to destroy any migrated protectors.
-                mLockSettingsService.destroyMigratedProtectors();
-
-                mLockSettingsService.loadEscrowData();
-            }
+            mLockSettingsService.onBootPhase(phase);
         }
 
         @Override
@@ -397,6 +388,21 @@
         }
     }
 
+    private void onBootPhase(int phase) {
+        if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
+            migrateOldDataAfterSystemReady();
+            deleteRepairModePersistentDataIfNeeded();
+        } else if (phase == SystemService.PHASE_BOOT_COMPLETED) {
+            mHandler.post(() -> {
+                // In the case of an upgrade, PHASE_BOOT_COMPLETED means that a rollback to the old
+                // build can no longer occur.  This is the time to destroy any migrated protectors.
+                destroyMigratedProtectors();
+
+                loadEscrowData();
+            });
+        }
+    }
+
     @VisibleForTesting
     protected static class SynchronizedStrongAuthTracker
             extends LockPatternUtils.StrongAuthTracker {
@@ -445,6 +451,7 @@
      * @param profileUserId  profile user Id
      * @param profileUserPassword  profile original password (when it has separated lock).
      */
+    @GuardedBy("mSpManager")
     private void tieProfileLockIfNecessary(int profileUserId,
             LockscreenCredential profileUserPassword) {
         // Only for profiles that shares credential with parent
@@ -903,14 +910,8 @@
                 // Hide notification first, as tie profile lock takes time
                 hideEncryptionNotification(new UserHandle(userId));
 
-                if (android.app.admin.flags.Flags.fixRaceConditionInTieProfileLock()) {
-                    synchronized (mSpManager) {
-                        tieProfileLockIfNecessary(userId, LockscreenCredential.createNone());
-                    }
-                } else {
-                    if (isCredentialSharableWithParent(userId)) {
-                        tieProfileLockIfNecessary(userId, LockscreenCredential.createNone());
-                    }
+                synchronized (mSpManager) {
+                    tieProfileLockIfNecessary(userId, LockscreenCredential.createNone());
                 }
             }
         });
@@ -1374,11 +1375,7 @@
                 mStorage.removeChildProfileLock(userId);
                 removeKeystoreProfileKey(userId);
             } else {
-                if (android.app.admin.flags.Flags.fixRaceConditionInTieProfileLock()) {
-                    synchronized (mSpManager) {
-                        tieProfileLockIfNecessary(userId, profileUserPassword);
-                    }
-                } else {
+                synchronized (mSpManager) {
                     tieProfileLockIfNecessary(userId, profileUserPassword);
                 }
             }
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 5e6737a4..6c0d8ad 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -2197,7 +2197,7 @@
                 mRouter.notifyRouterRegistered(
                         getVisibleRoutes(currentRoutes), currentSystemSessionInfo);
             } catch (RemoteException ex) {
-                Slog.w(TAG, "Failed to notify router registered. Router probably died.", ex);
+                logRemoteException("notifyRegistered", ex);
             }
         }
 
@@ -2212,7 +2212,7 @@
             try {
                 mRouter.notifyRoutesUpdated(getVisibleRoutes(routes));
             } catch (RemoteException ex) {
-                Slog.w(TAG, "Failed to notify routes updated. Router probably died.", ex);
+                logRemoteException("notifyRoutesUpdated", ex);
             }
         }
 
@@ -2221,11 +2221,7 @@
                 mRouter.notifySessionCreated(
                         requestId, maybeClearTransferInitiatorIdentity(sessionInfo));
             } catch (RemoteException ex) {
-                Slog.w(
-                        TAG,
-                        "Failed to notify router of the session creation."
-                                + " Router probably died.",
-                        ex);
+                logRemoteException("notifySessionCreated", ex);
             }
         }
 
@@ -2238,11 +2234,7 @@
             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);
+                logRemoteException("notifySessionCreationFailed", ex);
             }
         }
 
@@ -2253,10 +2245,7 @@
             try {
                 mRouter.notifySessionReleased(sessionInfo);
             } catch (RemoteException ex) {
-                Slog.w(
-                        TAG,
-                        "Failed to notify router of the session release. Router probably died.",
-                        ex);
+                logRemoteException("notifySessionReleased", ex);
             }
         }
 
@@ -2284,11 +2273,7 @@
                 }
                 mRouter.requestCreateSessionByManager(uniqueRequestId, oldSession, route);
             } catch (RemoteException ex) {
-                Slog.w(
-                        TAG,
-                        "getSessionHintsForCreatingSessionOnHandler: "
-                                + "Failed to request. Router probably died.",
-                        ex);
+                logRemoteException("requestCreateSessionByManager", ex);
                 managerRecord.notifyRequestFailed(
                         toOriginalRequestId(uniqueRequestId), REASON_UNKNOWN_ERROR);
             }
@@ -2303,7 +2288,7 @@
             try {
                 mRouter.notifySessionInfoChanged(maybeClearTransferInitiatorIdentity(sessionInfo));
             } catch (RemoteException ex) {
-                Slog.w(TAG, "Failed to notify session info changed. Router probably died.", ex);
+                logRemoteException("notifySessionInfoChanged", ex);
             }
         }
 
@@ -2362,6 +2347,22 @@
             }
             return false;
         }
+
+        /** Logs a {@link RemoteException} occurred during the execution of {@code operation}. */
+        private void logRemoteException(String operation, RemoteException exception) {
+            String message =
+                    TextUtils.formatSimple(
+                            "%s failed for %s due to %s",
+                            operation, getDebugString(), exception.toString());
+            Slog.w(TAG, message);
+        }
+
+        /** Returns a human readable representation of this router record for logging purposes. */
+        private String getDebugString() {
+            return TextUtils.formatSimple(
+                    "Router %s (id=%d,pid=%d,userId=%d,uid=%d)",
+                    mPackageName, mRouterId, mPid, mUserRecord.mUserId, mUid);
+        }
     }
 
     final class ManagerRecord implements IBinder.DeathRecipient {
diff --git a/services/core/java/com/android/server/media/TEST_MAPPING b/services/core/java/com/android/server/media/TEST_MAPPING
index 43e2afd..dbf9915 100644
--- a/services/core/java/com/android/server/media/TEST_MAPPING
+++ b/services/core/java/com/android/server/media/TEST_MAPPING
@@ -1,7 +1,10 @@
 {
   "presubmit": [
     {
-      "name": "CtsMediaBetterTogetherTestCases"
+      "name": "CtsMediaRouterTestCases"
+    },
+    {
+      "name": "CtsMediaSessionTestCases"
     },
     {
       "name": "MediaRouterServiceTests"
diff --git a/services/core/java/com/android/server/media/quality/MediaQualityService.java b/services/core/java/com/android/server/media/quality/MediaQualityService.java
index c810231..d440d3a 100644
--- a/services/core/java/com/android/server/media/quality/MediaQualityService.java
+++ b/services/core/java/com/android/server/media/quality/MediaQualityService.java
@@ -18,34 +18,48 @@
 
 import android.content.ContentValues;
 import android.content.Context;
+import android.content.pm.PackageManager;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
+import android.hardware.tv.mediaquality.IMediaQuality;
 import android.media.quality.AmbientBacklightSettings;
 import android.media.quality.IAmbientBacklightCallback;
 import android.media.quality.IMediaQualityManager;
 import android.media.quality.IPictureProfileCallback;
 import android.media.quality.ISoundProfileCallback;
 import android.media.quality.MediaQualityContract.BaseParameters;
-import android.media.quality.ParamCapability;
+import android.media.quality.MediaQualityManager;
+import android.media.quality.ParameterCapability;
 import android.media.quality.PictureProfile;
 import android.media.quality.PictureProfileHandle;
 import android.media.quality.SoundProfile;
 import android.media.quality.SoundProfileHandle;
 import android.os.Binder;
+import android.os.Bundle;
+import android.os.IBinder;
 import android.os.PersistableBundle;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.os.ServiceManager;
 import android.os.UserHandle;
 import android.util.Log;
+import android.util.Pair;
+import android.util.Slog;
+import android.util.SparseArray;
 
 import com.android.server.SystemService;
+import com.android.server.utils.Slogf;
 
 import org.json.JSONException;
 import org.json.JSONObject;
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
+import java.util.Map;
 import java.util.UUID;
 import java.util.stream.Collectors;
 
@@ -62,10 +76,14 @@
     private final MediaQualityDbHelper mMediaQualityDbHelper;
     private final BiMap<Long, String> mPictureProfileTempIdMap;
     private final BiMap<Long, String> mSoundProfileTempIdMap;
+    private final PackageManager mPackageManager;
+    private final SparseArray<UserState> mUserStates = new SparseArray<>();
+    private IMediaQuality mMediaQuality;
 
     public MediaQualityService(Context context) {
         super(context);
         mContext = context;
+        mPackageManager = mContext.getPackageManager();
         mPictureProfileTempIdMap = new BiMap<>();
         mSoundProfileTempIdMap = new BiMap<>();
         mMediaQualityDbHelper = new MediaQualityDbHelper(mContext);
@@ -75,6 +93,12 @@
 
     @Override
     public void onStart() {
+        IBinder binder = ServiceManager.getService(IMediaQuality.DESCRIPTOR + "/default");
+        if (binder != null) {
+            Slogf.d(TAG, "binder is not null");
+            mMediaQuality = IMediaQuality.Stub.asInterface(binder);
+        }
+
         publishBinderService(Context.MEDIA_QUALITY_SERVICE, new BinderService());
     }
 
@@ -83,12 +107,20 @@
 
         @Override
         public PictureProfile createPictureProfile(PictureProfile pp, UserHandle user) {
+            if ((pp.getPackageName() != null && !pp.getPackageName().isEmpty()
+                    && !incomingPackageEqualsCallingUidPackage(pp.getPackageName()))
+                    && !hasGlobalPictureQualityServicePermission()) {
+                notifyError(null, PictureProfile.ERROR_NO_PERMISSION,
+                        Binder.getCallingUid(), Binder.getCallingPid());
+            }
+
             SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
 
             ContentValues values = getContentValues(null,
                     pp.getProfileType(),
                     pp.getName(),
-                    pp.getPackageName(),
+                    pp.getPackageName() == null || pp.getPackageName().isEmpty()
+                            ? getPackageOfCallingUid() : pp.getPackageName(),
                     pp.getInputId(),
                     pp.getParameters());
 
@@ -102,9 +134,13 @@
 
         @Override
         public void updatePictureProfile(String id, PictureProfile pp, UserHandle user) {
-            Long intId = mPictureProfileTempIdMap.getKey(id);
+            Long dbId = mPictureProfileTempIdMap.getKey(id);
+            if (!hasPermissionToUpdatePictureProfile(dbId, pp)) {
+                notifyError(id, PictureProfile.ERROR_NO_PERMISSION,
+                        Binder.getCallingUid(), Binder.getCallingPid());
+            }
 
-            ContentValues values = getContentValues(intId,
+            ContentValues values = getContentValues(dbId,
                     pp.getProfileType(),
                     pp.getName(),
                     pp.getPackageName(),
@@ -116,25 +152,51 @@
                     null, values);
         }
 
-        @Override
-        public void removePictureProfile(String id, UserHandle user) {
-            Long intId = mPictureProfileTempIdMap.getKey(id);
-            if (intId != null) {
-                SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
-                String selection = BaseParameters.PARAMETER_ID + " = ?";
-                String[] selectionArgs = {Long.toString(intId)};
-                db.delete(mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME, selection,
-                        selectionArgs);
-                mPictureProfileTempIdMap.remove(intId);
-            }
+        private boolean hasPermissionToUpdatePictureProfile(Long dbId, PictureProfile toUpdate) {
+            PictureProfile fromDb = getPictureProfile(dbId);
+            return fromDb.getProfileType() == toUpdate.getProfileType()
+                    && fromDb.getPackageName().equals(toUpdate.getPackageName())
+                    && fromDb.getName().equals(toUpdate.getName())
+                    && fromDb.getName().equals(getPackageOfCallingUid());
         }
 
         @Override
-        public PictureProfile getPictureProfile(int type, String name, boolean includeParams,
+        public void removePictureProfile(String id, UserHandle user) {
+            Long dbId = mPictureProfileTempIdMap.getKey(id);
+
+            if (!hasPermissionToRemovePictureProfile(dbId)) {
+                notifyError(id, PictureProfile.ERROR_NO_PERMISSION,
+                        Binder.getCallingUid(), Binder.getCallingPid());
+            }
+
+            if (dbId != null) {
+                SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
+                String selection = BaseParameters.PARAMETER_ID + " = ?";
+                String[] selectionArgs = {Long.toString(dbId)};
+                int result = db.delete(mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME, selection,
+                        selectionArgs);
+                if (result == 0) {
+                    notifyError(id, PictureProfile.ERROR_INVALID_ARGUMENT,
+                            Binder.getCallingUid(), Binder.getCallingPid());
+                }
+                mPictureProfileTempIdMap.remove(dbId);
+            }
+        }
+
+        private boolean hasPermissionToRemovePictureProfile(Long dbId) {
+            PictureProfile fromDb = getPictureProfile(dbId);
+            return fromDb.getName().equalsIgnoreCase(getPackageOfCallingUid());
+        }
+
+        @Override
+        public PictureProfile getPictureProfile(int type, String name, Bundle options,
                 UserHandle user) {
+            boolean includeParams =
+                    options.getBoolean(MediaQualityManager.OPTION_INCLUDE_PARAMETERS, false);
             String selection = BaseParameters.PARAMETER_TYPE + " = ? AND "
-                    + BaseParameters.PARAMETER_NAME + " = ?";
-            String[] selectionArguments = {Integer.toString(type), name};
+                    + BaseParameters.PARAMETER_NAME + " = ? AND "
+                    + BaseParameters.PARAMETER_PACKAGE + " = ?";
+            String[] selectionArguments = {Integer.toString(type), name, getPackageOfCallingUid()};
 
             try (
                     Cursor cursor = getCursorAfterQuerying(
@@ -152,13 +214,44 @@
                     return null;
                 }
                 cursor.moveToFirst();
-                return getPictureProfileWithTempIdFromCursor(cursor);
+                return convertCursorToPictureProfileWithTempId(cursor);
+            }
+        }
+
+        private PictureProfile getPictureProfile(Long dbId) {
+            String selection = BaseParameters.PARAMETER_ID + " = ?";
+            String[] selectionArguments = {Long.toString(dbId)};
+
+            try (
+                    Cursor cursor = getCursorAfterQuerying(
+                            mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME,
+                            getMediaProfileColumns(false), selection, selectionArguments)
+            ) {
+                int count = cursor.getCount();
+                if (count == 0) {
+                    return null;
+                }
+                if (count > 1) {
+                    Log.wtf(TAG, String.format(Locale.US, "%d entries found for id=%d"
+                                    + " in %s. Should only ever be 0 or 1.", count, dbId,
+                            mMediaQualityDbHelper.PICTURE_QUALITY_TABLE_NAME));
+                    return null;
+                }
+                cursor.moveToFirst();
+                return convertCursorToPictureProfileWithTempId(cursor);
             }
         }
 
         @Override
         public List<PictureProfile> getPictureProfilesByPackage(
-                String packageName, boolean includeParams, UserHandle user) {
+                String packageName, Bundle options, UserHandle user) {
+            if (!hasGlobalPictureQualityServicePermission()) {
+                notifyError(null, PictureProfile.ERROR_NO_PERMISSION,
+                        Binder.getCallingUid(), Binder.getCallingPid());
+            }
+
+            boolean includeParams =
+                    options.getBoolean(MediaQualityManager.OPTION_INCLUDE_PARAMETERS, false);
             String selection = BaseParameters.PARAMETER_PACKAGE + " = ?";
             String[] selectionArguments = {packageName};
             return getPictureProfilesBasedOnConditions(getMediaProfileColumns(includeParams),
@@ -167,23 +260,30 @@
 
         @Override
         public List<PictureProfile> getAvailablePictureProfiles(
-                boolean includeParams, UserHandle user) {
-            String[] packageNames = mContext.getPackageManager().getPackagesForUid(
-                    Binder.getCallingUid());
-            if (packageNames != null && packageNames.length == 1 && !packageNames[0].isEmpty()) {
-                return getPictureProfilesByPackage(packageNames[0], includeParams, user);
+                        Bundle options, UserHandle user) {
+            String packageName = getPackageOfCallingUid();
+            if (packageName != null) {
+                return getPictureProfilesByPackage(packageName, options, user);
             }
             return new ArrayList<>();
         }
 
         @Override
         public boolean setDefaultPictureProfile(String profileId, UserHandle user) {
+            if (!hasGlobalPictureQualityServicePermission()) {
+                notifyError(profileId, PictureProfile.ERROR_NO_PERMISSION,
+                        Binder.getCallingUid(), Binder.getCallingPid());
+            }
             // TODO: pass the profile ID to MediaQuality HAL when ready.
             return false;
         }
 
         @Override
         public List<String> getPictureProfilePackageNames(UserHandle user) {
+            if (!hasGlobalPictureQualityServicePermission()) {
+                notifyError(null, PictureProfile.ERROR_NO_PERMISSION,
+                        Binder.getCallingUid(), Binder.getCallingPid());
+            }
             String [] column = {BaseParameters.PARAMETER_PACKAGE};
             List<PictureProfile> pictureProfiles = getPictureProfilesBasedOnConditions(column,
                     null, null);
@@ -205,12 +305,19 @@
 
         @Override
         public SoundProfile createSoundProfile(SoundProfile sp, UserHandle user) {
+            if ((sp.getPackageName() != null && !sp.getPackageName().isEmpty()
+                    && !incomingPackageEqualsCallingUidPackage(sp.getPackageName()))
+                    && !hasGlobalPictureQualityServicePermission()) {
+                //TODO: error handling
+                return null;
+            }
             SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
 
             ContentValues values = getContentValues(null,
                     sp.getProfileType(),
                     sp.getName(),
-                    sp.getPackageName(),
+                    sp.getPackageName() == null || sp.getPackageName().isEmpty()
+                            ? getPackageOfCallingUid() : sp.getPackageName(),
                     sp.getInputId(),
                     sp.getParameters());
 
@@ -224,9 +331,14 @@
 
         @Override
         public void updateSoundProfile(String id, SoundProfile sp, UserHandle user) {
-            Long intId = mSoundProfileTempIdMap.getKey(id);
+            Long dbId = mSoundProfileTempIdMap.getKey(id);
 
-            ContentValues values = getContentValues(intId,
+            if (!hasPermissionToUpdateSoundProfile(dbId, sp)) {
+                //TODO: error handling
+                return;
+            }
+
+            ContentValues values = getContentValues(dbId,
                     sp.getProfileType(),
                     sp.getName(),
                     sp.getPackageName(),
@@ -237,25 +349,49 @@
             db.replace(mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME, null, values);
         }
 
+        private boolean hasPermissionToUpdateSoundProfile(Long dbId, SoundProfile sp) {
+            SoundProfile fromDb = getSoundProfile(dbId);
+            return fromDb.getProfileType() == sp.getProfileType()
+                    && fromDb.getPackageName().equals(sp.getPackageName())
+                    && fromDb.getName().equals(sp.getName())
+                    && fromDb.getName().equals(getPackageOfCallingUid());
+        }
+
         @Override
         public void removeSoundProfile(String id, UserHandle user) {
             Long intId = mSoundProfileTempIdMap.getKey(id);
+            if (!hasPermissionToRemoveSoundProfile(intId)) {
+                //TODO: error handling
+                return;
+            }
+
             if (intId != null) {
                 SQLiteDatabase db = mMediaQualityDbHelper.getWritableDatabase();
                 String selection = BaseParameters.PARAMETER_ID + " = ?";
                 String[] selectionArgs = {Long.toString(intId)};
-                db.delete(mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME, selection,
+                int result = db.delete(mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME, selection,
                         selectionArgs);
+                if (result == 0) {
+                    //TODO: error handling
+                }
                 mSoundProfileTempIdMap.remove(intId);
             }
         }
 
+        private boolean hasPermissionToRemoveSoundProfile(Long dbId) {
+            SoundProfile fromDb = getSoundProfile(dbId);
+            return fromDb.getName().equalsIgnoreCase(getPackageOfCallingUid());
+        }
+
         @Override
-        public SoundProfile getSoundProfile(int type, String id, boolean includeParams,
+        public SoundProfile getSoundProfile(int type, String name, Bundle options,
                 UserHandle user) {
+            boolean includeParams =
+                    options.getBoolean(MediaQualityManager.OPTION_INCLUDE_PARAMETERS, false);
             String selection = BaseParameters.PARAMETER_TYPE + " = ? AND "
-                    + BaseParameters.PARAMETER_ID + " = ?";
-            String[] selectionArguments = {String.valueOf(type), id};
+                    + BaseParameters.PARAMETER_NAME + " = ? AND "
+                    + BaseParameters.PARAMETER_PACKAGE + " = ?";
+            String[] selectionArguments = {String.valueOf(type), name, getPackageOfCallingUid()};
 
             try (
                     Cursor cursor = getCursorAfterQuerying(
@@ -268,18 +404,49 @@
                 }
                 if (count > 1) {
                     Log.wtf(TAG, String.format(Locale.US, "%d entries found for id=%s"
-                                    + " in %s. Should only ever be 0 or 1.", count, id,
+                                    + " in %s. Should only ever be 0 or 1.", count, name,
                             mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME));
                     return null;
                 }
                 cursor.moveToFirst();
-                return getSoundProfileWithTempIdFromCursor(cursor);
+                return convertCursorToSoundProfileWithTempId(cursor);
+            }
+        }
+
+        private SoundProfile getSoundProfile(Long dbId) {
+            String selection = BaseParameters.PARAMETER_ID + " = ?";
+            String[] selectionArguments = {Long.toString(dbId)};
+
+            try (
+                    Cursor cursor = getCursorAfterQuerying(
+                            mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME,
+                            getMediaProfileColumns(false), selection, selectionArguments)
+            ) {
+                int count = cursor.getCount();
+                if (count == 0) {
+                    return null;
+                }
+                if (count > 1) {
+                    Log.wtf(TAG, String.format(Locale.US, "%d entries found for id=%s "
+                                    + "in %s. Should only ever be 0 or 1.", count, dbId,
+                            mMediaQualityDbHelper.SOUND_QUALITY_TABLE_NAME));
+                    return null;
+                }
+                cursor.moveToFirst();
+                return convertCursorToSoundProfileWithTempId(cursor);
             }
         }
 
         @Override
         public List<SoundProfile> getSoundProfilesByPackage(
-                String packageName, boolean includeParams, UserHandle user) {
+                String packageName, Bundle options, UserHandle user) {
+            if (!hasGlobalSoundQualityServicePermission()) {
+                //TODO: error handling
+                return new ArrayList<>();
+            }
+
+            boolean includeParams =
+                    options.getBoolean(MediaQualityManager.OPTION_INCLUDE_PARAMETERS, false);
             String selection = BaseParameters.PARAMETER_PACKAGE + " = ?";
             String[] selectionArguments = {packageName};
             return getSoundProfilesBasedOnConditions(getMediaProfileColumns(includeParams),
@@ -287,24 +454,30 @@
         }
 
         @Override
-        public List<SoundProfile> getAvailableSoundProfiles(
-                boolean includeParams, UserHandle user) {
-            String[] packageNames = mContext.getPackageManager().getPackagesForUid(
-                    Binder.getCallingUid());
-            if (packageNames != null && packageNames.length == 1 && !packageNames[0].isEmpty()) {
-                return getSoundProfilesByPackage(packageNames[0], includeParams, user);
+        public List<SoundProfile> getAvailableSoundProfiles(Bundle options, UserHandle user) {
+            String packageName = getPackageOfCallingUid();
+            if (packageName != null) {
+                return getSoundProfilesByPackage(packageName, options, user);
             }
             return new ArrayList<>();
         }
 
         @Override
         public boolean setDefaultSoundProfile(String profileId, UserHandle user) {
+            if (!hasGlobalSoundQualityServicePermission()) {
+                //TODO: error handling
+                return false;
+            }
             // TODO: pass the profile ID to MediaQuality HAL when ready.
             return false;
         }
 
         @Override
         public List<String> getSoundProfilePackageNames(UserHandle user) {
+            if (!hasGlobalSoundQualityServicePermission()) {
+                //TODO: error handling
+                return new ArrayList<>();
+            }
             String [] column = {BaseParameters.PARAMETER_NAME};
             List<SoundProfile> soundProfiles = getSoundProfilesBasedOnConditions(column,
                     null, null);
@@ -314,6 +487,37 @@
                     .collect(Collectors.toList());
         }
 
+        private String getPackageOfCallingUid() {
+            String[] packageNames = mPackageManager.getPackagesForUid(
+                    Binder.getCallingUid());
+            if (packageNames != null && packageNames.length == 1 && !packageNames[0].isEmpty()) {
+                return packageNames[0];
+            }
+            return null;
+        }
+
+        private boolean incomingPackageEqualsCallingUidPackage(String incomingPackage) {
+            return incomingPackage.equalsIgnoreCase(getPackageOfCallingUid());
+        }
+
+        private boolean hasGlobalPictureQualityServicePermission() {
+            return mPackageManager.checkPermission(android.Manifest.permission
+                            .MANAGE_GLOBAL_PICTURE_QUALITY_SERVICE,
+                    mContext.getPackageName()) == mPackageManager.PERMISSION_GRANTED;
+        }
+
+        private boolean hasGlobalSoundQualityServicePermission() {
+            return mPackageManager.checkPermission(android.Manifest.permission
+                            .MANAGE_GLOBAL_SOUND_QUALITY_SERVICE,
+                    mContext.getPackageName()) == mPackageManager.PERMISSION_GRANTED;
+        }
+
+        private boolean hasReadColorZonesPermission() {
+            return mPackageManager.checkPermission(android.Manifest.permission
+                            .READ_COLOR_ZONES,
+                    mContext.getPackageName()) == mPackageManager.PERMISSION_GRANTED;
+        }
+
         private void populateTempIdMap(BiMap<Long, String> map, Long id) {
             if (id != null && map.getValue(id) == null) {
                 String uuid;
@@ -421,7 +625,7 @@
             return columns.toArray(new String[0]);
         }
 
-        private PictureProfile getPictureProfileWithTempIdFromCursor(Cursor cursor) {
+        private PictureProfile convertCursorToPictureProfileWithTempId(Cursor cursor) {
             return new PictureProfile(
                     getTempId(mPictureProfileTempIdMap, cursor),
                     getType(cursor),
@@ -433,7 +637,7 @@
             );
         }
 
-        private SoundProfile getSoundProfileWithTempIdFromCursor(Cursor cursor) {
+        private SoundProfile convertCursorToSoundProfileWithTempId(Cursor cursor) {
             return new SoundProfile(
                     getTempId(mSoundProfileTempIdMap, cursor),
                     getType(cursor),
@@ -493,7 +697,7 @@
             ) {
                 List<PictureProfile> pictureProfiles = new ArrayList<>();
                 while (cursor.moveToNext()) {
-                    pictureProfiles.add(getPictureProfileWithTempIdFromCursor(cursor));
+                    pictureProfiles.add(convertCursorToPictureProfileWithTempId(cursor));
                 }
                 return pictureProfiles;
             }
@@ -508,53 +712,102 @@
             ) {
                 List<SoundProfile> soundProfiles = new ArrayList<>();
                 while (cursor.moveToNext()) {
-                    soundProfiles.add(getSoundProfileWithTempIdFromCursor(cursor));
+                    soundProfiles.add(convertCursorToSoundProfileWithTempId(cursor));
                 }
                 return soundProfiles;
             }
         }
 
+        private void notifyError(String profileId, int errorCode, int uid, int pid) {
+            UserState userState = getOrCreateUserStateLocked(UserHandle.USER_SYSTEM);
+            int n = userState.mCallbacks.beginBroadcast();
+
+            for (int i = 0; i < n; ++i) {
+                try {
+                    IPictureProfileCallback callback = userState.mCallbacks.getBroadcastItem(i);
+                    Pair<Integer, Integer> pidUid = userState.mCallbackPidUidMap.get(callback);
+
+                    if (pidUid.first == pid && pidUid.second == uid) {
+                        userState.mCallbacks.getBroadcastItem(i).onError(profileId, errorCode);
+                    }
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "failed to report added input to callback", e);
+                }
+            }
+            userState.mCallbacks.finishBroadcast();
+        }
+
         @Override
         public void registerPictureProfileCallback(final IPictureProfileCallback callback) {
+            int callingPid = Binder.getCallingPid();
+            int callingUid = Binder.getCallingUid();
+
+            UserState userState = getOrCreateUserStateLocked(Binder.getCallingUid());
+            userState.mCallbackPidUidMap.put(callback, Pair.create(callingPid, callingUid));
         }
+
         @Override
         public void registerSoundProfileCallback(final ISoundProfileCallback callback) {
         }
 
         @Override
         public void registerAmbientBacklightCallback(IAmbientBacklightCallback callback) {
+            if (!hasReadColorZonesPermission()) {
+                //TODO: error handling
+            }
         }
 
         @Override
         public void setAmbientBacklightSettings(
                 AmbientBacklightSettings settings, UserHandle user) {
+            if (!hasReadColorZonesPermission()) {
+                //TODO: error handling
+            }
         }
 
         @Override
         public void setAmbientBacklightEnabled(boolean enabled, UserHandle user) {
+            if (!hasReadColorZonesPermission()) {
+                //TODO: error handling
+            }
         }
 
         @Override
-        public List<ParamCapability> getParamCapabilities(List<String> names, UserHandle user) {
+        public List<ParameterCapability> getParameterCapabilities(
+                List<String> names, UserHandle user) {
             return new ArrayList<>();
         }
 
         @Override
         public List<String> getPictureProfileAllowList(UserHandle user) {
+            if (!hasGlobalPictureQualityServicePermission()) {
+                //TODO: error handling
+                return new ArrayList<>();
+            }
             return new ArrayList<>();
         }
 
         @Override
         public void setPictureProfileAllowList(List<String> packages, UserHandle user) {
+            if (!hasGlobalPictureQualityServicePermission()) {
+                //TODO: error handling
+            }
         }
 
         @Override
         public List<String> getSoundProfileAllowList(UserHandle user) {
+            if (!hasGlobalSoundQualityServicePermission()) {
+                //TODO: error handling
+                return new ArrayList<>();
+            }
             return new ArrayList<>();
         }
 
         @Override
         public void setSoundProfileAllowList(List<String> packages, UserHandle user) {
+            if (!hasGlobalSoundQualityServicePermission()) {
+                //TODO: error handling
+            }
         }
 
         @Override
@@ -564,28 +817,94 @@
 
         @Override
         public void setAutoPictureQualityEnabled(boolean enabled, UserHandle user) {
+            if (!hasGlobalPictureQualityServicePermission()) {
+                //TODO: error handling
+            }
+
+            try {
+                if (mMediaQuality != null) {
+                    mMediaQuality.setAutoPqEnabled(enabled);
+                }
+            } catch (UnsupportedOperationException e) {
+                Slog.e(TAG, "The current device is not supported");
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to set auto picture quality", e);
+            }
         }
 
         @Override
         public boolean isAutoPictureQualityEnabled(UserHandle user) {
+            try {
+                if (mMediaQuality != null) {
+                    return mMediaQuality.getAutoPqEnabled();
+                }
+            } catch (UnsupportedOperationException e) {
+                Slog.e(TAG, "The current device is not supported");
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to get auto picture quality", e);
+            }
             return false;
         }
 
         @Override
         public void setSuperResolutionEnabled(boolean enabled, UserHandle user) {
+            if (!hasGlobalPictureQualityServicePermission()) {
+                //TODO: error handling
+            }
+
+            try {
+                if (mMediaQuality != null) {
+                    mMediaQuality.setAutoSrEnabled(enabled);
+                }
+            } catch (UnsupportedOperationException e) {
+                Slog.e(TAG, "The current device is not supported");
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to set auto super resolution", e);
+            }
         }
 
         @Override
         public boolean isSuperResolutionEnabled(UserHandle user) {
+            try {
+                if (mMediaQuality != null) {
+                    return mMediaQuality.getAutoSrEnabled();
+                }
+            } catch (UnsupportedOperationException e) {
+                Slog.e(TAG, "The current device is not supported");
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to get auto super resolution", e);
+            }
             return false;
         }
 
         @Override
         public void setAutoSoundQualityEnabled(boolean enabled, UserHandle user) {
+            if (!hasGlobalSoundQualityServicePermission()) {
+                //TODO: error handling
+            }
+
+            try {
+                if (mMediaQuality != null) {
+                    mMediaQuality.setAutoAqEnabled(enabled);
+                }
+            } catch (UnsupportedOperationException e) {
+                Slog.e(TAG, "The current device is not supported");
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to set auto audio quality", e);
+            }
         }
 
         @Override
         public boolean isAutoSoundQualityEnabled(UserHandle user) {
+            try {
+                if (mMediaQuality != null) {
+                    return mMediaQuality.getAutoAqEnabled();
+                }
+            } catch (UnsupportedOperationException e) {
+                Slog.e(TAG, "The current device is not supported");
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to get auto audio quality", e);
+            }
             return false;
         }
 
@@ -594,4 +913,38 @@
             return false;
         }
     }
+
+    private class MediaQualityManagerCallbackList extends
+            RemoteCallbackList<IPictureProfileCallback> {
+        @Override
+        public void onCallbackDied(IPictureProfileCallback callback) {
+            //todo
+        }
+    }
+
+    private final class UserState {
+        // A list of callbacks.
+        private final MediaQualityManagerCallbackList mCallbacks =
+                new MediaQualityManagerCallbackList();
+
+        private final Map<IPictureProfileCallback, Pair<Integer, Integer>> mCallbackPidUidMap =
+                new HashMap<>();
+
+        private UserState(Context context, int userId) {
+
+        }
+    }
+
+    private UserState getOrCreateUserStateLocked(int userId) {
+        UserState userState = getUserStateLocked(userId);
+        if (userState == null) {
+            userState = new UserState(mContext, userId);
+            mUserStates.put(userId, userState);
+        }
+        return userState;
+    }
+
+    private UserState getUserStateLocked(int userId) {
+        return mUserStates.get(userId);
+    }
 }
diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java
index 0b40d64..3f2c222 100644
--- a/services/core/java/com/android/server/notification/ConditionProviders.java
+++ b/services/core/java/com/android/server/notification/ConditionProviders.java
@@ -325,7 +325,7 @@
         for (int i = 0; i < N; i++) {
             final Condition c = conditions[i];
             if (mCallback != null) {
-                mCallback.onConditionChanged(c.id, c);
+                mCallback.onConditionChanged(c.id, c, info.uid);
             }
         }
     }
@@ -515,7 +515,7 @@
 
     public interface Callback {
         void onServiceAdded(ComponentName component);
-        void onConditionChanged(Uri id, Condition condition);
+        void onConditionChanged(Uri id, Condition condition, int callerUid);
     }
 
 }
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 7375a68..dd9741c 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -3082,16 +3082,42 @@
 
     private void sendRegisteredOnlyBroadcast(Intent baseIntent) {
         int[] userIds = mUmInternal.getProfileIds(mAmi.getCurrentUserId(), true);
-        Intent intent = new Intent(baseIntent).addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
-        for (int userId : userIds) {
-            getContext().sendBroadcastAsUser(intent, UserHandle.of(userId), null);
-        }
-        // explicitly send the broadcast to all DND packages, even if they aren't currently running
-        for (int userId : userIds) {
-            for (String pkg : mConditionProviders.getAllowedPackages(userId)) {
-                Intent pkgIntent = new Intent(baseIntent).setPackage(pkg).setFlags(
-                        Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-                getContext().sendBroadcastAsUser(pkgIntent, UserHandle.of(userId));
+        if (Flags.nmBinderPerfReduceZenBroadcasts()) {
+            for (int userId : userIds) {
+                Context userContext = getContext().createContextAsUser(UserHandle.of(userId), 0);
+                String[] dndPackages = mConditionProviders.getAllowedPackages(userId)
+                        .toArray(new String[0]);
+
+                // We send the broadcast to all DND packages in the second step, so leave them out
+                // of this first broadcast for *running* receivers. That ensures each package only
+                // receives it once.
+                Intent registeredOnlyIntent = new Intent(baseIntent)
+                        .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+                userContext.sendBroadcastMultiplePermissions(registeredOnlyIntent,
+                        /* receiverPermissions= */ new String[0],
+                        /* excludedPermissions= */ new String[0],
+                        /* excludedPackages= */ dndPackages);
+
+                for (String pkg : dndPackages) {
+                    Intent pkgIntent = new Intent(baseIntent).setPackage(pkg)
+                            .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+                    userContext.sendBroadcast(pkgIntent);
+                }
+            }
+        } else {
+            Intent intent = new Intent(baseIntent).addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+            for (int userId : userIds) {
+                getContext().sendBroadcastAsUser(intent, UserHandle.of(userId), null);
+            }
+
+            // explicitly send the broadcast to all DND packages, even if they aren't currently
+            // running
+            for (int userId : userIds) {
+                for (String pkg : mConditionProviders.getAllowedPackages(userId)) {
+                    Intent pkgIntent = new Intent(baseIntent).setPackage(pkg).setFlags(
+                            Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+                    getContext().sendBroadcastAsUser(pkgIntent, UserHandle.of(userId));
+                }
             }
         }
     }
@@ -3136,6 +3162,7 @@
             mAssistants.onBootPhaseAppsCanStart();
             mConditionProviders.onBootPhaseAppsCanStart();
             mHistoryManager.onBootPhaseAppsCanStart();
+            mPreferencesHelper.onBootPhaseAppsCanStart();
             migrateDefaultNAS();
             maybeShowInitialReviewPermissionsNotification();
 
@@ -4276,16 +4303,16 @@
 
         @Override
         @FlaggedApi(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI)
-        public @NonNull String[] getTypeAdjustmentDeniedPackages() {
+        public @NonNull int[] getAllowedAdjustmentKeyTypesForPackage(String pkg) {
             checkCallerIsSystemOrSystemUiOrShell();
-            return mAssistants.getTypeAdjustmentDeniedPackages();
+            return mAssistants.getAllowedAdjustmentKeyTypesForPackage(pkg);
         }
 
-        @Override
         @FlaggedApi(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI)
-        public void setTypeAdjustmentForPackageState(String pkg, boolean enabled) {
+        public void setAssistantAdjustmentKeyTypeStateForPackage(String pkg, int type,
+                                                                 boolean enabled) {
             checkCallerIsSystemOrSystemUiOrShell();
-            mAssistants.setTypeAdjustmentForPackageState(pkg, enabled);
+            mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(pkg, type, enabled);
 
             handleSavePolicyFile();
         }
@@ -5877,8 +5904,9 @@
         // TODO: b/310620812 - Remove getZenRules() when MODES_API is inlined.
         @Override
         public List<ZenModeConfig.ZenRule> getZenRules() throws RemoteException {
-            enforcePolicyAccess(Binder.getCallingUid(), "getZenRules");
-            return mZenModeHelper.getZenRules(getCallingZenUser());
+            int callingUid = Binder.getCallingUid();
+            enforcePolicyAccess(callingUid, "getZenRules");
+            return mZenModeHelper.getZenRules(getCallingZenUser(), callingUid);
         }
 
         @Override
@@ -5886,15 +5914,17 @@
             if (!android.app.Flags.modesApi()) {
                 throw new IllegalStateException("getAutomaticZenRules called with flag off!");
             }
-            enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRules");
-            return mZenModeHelper.getAutomaticZenRules(getCallingZenUser());
+            int callingUid = Binder.getCallingUid();
+            enforcePolicyAccess(callingUid, "getAutomaticZenRules");
+            return mZenModeHelper.getAutomaticZenRules(getCallingZenUser(), callingUid);
         }
 
         @Override
         public AutomaticZenRule getAutomaticZenRule(String id) throws RemoteException {
             Objects.requireNonNull(id, "Id is null");
-            enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRule");
-            return mZenModeHelper.getAutomaticZenRule(getCallingZenUser(), id);
+            int callingUid = Binder.getCallingUid();
+            enforcePolicyAccess(callingUid, "getAutomaticZenRule");
+            return mZenModeHelper.getAutomaticZenRule(getCallingZenUser(), id, callingUid);
         }
 
         @Override
@@ -6039,8 +6069,9 @@
         @Condition.State
         public int getAutomaticZenRuleState(@NonNull String id) {
             Objects.requireNonNull(id, "id is null");
-            enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRuleState");
-            return mZenModeHelper.getAutomaticZenRuleState(getCallingZenUser(), id);
+            int callingUid = Binder.getCallingUid();
+            enforcePolicyAccess(callingUid, "getAutomaticZenRuleState");
+            return mZenModeHelper.getAutomaticZenRuleState(getCallingZenUser(), id, callingUid);
         }
 
         @Override
@@ -7083,7 +7114,7 @@
                         toRemove.add(potentialKey);
                     } else if (notificationClassificationUi()
                             && !mAssistants.isTypeAdjustmentAllowedForPackage(
-                            r.getSbn().getPackageName())) {
+                            r.getSbn().getPackageName(), adjustments.getInt(KEY_TYPE))) {
                         toRemove.add(potentialKey);
                     }
                 }
@@ -11740,7 +11771,11 @@
         private static final String ATT_DENIED = "denied_adjustments";
         private static final String ATT_ENABLED_TYPES = "enabled_key_types";
         private static final String ATT_NAS_UNSUPPORTED = "unsupported_adjustments";
-        private static final String ATT_TYPES_DENIED_APPS = "types_denied_apps";
+        // Encapsulates a list of packages and the bundle types enabled for each package.
+        private static final String TAG_TYPES_ENABLED_FOR_APPS = "types_enabled_for_apps";
+        // Encapsulates the bundle types enabled for a package.
+        private static final String ATT_APP_ENABLED_TYPES = "app_enabled_types";
+        private static final String ATT_PACKAGE = "package";
 
         private final Object mLock = new Object();
 
@@ -11756,8 +11791,14 @@
         @GuardedBy("mLock")
         private Map<Integer, HashSet<String>> mNasUnsupported = new ArrayMap<>();
 
+        // Types of classifications (aka bundles) enabled/allowed for this package.
+        // If the set is NULL (or package is not in the list), default classification allow list
+        // (the global one) should be used.
+        // If the set is empty, that indicates the package explicitly has all classifications
+        // disallowed.
         @GuardedBy("mLock")
-        private Set<String> mClassificationTypeDeniedPackages = new ArraySet<>();
+        private Map<String, Set<Integer>> mClassificationTypePackagesEnabledTypes =
+                new ArrayMap<>();
 
         protected ComponentName mDefaultFromConfig = null;
 
@@ -11958,41 +11999,88 @@
             }
         }
 
+        /**
+         * Returns whether the type adjustment is allowed for this particular package.
+         * If no package-specific restrictions have been set, defaults to the same value as
+         * isAdjustmentKeyTypeAllowed(type).
+         */
         @FlaggedApi(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI)
-        protected @NonNull boolean isTypeAdjustmentAllowedForPackage(String pkg) {
+        protected boolean isTypeAdjustmentAllowedForPackage(String pkg,
+                                                                     @Adjustment.Types int type) {
             synchronized (mLock) {
                 if (notificationClassificationUi()) {
-                    return !mClassificationTypeDeniedPackages.contains(pkg);
+                    if (mClassificationTypePackagesEnabledTypes.containsKey(pkg)) {
+                        Set<Integer> enabled = mClassificationTypePackagesEnabledTypes.get(pkg);
+                        if (enabled != null) {
+                            return enabled.contains(type);
+                        }
+                    }
+                    // If mClassificationTypePackagesEnabledTypes does not contain the pkg, or
+                    // the stored set is null, return the default.
+                    return isAdjustmentKeyTypeAllowed(type);
                 }
             }
-            return true;
+            return false;
         }
 
         @FlaggedApi(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI)
-        protected @NonNull String[] getTypeAdjustmentDeniedPackages() {
+        protected @NonNull int[] getAllowedAdjustmentKeyTypesForPackage(String pkg) {
             synchronized (mLock) {
                 if (notificationClassificationUi()) {
-                    return mClassificationTypeDeniedPackages.toArray(new String[0]);
+                    if (mClassificationTypePackagesEnabledTypes.containsKey(pkg)) {
+                        Set<Integer> enabled = mClassificationTypePackagesEnabledTypes.get(pkg);
+                        if (enabled != null) {
+                            // Convert Set to int[] for return.
+                            int[] returnEnabled = new int[enabled.size()];
+                            int i = 0;
+                            for (int val: enabled) {
+                                returnEnabled[i] = val;
+                                i++;
+                            }
+                            return returnEnabled;
+                        }
+                    }
+                    // If package is not in the map, or the value is null, return the default.
+                    return getAllowedAdjustmentKeyTypes();
                 }
             }
-            return new String[]{};
+            return new int[]{};
         }
 
         /**
          * Set whether a particular package can have its notification channels adjusted to have a
          * different type by NotificationAssistants.
+         * Note: once this method is called to enable or disable a specific type for a package,
+         * the global default is set as the starting point, and the type is enabled/disabled from
+         * there. Future changes to the global default will not apply automatically to this package.
          */
         @FlaggedApi(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI)
-        public void setTypeAdjustmentForPackageState(String pkg, boolean enabled) {
+        public void setAssistantAdjustmentKeyTypeStateForPackage(String pkg,
+                                                       @Adjustment.Types int type,
+                                                       boolean enabled) {
             if (!notificationClassificationUi()) {
                 return;
             }
             synchronized (mLock) {
-                if (enabled) {
-                    mClassificationTypeDeniedPackages.remove(pkg);
-                } else {
-                    mClassificationTypeDeniedPackages.add(pkg);
+                Set<Integer> enabledTypes = null;
+                if (mClassificationTypePackagesEnabledTypes.containsKey(pkg)) {
+                    enabledTypes = mClassificationTypePackagesEnabledTypes.get(pkg);
                 }
+                if (enabledTypes == null) {
+                    // Use global default to start.
+                    enabledTypes = new ArraySet<Integer>();
+                    // Convert from int[] to Set<Integer>
+                    for (int value : getAllowedAdjustmentKeyTypes()) {
+                        enabledTypes.add(value);
+                    }
+                }
+
+                if (enabled) {
+                    enabledTypes.add(type);
+                } else {
+                    enabledTypes.remove(type);
+                }
+                mClassificationTypePackagesEnabledTypes.put(pkg, enabledTypes);
             }
         }
 
@@ -12459,16 +12547,25 @@
                         TextUtils.join(",", mAllowedAdjustmentKeyTypes));
                 out.endTag(null, ATT_ENABLED_TYPES);
                 if (notificationClassificationUi()) {
-                    out.startTag(null, ATT_TYPES_DENIED_APPS);
-                    out.attribute(null, ATT_TYPES,
-                            TextUtils.join(",", mClassificationTypeDeniedPackages));
-                    out.endTag(null, ATT_TYPES_DENIED_APPS);
+                    out.startTag(null, TAG_TYPES_ENABLED_FOR_APPS);
+                    for (String pkg: mClassificationTypePackagesEnabledTypes.keySet()) {
+                        Set<Integer> allowedTypes =
+                                mClassificationTypePackagesEnabledTypes.get(pkg);
+                        if (allowedTypes != null) {
+                            out.startTag(null, ATT_APP_ENABLED_TYPES);
+                            out.attribute(null, ATT_PACKAGE, pkg);
+                            out.attribute(null, ATT_TYPES, TextUtils.join(",", allowedTypes));
+                            out.endTag(null, ATT_APP_ENABLED_TYPES);
+                        }
+                    }
+                    out.endTag(null, TAG_TYPES_ENABLED_FOR_APPS);
                 }
             }
         }
 
         @Override
-        protected void readExtraTag(String tag, TypedXmlPullParser parser) throws IOException {
+        protected void readExtraTag(String tag, TypedXmlPullParser parser) throws IOException,
+                XmlPullParserException {
             if (!notificationClassification()) {
                 return;
             }
@@ -12495,12 +12592,25 @@
                         }
                     }
                 }
-            } else if (notificationClassificationUi() && ATT_TYPES_DENIED_APPS.equals(tag)) {
-                final String apps = XmlUtils.readStringAttribute(parser, ATT_TYPES);
+            } else if (TAG_TYPES_ENABLED_FOR_APPS.equals(tag)) {
+                final int appsOuterDepth = parser.getDepth();
                 synchronized (mLock) {
-                    mClassificationTypeDeniedPackages.clear();
-                    if (!TextUtils.isEmpty(apps)) {
-                        mClassificationTypeDeniedPackages.addAll(Arrays.asList(apps.split(",")));
+                    mClassificationTypePackagesEnabledTypes.clear();
+                    while (XmlUtils.nextElementWithin(parser, appsOuterDepth)) {
+                        if (!ATT_APP_ENABLED_TYPES.equals(parser.getName())) {
+                            continue;
+                        }
+                        final String app = XmlUtils.readStringAttribute(parser, ATT_PACKAGE);
+                        Set<Integer> allowedTypes = new ArraySet<>();
+                        final String typesString = XmlUtils.readStringAttribute(parser, ATT_TYPES);
+                        if (!TextUtils.isEmpty(typesString)) {
+                            allowedTypes = Arrays.stream(typesString.split(","))
+                                    .map(Integer::valueOf)
+                                    .collect(Collectors.toSet());
+                        }
+                        // Empty type list is allowed, because empty type list signifies the user
+                        // has manually cleared the package of allowed types.
+                        mClassificationTypePackagesEnabledTypes.put(app, allowedTypes);
                     }
                 }
             }
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 93f512b..0bb3c6a 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -36,10 +36,7 @@
 import android.app.Notification;
 import android.app.NotificationChannel;
 import android.app.Person;
-import android.content.ContentProvider;
-import android.content.ContentResolver;
 import android.content.Context;
-import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.ShortcutInfo;
@@ -48,7 +45,6 @@
 import android.media.AudioSystem;
 import android.metrics.LogMaker;
 import android.net.Uri;
-import android.os.Binder;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.IBinder;
@@ -1493,23 +1489,14 @@
 
             final Notification notification = getNotification();
             notification.visitUris((uri) -> {
-                if (com.android.server.notification.Flags.notificationVerifyChannelSoundUri()) {
-                    visitGrantableUri(uri, false, false);
-                } else {
-                    oldVisitGrantableUri(uri, false, false);
-                }
+                visitGrantableUri(uri, false, false);
             });
 
             if (notification.getChannelId() != null) {
                 NotificationChannel channel = getChannel();
                 if (channel != null) {
-                    if (com.android.server.notification.Flags.notificationVerifyChannelSoundUri()) {
-                        visitGrantableUri(channel.getSound(), (channel.getUserLockedFields()
-                                & NotificationChannel.USER_LOCKED_SOUND) != 0, true);
-                    } else {
-                        oldVisitGrantableUri(channel.getSound(), (channel.getUserLockedFields()
-                                & NotificationChannel.USER_LOCKED_SOUND) != 0, true);
-                    }
+                    visitGrantableUri(channel.getSound(), (channel.getUserLockedFields()
+                            & NotificationChannel.USER_LOCKED_SOUND) != 0, true);
                 }
             }
         } finally {
@@ -1525,53 +1512,6 @@
      * {@link #mGrantableUris}. Otherwise, this will either log or throw
      * {@link SecurityException} depending on target SDK of enqueuing app.
      */
-    private void oldVisitGrantableUri(Uri uri, boolean userOverriddenUri, boolean isSound) {
-        if (uri == null || !ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) return;
-
-        if (mGrantableUris != null && mGrantableUris.contains(uri)) {
-            return; // already verified this URI
-        }
-
-        final int sourceUid = getSbn().getUid();
-        final long ident = Binder.clearCallingIdentity();
-        try {
-            // This will throw a SecurityException if the caller can't grant.
-            mUgmInternal.checkGrantUriPermission(sourceUid, null,
-                    ContentProvider.getUriWithoutUserId(uri),
-                    Intent.FLAG_GRANT_READ_URI_PERMISSION,
-                    ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(sourceUid)));
-
-            if (mGrantableUris == null) {
-                mGrantableUris = new ArraySet<>();
-            }
-            mGrantableUris.add(uri);
-        } catch (SecurityException e) {
-            if (!userOverriddenUri) {
-                if (isSound) {
-                    mSound = Settings.System.DEFAULT_NOTIFICATION_URI;
-                    Log.w(TAG, "Replacing " + uri + " from " + sourceUid + ": " + e.getMessage());
-                } else {
-                    if (mTargetSdkVersion >= Build.VERSION_CODES.P) {
-                        throw e;
-                    } else {
-                        Log.w(TAG,
-                                "Ignoring " + uri + " from " + sourceUid + ": " + e.getMessage());
-                    }
-                }
-            }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
-    /**
-     * Note the presence of a {@link Uri} that should have permission granted to
-     * whoever will be rendering it.
-     * <p>
-     * If the enqueuing app has the ability to grant access, it will be added to
-     * {@link #mGrantableUris}. Otherwise, this will either log or throw
-     * {@link SecurityException} depending on target SDK of enqueuing app.
-     */
     private void visitGrantableUri(Uri uri, boolean userOverriddenUri,
             boolean isSound) {
         if (mGrantableUris != null && mGrantableUris.contains(uri)) {
diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java
index 15377d6..36eabae 100644
--- a/services/core/java/com/android/server/notification/PreferencesHelper.java
+++ b/services/core/java/com/android/server/notification/PreferencesHelper.java
@@ -82,7 +82,6 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.IntArray;
-import android.util.Log;
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseBooleanArray;
@@ -272,6 +271,15 @@
         updateMediaNotificationFilteringEnabled();
     }
 
+    void onBootPhaseAppsCanStart() {
+        // IpcDataCaches must be invalidated once data becomes available, as queries will only
+        // begin to be cached after the first invalidation signal. At this point, we know about all
+        // notification channels.
+        if (android.app.Flags.nmBinderPerfCacheChannels()) {
+            invalidateNotificationChannelCache();
+        }
+    }
+
     public void readXml(TypedXmlPullParser parser, boolean forRestore, int userId)
             throws XmlPullParserException, IOException {
         int type = parser.getEventType();
@@ -531,12 +539,14 @@
     private PackagePreferences getOrCreatePackagePreferencesLocked(String pkg,
             @UserIdInt int userId, int uid, int importance, int priority, int visibility,
             boolean showBadge, int bubblePreference, long creationTime) {
+        boolean created = false;
         final String key = packagePreferencesKey(pkg, uid);
         PackagePreferences
                 r = (uid == UNKNOWN_UID)
                 ? mRestoredWithoutUids.get(unrestoredPackageKey(pkg, userId))
                 : mPackagePreferences.get(key);
         if (r == null) {
+            created = true;
             r = new PackagePreferences();
             r.pkg = pkg;
             r.uid = uid;
@@ -572,6 +582,9 @@
                 mRestoredWithoutUids.remove(unrestoredPackageKey(pkg, userId));
             }
         }
+        if (android.app.Flags.nmBinderPerfCacheChannels() && created) {
+            invalidateNotificationChannelCache();
+        }
         return r;
     }
 
@@ -664,6 +677,9 @@
         }
         NotificationChannel channel = new NotificationChannel(channelId, label, IMPORTANCE_LOW);
         p.channels.put(channelId, channel);
+        if (android.app.Flags.nmBinderPerfCacheChannels()) {
+            invalidateNotificationChannelCache();
+        }
         return channel;
     }
 
@@ -1171,9 +1187,7 @@
                 // Verify that the app has permission to read the sound Uri
                 // Only check for new channels, as regular apps can only set sound
                 // before creating. See: {@link NotificationChannel#setSound}
-                if (Flags.notificationVerifyChannelSoundUri()) {
-                    PermissionHelper.grantUriPermission(mUgmInternal, channel.getSound(), uid);
-                }
+                PermissionHelper.grantUriPermission(mUgmInternal, channel.getSound(), uid);
 
                 channel.setImportanceLockedByCriticalDeviceFunction(
                         r.defaultAppLockedImportance || r.fixedImportance);
@@ -1208,6 +1222,10 @@
             updateCurrentUserHasChannelsBypassingDnd(callingUid, fromSystemOrSystemUi);
         }
 
+        if (android.app.Flags.nmBinderPerfCacheChannels() && needsPolicyFileChange) {
+            invalidateNotificationChannelCache();
+        }
+
         return needsPolicyFileChange;
     }
 
@@ -1229,6 +1247,9 @@
             }
             channel.unlockFields(USER_LOCKED_IMPORTANCE);
         }
+        if (android.app.Flags.nmBinderPerfCacheChannels()) {
+            invalidateNotificationChannelCache();
+        }
     }
 
 
@@ -1301,6 +1322,9 @@
             updateCurrentUserHasChannelsBypassingDnd(callingUid, fromSystemOrSystemUi);
         }
         if (changed) {
+            if (android.app.Flags.nmBinderPerfCacheChannels()) {
+                invalidateNotificationChannelCache();
+            }
             updateConfig();
         }
     }
@@ -1537,6 +1561,10 @@
         if (channelBypassedDnd) {
             updateCurrentUserHasChannelsBypassingDnd(callingUid, fromSystemOrSystemUi);
         }
+
+        if (android.app.Flags.nmBinderPerfCacheChannels() && deletedChannel) {
+            invalidateNotificationChannelCache();
+        }
         return deletedChannel;
     }
 
@@ -1566,6 +1594,9 @@
             }
             r.channels.remove(channelId);
         }
+        if (android.app.Flags.nmBinderPerfCacheChannels()) {
+            invalidateNotificationChannelCache();
+        }
     }
 
     @Override
@@ -1576,13 +1607,18 @@
             if (r == null) {
                 return;
             }
+            boolean deleted = false;
             int N = r.channels.size() - 1;
             for (int i = N; i >= 0; i--) {
                 String key = r.channels.keyAt(i);
                 if (!DEFAULT_CHANNEL_ID.equals(key)) {
                     r.channels.remove(key);
+                    deleted = true;
                 }
             }
+            if (android.app.Flags.nmBinderPerfCacheChannels() && deleted) {
+                invalidateNotificationChannelCache();
+            }
         }
     }
 
@@ -1613,6 +1649,9 @@
                 }
             }
         }
+        if (android.app.Flags.nmBinderPerfCacheChannels()) {
+            invalidateNotificationChannelCache();
+        }
     }
 
     public void updateDefaultApps(int userId, ArraySet<String> toRemove,
@@ -1642,6 +1681,9 @@
                 }
             }
         }
+        if (android.app.Flags.nmBinderPerfCacheChannels()) {
+            invalidateNotificationChannelCache();
+        }
     }
 
     public NotificationChannelGroup getNotificationChannelGroupWithChannels(String pkg,
@@ -1757,6 +1799,9 @@
         if (groupBypassedDnd) {
             updateCurrentUserHasChannelsBypassingDnd(callingUid, fromSystemOrSystemUi);
         }
+        if (android.app.Flags.nmBinderPerfCacheChannels() && deletedChannels.size() > 0) {
+            invalidateNotificationChannelCache();
+        }
         return deletedChannels;
     }
 
@@ -1902,8 +1947,13 @@
                 }
             }
         }
-        if (!deletedChannelIds.isEmpty() && mCurrentUserHasChannelsBypassingDnd) {
-            updateCurrentUserHasChannelsBypassingDnd(callingUid, fromSystemOrSystemUi);
+        if (!deletedChannelIds.isEmpty()) {
+            if (mCurrentUserHasChannelsBypassingDnd) {
+                updateCurrentUserHasChannelsBypassingDnd(callingUid, fromSystemOrSystemUi);
+            }
+            if (android.app.Flags.nmBinderPerfCacheChannels()) {
+                invalidateNotificationChannelCache();
+            }
         }
         return deletedChannelIds;
     }
@@ -2196,6 +2246,11 @@
             PackagePreferences prefs = getOrCreatePackagePreferencesLocked(sourcePkg, sourceUid);
             prefs.delegate = new Delegate(delegatePkg, delegateUid, true);
         }
+        if (android.app.Flags.nmBinderPerfCacheChannels()) {
+            // If package delegates change, then which packages can get what channel information
+            // also changes, so we need to clear the cache.
+            invalidateNotificationChannelCache();
+        }
     }
 
     /**
@@ -2208,6 +2263,9 @@
                 prefs.delegate.mEnabled = false;
             }
         }
+        if (android.app.Flags.nmBinderPerfCacheChannels()) {
+            invalidateNotificationChannelCache();
+        }
     }
 
     /**
@@ -2811,18 +2869,24 @@
 
     public void onUserRemoved(int userId) {
         synchronized (mLock) {
+            boolean removed = false;
             int N = mPackagePreferences.size();
             for (int i = N - 1; i >= 0; i--) {
                 PackagePreferences PackagePreferences = mPackagePreferences.valueAt(i);
                 if (UserHandle.getUserId(PackagePreferences.uid) == userId) {
                     mPackagePreferences.removeAt(i);
+                    removed = true;
                 }
             }
+            if (android.app.Flags.nmBinderPerfCacheChannels() && removed) {
+                invalidateNotificationChannelCache();
+            }
         }
     }
 
     protected void onLocaleChanged(Context context, int userId) {
         synchronized (mLock) {
+            boolean updated = false;
             int N = mPackagePreferences.size();
             for (int i = 0; i < N; i++) {
                 PackagePreferences PackagePreferences = mPackagePreferences.valueAt(i);
@@ -2833,10 +2897,14 @@
                                 DEFAULT_CHANNEL_ID).setName(
                                 context.getResources().getString(
                                         R.string.default_notification_channel_label));
+                        updated = true;
                     }
                     // TODO (b/346396459): Localize all reserved channels
                 }
             }
+            if (android.app.Flags.nmBinderPerfCacheChannels() && updated) {
+                invalidateNotificationChannelCache();
+            }
         }
     }
 
@@ -2884,7 +2952,7 @@
                                                     channel.getAudioAttributes().getUsage());
                                     if (Settings.System.DEFAULT_NOTIFICATION_URI.equals(
                                             restoredUri)) {
-                                        Log.w(TAG,
+                                        Slog.w(TAG,
                                                 "Could not restore sound: " + uri + " for channel: "
                                                         + channel);
                                     }
@@ -2922,6 +2990,9 @@
 
         if (updated) {
             updateConfig();
+            if (android.app.Flags.nmBinderPerfCacheChannels()) {
+                invalidateNotificationChannelCache();
+            }
         }
         return updated;
     }
@@ -2939,6 +3010,9 @@
                 p.priority = DEFAULT_PRIORITY;
                 p.visibility = DEFAULT_VISIBILITY;
                 p.showBadge = DEFAULT_SHOW_BADGE;
+                if (android.app.Flags.nmBinderPerfCacheChannels()) {
+                    invalidateNotificationChannelCache();
+                }
             }
         }
     }
@@ -3123,6 +3197,9 @@
                 }
             }
         }
+        if (android.app.Flags.nmBinderPerfCacheChannels()) {
+            invalidateNotificationChannelCache();
+        }
     }
 
     public void migrateNotificationPermissions(List<UserInfo> users) {
@@ -3154,6 +3231,12 @@
         mRankingHandler.requestSort();
     }
 
+    @VisibleForTesting
+    // Utility method for overriding in tests to confirm that the cache gets cleared.
+    protected void invalidateNotificationChannelCache() {
+        NotificationManager.invalidateNotificationChannelCache();
+    }
+
     private static String packagePreferencesKey(String pkg, int uid) {
         return pkg + "|" + uid;
     }
diff --git a/services/core/java/com/android/server/notification/ZenModeConditions.java b/services/core/java/com/android/server/notification/ZenModeConditions.java
index 52d0c41..d44baeb 100644
--- a/services/core/java/com/android/server/notification/ZenModeConditions.java
+++ b/services/core/java/com/android/server/notification/ZenModeConditions.java
@@ -113,15 +113,18 @@
     }
 
     @Override
-    public void onConditionChanged(Uri id, Condition condition) {
+    public void onConditionChanged(Uri id, Condition condition, int callingUid) {
         if (DEBUG) Log.d(TAG, "onConditionChanged " + id + " " + condition);
         ZenModeConfig config = mHelper.getConfig();
         if (config == null) return;
-        final int callingUid = Binder.getCallingUid();
+        if (!Flags.fixCallingUidFromCps()) {
+            // Old behavior: overwrite with known-bad callingUid (always system_server).
+            callingUid = Binder.getCallingUid();
+        }
 
         // This change is known to be for UserHandle.CURRENT because ConditionProviders for
         // background users are not bound.
-        mHelper.setAutomaticZenRuleState(UserHandle.CURRENT, id, condition,
+        mHelper.setAutomaticZenRuleStateFromConditionProvider(UserHandle.CURRENT, id, condition,
                 callingUid == Process.SYSTEM_UID ? ZenModeConfig.ORIGIN_SYSTEM
                         : ZenModeConfig.ORIGIN_APP,
                 callingUid);
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index b571d62..0a63f3f 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -413,13 +413,13 @@
     }
 
     // TODO: b/310620812 - Make private (or inline) when MODES_API is inlined.
-    public List<ZenRule> getZenRules(UserHandle user) {
+    public List<ZenRule> getZenRules(UserHandle user, int callingUid) {
         List<ZenRule> rules = new ArrayList<>();
         synchronized (mConfigLock) {
             ZenModeConfig config = getConfigLocked(user);
             if (config == null) return rules;
             for (ZenRule rule : config.automaticRules.values()) {
-                if (canManageAutomaticZenRule(rule)) {
+                if (canManageAutomaticZenRule(rule, callingUid)) {
                     rules.add(rule);
                 }
             }
@@ -432,8 +432,8 @@
      * (which means the owned rules for a regular app, and every rule for system callers) together
      * with their ids.
      */
-    Map<String, AutomaticZenRule> getAutomaticZenRules(UserHandle user) {
-        List<ZenRule> ruleList = getZenRules(user);
+    Map<String, AutomaticZenRule> getAutomaticZenRules(UserHandle user, int callingUid) {
+        List<ZenRule> ruleList = getZenRules(user, callingUid);
         HashMap<String, AutomaticZenRule> rules = new HashMap<>(ruleList.size());
         for (ZenRule rule : ruleList) {
             rules.put(rule.id, zenRuleToAutomaticZenRule(rule));
@@ -441,7 +441,7 @@
         return rules;
     }
 
-    public AutomaticZenRule getAutomaticZenRule(UserHandle user, String id) {
+    public AutomaticZenRule getAutomaticZenRule(UserHandle user, String id, int callingUid) {
         ZenRule rule;
         synchronized (mConfigLock) {
             ZenModeConfig config = getConfigLocked(user);
@@ -449,7 +449,7 @@
             rule = config.automaticRules.get(id);
         }
         if (rule == null) return null;
-        if (canManageAutomaticZenRule(rule)) {
+        if (canManageAutomaticZenRule(rule, callingUid)) {
             return zenRuleToAutomaticZenRule(rule);
         }
         return null;
@@ -591,7 +591,7 @@
                         + " reason=" + reason);
             }
             ZenModeConfig.ZenRule oldRule = config.automaticRules.get(ruleId);
-            if (oldRule == null || !canManageAutomaticZenRule(oldRule)) {
+            if (oldRule == null || !canManageAutomaticZenRule(oldRule, callingUid)) {
                 throw new SecurityException(
                         "Cannot update rules not owned by your condition provider");
             }
@@ -859,7 +859,7 @@
             newConfig = config.copy();
             ZenRule ruleToRemove = newConfig.automaticRules.get(id);
             if (ruleToRemove == null) return false;
-            if (canManageAutomaticZenRule(ruleToRemove)) {
+            if (canManageAutomaticZenRule(ruleToRemove, callingUid)) {
                 newConfig.automaticRules.remove(id);
                 maybePreserveRemovedRule(newConfig, ruleToRemove, origin);
                 if (ruleToRemove.getPkg() != null
@@ -893,7 +893,8 @@
             newConfig = config.copy();
             for (int i = newConfig.automaticRules.size() - 1; i >= 0; i--) {
                 ZenRule rule = newConfig.automaticRules.get(newConfig.automaticRules.keyAt(i));
-                if (Objects.equals(rule.getPkg(), packageName) && canManageAutomaticZenRule(rule)) {
+                if (Objects.equals(rule.getPkg(), packageName)
+                        && canManageAutomaticZenRule(rule, callingUid)) {
                     newConfig.automaticRules.removeAt(i);
                     maybePreserveRemovedRule(newConfig, rule, origin);
                 }
@@ -938,14 +939,14 @@
     }
 
     @Condition.State
-    int getAutomaticZenRuleState(UserHandle user, String id) {
+    int getAutomaticZenRuleState(UserHandle user, String id, int callingUid) {
         synchronized (mConfigLock) {
             ZenModeConfig config = getConfigLocked(user);
             if (config == null) {
                 return Condition.STATE_UNKNOWN;
             }
             ZenRule rule = config.automaticRules.get(id);
-            if (rule == null || !canManageAutomaticZenRule(rule)) {
+            if (rule == null || !canManageAutomaticZenRule(rule, callingUid)) {
                 return Condition.STATE_UNKNOWN;
             }
             if (Flags.modesApi() && Flags.modesUi()) {
@@ -968,7 +969,7 @@
             newConfig = config.copy();
             ZenRule rule = newConfig.automaticRules.get(id);
             if (Flags.modesApi()) {
-                if (rule != null && canManageAutomaticZenRule(rule)) {
+                if (rule != null && canManageAutomaticZenRule(rule, callingUid)) {
                     setAutomaticZenRuleStateLocked(newConfig, Collections.singletonList(rule),
                             condition, origin, callingUid);
                 }
@@ -980,8 +981,8 @@
         }
     }
 
-    void setAutomaticZenRuleState(UserHandle user, Uri ruleDefinition, Condition condition,
-            @ConfigOrigin int origin, int callingUid) {
+    void setAutomaticZenRuleStateFromConditionProvider(UserHandle user, Uri ruleDefinition,
+            Condition condition, @ConfigOrigin int origin, int callingUid) {
         checkSetRuleStateOrigin("setAutomaticZenRuleState(Uri ruleDefinition)", origin);
         ZenModeConfig newConfig;
         synchronized (mConfigLock) {
@@ -992,7 +993,7 @@
             List<ZenRule> matchingRules = findMatchingRules(newConfig, ruleDefinition, condition);
             if (Flags.modesApi()) {
                 for (int i = matchingRules.size() - 1; i >= 0; i--) {
-                    if (!canManageAutomaticZenRule(matchingRules.get(i))) {
+                    if (!canManageAutomaticZenRule(matchingRules.get(i), callingUid)) {
                         matchingRules.remove(i);
                     }
                 }
@@ -1125,15 +1126,21 @@
         return count;
     }
 
-    public boolean canManageAutomaticZenRule(ZenRule rule) {
-        final int callingUid = Binder.getCallingUid();
+    public boolean canManageAutomaticZenRule(ZenRule rule, int callingUid) {
+        if (!com.android.server.notification.Flags.fixCallingUidFromCps()) {
+            // Old behavior: ignore supplied callingUid and instead obtain it here. Will be
+            // incorrect if not currently handling a Binder call.
+            callingUid = Binder.getCallingUid();
+        }
+
         if (callingUid == 0 || callingUid == Process.SYSTEM_UID) {
+            // Checked specifically, because checkCallingPermission() will fail.
             return true;
         } else if (mContext.checkCallingPermission(android.Manifest.permission.MANAGE_NOTIFICATIONS)
                 == PackageManager.PERMISSION_GRANTED) {
             return true;
         } else {
-            String[] packages = mPm.getPackagesForUid(Binder.getCallingUid());
+            String[] packages = mPm.getPackagesForUid(callingUid);
             if (packages != null) {
                 final int packageCount = packages.length;
                 for (int i = 0; i < packageCount; i++) {
@@ -2902,8 +2909,8 @@
     }
 
     /**
-     * Checks that the {@code origin} supplied to {@link #setAutomaticZenRuleState} overloads makes
-     * sense.
+     * Checks that the {@code origin} supplied to {@link #setAutomaticZenRuleState} or
+     * {@link #setAutomaticZenRuleStateFromConditionProvider} makes sense.
      */
     private static void checkSetRuleStateOrigin(String method, @ConfigOrigin int origin) {
         if (!Flags.modesApi()) {
diff --git a/services/core/java/com/android/server/notification/flags.aconfig b/services/core/java/com/android/server/notification/flags.aconfig
index 65a38ae..c1ca9c2 100644
--- a/services/core/java/com/android/server/notification/flags.aconfig
+++ b/services/core/java/com/android/server/notification/flags.aconfig
@@ -172,18 +172,28 @@
 }
 
 flag {
-  name: "notification_verify_channel_sound_uri"
+  name: "notification_vibration_in_sound_uri_for_channel"
   namespace: "systemui"
-  description: "Verify Uri permission for sound when creating a notification channel"
-  bug: "337775777"
+  description: "Enables sound uri with vibration source in notification channel"
+  bug: "351975435"
+}
+
+flag {
+  name: "nm_binder_perf_reduce_zen_broadcasts"
+  namespace: "systemui"
+  description: "Don't send duplicate zen-related (policy changed, etc) broadcasts"
+  bug: "324376849"
   metadata {
     purpose: PURPOSE_BUGFIX
   }
 }
 
 flag {
-  name: "notification_vibration_in_sound_uri_for_channel"
+  name: "fix_calling_uid_from_cps"
   namespace: "systemui"
-  description: "Enables sound uri with vibration source in notification channel"
-  bug: "351975435"
+  description: "Correctly checks zen rule ownership when a CPS notifies with a Condition"
+  bug: "379722187"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
 }
diff --git a/services/core/java/com/android/server/om/IdmapDaemon.java b/services/core/java/com/android/server/om/IdmapDaemon.java
index 1b22154..d33c860 100644
--- a/services/core/java/com/android/server/om/IdmapDaemon.java
+++ b/services/core/java/com/android/server/om/IdmapDaemon.java
@@ -28,6 +28,7 @@
 import android.os.IIdmap2;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.StrictMode;
 import android.os.SystemClock;
 import android.os.SystemService;
 import android.text.TextUtils;
@@ -40,7 +41,6 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.TimeoutException;
-import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * To prevent idmap2d from continuously running, the idmap daemon will terminate after 10 seconds
@@ -66,7 +66,7 @@
 
     private static IdmapDaemon sInstance;
     private volatile IIdmap2 mService;
-    private final AtomicInteger mOpenedCount = new AtomicInteger();
+    private int mOpenedCount = 0;
     private final Object mIdmapToken = new Object();
 
     /**
@@ -74,15 +74,20 @@
      * finalized, the idmap service will be stopped after a period of time unless another connection
      * to the service is open.
      **/
-    private class Connection implements AutoCloseable {
+    private final class Connection implements AutoCloseable {
         @Nullable
         private final IIdmap2 mIdmap2;
         private boolean mOpened = true;
 
-        private Connection(IIdmap2 idmap2) {
+        private Connection() {
+            mIdmap2 = null;
+            mOpened = false;
+        }
+
+        private Connection(@NonNull IIdmap2 idmap2) {
+            mIdmap2 = idmap2;
             synchronized (mIdmapToken) {
-                mOpenedCount.incrementAndGet();
-                mIdmap2 = idmap2;
+                ++mOpenedCount;
             }
         }
 
@@ -94,20 +99,22 @@
                 }
 
                 mOpened = false;
-                if (mOpenedCount.decrementAndGet() != 0) {
+                if (--mOpenedCount != 0) {
                     // Only post the callback to stop the service if the service does not have an
                     // open connection.
                     return;
                 }
 
+                final var service = mService;
                 FgThread.getHandler().postDelayed(() -> {
                     synchronized (mIdmapToken) {
-                        // Only stop the service if the service does not have an open connection.
-                        if (mService == null || mOpenedCount.get() != 0) {
+                        // Only stop the service if it's the one we were scheduled for and
+                        // it does not have an open connection.
+                        if (mService != service || mOpenedCount != 0) {
                             return;
                         }
 
-                        stopIdmapService();
+                        stopIdmapServiceLocked();
                         mService = null;
                     }
                 }, mIdmapToken, SERVICE_TIMEOUT_MS);
@@ -175,6 +182,8 @@
     }
 
     boolean idmapExists(String overlayPath, int userId) {
+        // The only way to verify an idmap is to read its state on disk.
+        final StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
         try (Connection c = connect()) {
             final IIdmap2 idmap2 = c.getIdmap2();
             if (idmap2 == null) {
@@ -187,6 +196,8 @@
         } catch (Exception e) {
             Slog.wtf(TAG, "failed to check if idmap exists for " + overlayPath, e);
             return false;
+        } finally {
+            StrictMode.setThreadPolicy(oldPolicy);
         }
     }
 
@@ -242,14 +253,16 @@
         } catch (Exception e) {
             Slog.wtf(TAG, "failed to get all fabricated overlays", e);
         } finally {
-            try {
-                if (c.getIdmap2() != null && iteratorId != -1) {
-                    c.getIdmap2().releaseFabricatedOverlayIterator(iteratorId);
+            if (c != null) {
+                try {
+                    if (c.getIdmap2() != null && iteratorId != -1) {
+                        c.getIdmap2().releaseFabricatedOverlayIterator(iteratorId);
+                    }
+                } catch (RemoteException e) {
+                    // ignore
                 }
-            } catch (RemoteException e) {
-                // ignore
+                c.close();
             }
-            c.close();
         }
         return allInfos;
     }
@@ -271,9 +284,11 @@
     }
 
     @Nullable
-    private IBinder getIdmapService() throws TimeoutException, RemoteException {
+    private IBinder getIdmapServiceLocked() throws TimeoutException, RemoteException {
         try {
-            SystemService.start(IDMAP_DAEMON);
+            if (!SystemService.isRunning(IDMAP_DAEMON)) {
+                SystemService.start(IDMAP_DAEMON);
+            }
         } catch (RuntimeException e) {
             Slog.wtf(TAG, "Failed to enable idmap2 daemon", e);
             if (e.getMessage().contains("failed to set system property")) {
@@ -306,9 +321,11 @@
                         walltimeMillis - endWalltimeMillis + SERVICE_CONNECT_WALLTIME_TIMEOUT_MS));
     }
 
-    private static void stopIdmapService() {
+    private static void stopIdmapServiceLocked() {
         try {
-            SystemService.stop(IDMAP_DAEMON);
+            if (SystemService.isRunning(IDMAP_DAEMON)) {
+                SystemService.stop(IDMAP_DAEMON);
+            }
         } catch (RuntimeException e) {
             // If the idmap daemon cannot be disabled for some reason, it is okay
             // since we already finished invoking idmap.
@@ -326,9 +343,9 @@
                 return new Connection(mService);
             }
 
-            IBinder binder = getIdmapService();
+            IBinder binder = getIdmapServiceLocked();
             if (binder == null) {
-                return new Connection(null);
+                return new Connection();
             }
 
             mService = IIdmap2.Stub.asInterface(binder);
diff --git a/services/core/java/com/android/server/om/OverlayActorEnforcer.java b/services/core/java/com/android/server/om/OverlayActorEnforcer.java
index 38f3939..d806770 100644
--- a/services/core/java/com/android/server/om/OverlayActorEnforcer.java
+++ b/services/core/java/com/android/server/om/OverlayActorEnforcer.java
@@ -19,7 +19,6 @@
 import android.annotation.NonNull;
 import android.content.om.OverlayInfo;
 import android.content.om.OverlayableInfo;
-import android.content.res.Flags;
 import android.net.Uri;
 import android.os.Process;
 import android.text.TextUtils;
@@ -50,6 +49,10 @@
      */
     static Pair<String, ActorState> getPackageNameForActor(@NonNull String actorUriString,
             @NonNull Map<String, Map<String, String>> namedActors) {
+        if (namedActors.isEmpty()) {
+            return Pair.create(null, ActorState.NO_NAMED_ACTORS);
+        }
+
         Uri actorUri = Uri.parse(actorUriString);
 
         String actorScheme = actorUri.getScheme();
@@ -58,10 +61,6 @@
             return Pair.create(null, ActorState.INVALID_OVERLAYABLE_ACTOR_NAME);
         }
 
-        if (namedActors.isEmpty()) {
-            return Pair.create(null, ActorState.NO_NAMED_ACTORS);
-        }
-
         String actorNamespace = actorUri.getAuthority();
         Map<String, String> namespace = namedActors.get(actorNamespace);
         if (ArrayUtils.isEmpty(namespace)) {
@@ -163,15 +162,11 @@
             return ActorState.UNABLE_TO_GET_TARGET_OVERLAYABLE;
         }
 
-        // Framework doesn't have <overlayable> declaration by design, and we still want to be able
-        // to enable its overlays from the packages with the permission.
-        if (targetOverlayable == null
-                && !(Flags.rroControlForAndroidNoOverlayable() && targetPackageName.equals(
-                "android"))) {
+        if (targetOverlayable == null) {
             return ActorState.MISSING_OVERLAYABLE;
         }
 
-        final String actor = targetOverlayable == null ? null : targetOverlayable.actor;
+        String actor = targetOverlayable.actor;
         if (TextUtils.isEmpty(actor)) {
             // If there's no actor defined, fallback to the legacy permission check
             try {
diff --git a/services/core/java/com/android/server/om/OverlayReferenceMapper.java b/services/core/java/com/android/server/om/OverlayReferenceMapper.java
index fdceabe..18de995 100644
--- a/services/core/java/com/android/server/om/OverlayReferenceMapper.java
+++ b/services/core/java/com/android/server/om/OverlayReferenceMapper.java
@@ -26,15 +26,13 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.CollectionUtils;
 import com.android.server.SystemConfig;
 import com.android.server.pm.pkg.AndroidPackage;
 
 import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 
 /**
@@ -121,20 +119,16 @@
                 return actorPair.first;
             }
 
-            @NonNull
+            @Nullable
             @Override
-            public Map<String, Set<String>> getTargetToOverlayables(@NonNull AndroidPackage pkg) {
+            public Pair<String, String> getTargetToOverlayables(@NonNull AndroidPackage pkg) {
                 String target = pkg.getOverlayTarget();
                 if (TextUtils.isEmpty(target)) {
-                    return Collections.emptyMap();
+                    return null;
                 }
 
                 String overlayable = pkg.getOverlayTargetOverlayableName();
-                Map<String, Set<String>> targetToOverlayables = new HashMap<>();
-                Set<String> overlayables = new HashSet<>();
-                overlayables.add(overlayable);
-                targetToOverlayables.put(target, overlayables);
-                return targetToOverlayables;
+                return Pair.create(target, overlayable);
             }
         };
     }
@@ -174,7 +168,7 @@
             }
 
             // TODO(b/135203078): Replace with isOverlay boolean flag check; fix test mocks
-            if (!mProvider.getTargetToOverlayables(pkg).isEmpty()) {
+            if (mProvider.getTargetToOverlayables(pkg) != null) {
                 addOverlay(pkg, otherPkgs, changed);
             }
 
@@ -245,20 +239,17 @@
             String target = targetPkg.getPackageName();
             removeTarget(target, changedPackages);
 
-            Map<String, String> overlayablesToActors = targetPkg.getOverlayables();
-            for (String overlayable : overlayablesToActors.keySet()) {
-                String actor = overlayablesToActors.get(overlayable);
+            final Map<String, String> overlayablesToActors = targetPkg.getOverlayables();
+            for (final var entry : overlayablesToActors.entrySet()) {
+                final String overlayable = entry.getKey();
+                final String actor = entry.getValue();
                 addTargetToMap(actor, target, changedPackages);
 
                 for (AndroidPackage overlayPkg : otherPkgs.values()) {
-                    Map<String, Set<String>> targetToOverlayables =
+                    var targetToOverlayables =
                             mProvider.getTargetToOverlayables(overlayPkg);
-                    Set<String> overlayables = targetToOverlayables.get(target);
-                    if (CollectionUtils.isEmpty(overlayables)) {
-                        continue;
-                    }
-
-                    if (overlayables.contains(overlayable)) {
+                    if (targetToOverlayables != null && targetToOverlayables.first.equals(target)
+                            && Objects.equals(targetToOverlayables.second, overlayable)) {
                         String overlay = overlayPkg.getPackageName();
                         addOverlayToMap(actor, target, overlay, changedPackages);
                     }
@@ -310,25 +301,22 @@
             String overlay = overlayPkg.getPackageName();
             removeOverlay(overlay, changedPackages);
 
-            Map<String, Set<String>> targetToOverlayables =
+            Pair<String, String> targetToOverlayables =
                     mProvider.getTargetToOverlayables(overlayPkg);
-            for (Map.Entry<String, Set<String>> entry : targetToOverlayables.entrySet()) {
-                String target = entry.getKey();
-                Set<String> overlayables = entry.getValue();
+            if (targetToOverlayables != null) {
+                String target = targetToOverlayables.first;
                 AndroidPackage targetPkg = otherPkgs.get(target);
                 if (targetPkg == null) {
-                    continue;
+                    return;
                 }
-
                 String targetPkgName = targetPkg.getPackageName();
                 Map<String, String> overlayableToActor = targetPkg.getOverlayables();
-                for (String overlayable : overlayables) {
-                    String actor = overlayableToActor.get(overlayable);
-                    if (TextUtils.isEmpty(actor)) {
-                        continue;
-                    }
-                    addOverlayToMap(actor, targetPkgName, overlay, changedPackages);
+                String overlayable = targetToOverlayables.second;
+                String actor = overlayableToActor.get(overlayable);
+                if (TextUtils.isEmpty(actor)) {
+                    return;
                 }
+                addOverlayToMap(actor, targetPkgName, overlay, changedPackages);
             }
         }
     }
@@ -430,11 +418,11 @@
         String getActorPkg(@NonNull String actor);
 
         /**
-         * Mock response of multiple overlay tags.
+         * Mock response of overlay tags.
          *
          * TODO(b/119899133): Replace with actual implementation; fix OverlayReferenceMapperTests
          */
-        @NonNull
-        Map<String, Set<String>> getTargetToOverlayables(@NonNull AndroidPackage pkg);
+        @Nullable
+        Pair<String, String> getTargetToOverlayables(@NonNull AndroidPackage pkg);
     }
 }
diff --git a/services/core/java/com/android/server/pm/ComputerEngine.java b/services/core/java/com/android/server/pm/ComputerEngine.java
index 3861762..b441e9d 100644
--- a/services/core/java/com/android/server/pm/ComputerEngine.java
+++ b/services/core/java/com/android/server/pm/ComputerEngine.java
@@ -5386,7 +5386,7 @@
                     + ", uid:" + callingUid);
             throw new IllegalArgumentException("Unknown package: " + packageName);
         }
-        if (pkg.getUid() != callingUid
+        if (!UserHandle.isSameApp(callingUid, pkg.getUid())
                 && Process.SYSTEM_UID != callingUid) {
             throw new SecurityException("May not access signing KeySet of other apps.");
         }
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 85b92c7..4c70d23 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -1533,7 +1533,7 @@
         boolean systemApp = false;
         boolean replace = false;
         synchronized (mPm.mLock) {
-            final PackageSetting ps = mPm.mSettings.getPackageLPr(pkgName);
+            PackageSetting ps = mPm.mSettings.getPackageLPr(pkgName);
             // Check if installing already existing package
             if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
                 String oldName = mPm.mSettings.getRenamedPackageLPr(pkgName);
@@ -1544,14 +1544,15 @@
                     // name.  We must continue using the original name, so
                     // rename the new package here.
                     parsedPackage.setPackageName(oldName);
-                    pkgName = parsedPackage.getPackageName();
-                    replace = true;
+                    pkgName = oldName;
+                    ps = mPm.mSettings.getPackageLPr(oldName);
                     if (DEBUG_INSTALL) {
                         Slog.d(TAG, "Replacing existing renamed package: oldName="
                                 + oldName + " pkgName=" + pkgName);
                     }
-                } else if (ps != null) {
-                    // This package, under its official name, already exists
+                }
+                if (ps != null) {
+                    // This package, under its official name or its old name, already exists
                     // on the device; we should replace it.
                     replace = true;
                     if (DEBUG_INSTALL) Slog.d(TAG, "Replace existing package: " + pkgName);
diff --git a/services/core/java/com/android/server/pm/ResilientAtomicFile.java b/services/core/java/com/android/server/pm/ResilientAtomicFile.java
index 3aefc5a..473ed61 100644
--- a/services/core/java/com/android/server/pm/ResilientAtomicFile.java
+++ b/services/core/java/com/android/server/pm/ResilientAtomicFile.java
@@ -23,6 +23,7 @@
 import android.util.Log;
 import android.util.Slog;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.security.FileIntegrity;
 
 import libcore.io.IoUtils;
@@ -121,6 +122,11 @@
     }
 
     public void finishWrite(FileOutputStream str) throws IOException {
+        finishWrite(str, true /* doFsVerity */);
+    }
+
+    @VisibleForTesting
+    public void finishWrite(FileOutputStream str, final boolean doFsVerity) throws IOException {
         if (mMainOutStream != str) {
             throw new IllegalStateException("Invalid incoming stream.");
         }
@@ -145,13 +151,15 @@
                 finalizeOutStream(reserveOutStream);
             }
 
-            // Protect both main and reserve using fs-verity.
-            try (ParcelFileDescriptor mainPfd = ParcelFileDescriptor.dup(mainInStream.getFD());
-                 ParcelFileDescriptor copyPfd = ParcelFileDescriptor.dup(reserveInStream.getFD())) {
-                FileIntegrity.setUpFsVerity(mainPfd);
-                FileIntegrity.setUpFsVerity(copyPfd);
-            } catch (IOException e) {
-                Slog.e(LOG_TAG, "Failed to verity-protect " + mDebugName, e);
+            if (doFsVerity) {
+                // Protect both main and reserve using fs-verity.
+                try (ParcelFileDescriptor mainPfd = ParcelFileDescriptor.dup(mainInStream.getFD());
+                     ParcelFileDescriptor copyPfd = ParcelFileDescriptor.dup(reserveInStream.getFD())) {
+                    FileIntegrity.setUpFsVerity(mainPfd);
+                    FileIntegrity.setUpFsVerity(copyPfd);
+                } catch (IOException e) {
+                    Slog.e(LOG_TAG, "Failed to verity-protect " + mDebugName, e);
+                }
             }
         } catch (IOException e) {
             Slog.e(LOG_TAG, "Failed to write reserve copy " + mDebugName + ": " + mReserveCopy, e);
diff --git a/services/core/java/com/android/server/pm/SharedLibrariesImpl.java b/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
index 17d7a14..e1b7622 100644
--- a/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
+++ b/services/core/java/com/android/server/pm/SharedLibrariesImpl.java
@@ -612,7 +612,7 @@
                 final PackageSetting staticLibPkgSetting =
                         mPm.getPackageSettingForMutation(sharedLibraryInfo.getPackageName());
                 if (staticLibPkgSetting == null) {
-                    Slog.wtf(TAG, "Shared lib without setting: " + sharedLibraryInfo);
+                    Slog.w(TAG, "Shared lib without setting: " + sharedLibraryInfo);
                     continue;
                 }
                 for (int u = 0; u < installedUserCount; u++) {
diff --git a/services/core/java/com/android/server/pm/ShortcutPackageItem.java b/services/core/java/com/android/server/pm/ShortcutPackageItem.java
index 44789e4..027da49 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackageItem.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackageItem.java
@@ -179,7 +179,7 @@
                 itemOut.endDocument();
 
                 os.flush();
-                file.finishWrite(os);
+                mShortcutUser.mService.injectFinishWrite(file, os);
             } catch (XmlPullParserException | IOException e) {
                 Slog.e(TAG, "Failed to write to file " + file.getBaseFile(), e);
                 file.failWrite(os);
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 2785da5..373c1ed 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -1008,7 +1008,7 @@
                 out.endDocument();
 
                 // Close.
-                file.finishWrite(outs);
+                injectFinishWrite(file, outs);
             } catch (IOException e) {
                 Slog.w(TAG, "Failed to write to file " + file.getBaseFile(), e);
                 file.failWrite(outs);
@@ -1096,7 +1096,7 @@
                     saveUserInternalLocked(userId, os, /* forBackup= */ false);
                 }
 
-                file.finishWrite(os);
+                injectFinishWrite(file, os);
 
                 // Remove all dangling bitmap files.
                 cleanupDanglingBitmapDirectoriesLocked(userId);
@@ -5067,6 +5067,12 @@
         return Build.FINGERPRINT;
     }
 
+    // Injection point.
+    void injectFinishWrite(@NonNull final ResilientAtomicFile file,
+            @NonNull final FileOutputStream os) throws IOException {
+        file.finishWrite(os);
+    }
+
     final void wtf(String message) {
         wtf(message, /* exception= */ null);
     }
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 672eb4c..9d840d0 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -1681,8 +1681,8 @@
 
             // handle overflow
             if (attributionChainId < 0) {
-                attributionChainId = 0;
                 sAttributionChainIds.set(0);
+                attributionChainId = sAttributionChainIds.incrementAndGet();
             }
             return attributionChainId;
         }
diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java b/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java
index 7a5a14d..b329437 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java
@@ -293,8 +293,8 @@
         if (mOverlayPaths == null && mSharedLibraryOverlayPaths == null) {
             return null;
         }
-        final OverlayPaths.Builder newPaths = new OverlayPaths.Builder();
-        newPaths.addAll(mOverlayPaths);
+        final OverlayPaths.Builder newPaths = mOverlayPaths == null
+                ? new OverlayPaths.Builder() : new OverlayPaths.Builder(mOverlayPaths);
         if (mSharedLibraryOverlayPaths != null) {
             for (final OverlayPaths libOverlayPaths : mSharedLibraryOverlayPaths.values()) {
                 newPaths.addAll(libOverlayPaths);
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 5ab5965..7f511e1 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -85,15 +85,16 @@
 
 import static com.android.hardware.input.Flags.enableNew25q2Keycodes;
 import static com.android.hardware.input.Flags.enableTalkbackAndMagnifierKeyGestures;
+import static com.android.hardware.input.Flags.enableVoiceAccessKeyGestures;
 import static com.android.hardware.input.Flags.inputManagerLifecycleSupport;
 import static com.android.hardware.input.Flags.keyboardA11yShortcutControl;
 import static com.android.hardware.input.Flags.modifierShortcutDump;
 import static com.android.hardware.input.Flags.overridePowerKeyBehaviorInFocusedWindow;
 import static com.android.hardware.input.Flags.useKeyGestureEventHandler;
+import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.SCREENSHOT_KEYCHORD_DELAY;
 import static com.android.server.GestureLauncherService.DOUBLE_POWER_TAP_COUNT_THRESHOLD;
 import static com.android.server.flags.Flags.modifierShortcutManagerMultiuser;
 import static com.android.server.flags.Flags.newBugreportKeyboardShortcut;
-import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.SCREENSHOT_KEYCHORD_DELAY;
 import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVERED;
 import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_COVER_ABSENT;
 import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.CAMERA_LENS_UNCOVERED;
@@ -502,6 +503,8 @@
 
     private TalkbackShortcutController mTalkbackShortcutController;
 
+    private VoiceAccessShortcutController mVoiceAccessShortcutController;
+
     private WindowWakeUpPolicy mWindowWakeUpPolicy;
 
     /**
@@ -562,8 +565,8 @@
     volatile boolean mPowerKeyHandled;
     volatile boolean mBackKeyHandled;
     volatile boolean mEndCallKeyHandled;
-    volatile boolean mCameraGestureTriggered;
-    volatile boolean mCameraGestureTriggeredDuringGoingToSleep;
+    volatile boolean mPowerButtonLaunchGestureTriggered;
+    volatile boolean mPowerButtonLaunchGestureTriggeredDuringGoingToSleep;
 
     /**
      * {@code true} if the device is entering a low-power state; {@code false otherwise}.
@@ -2265,6 +2268,10 @@
             return new TalkbackShortcutController(mContext);
         }
 
+        VoiceAccessShortcutController getVoiceAccessShortcutController() {
+            return new VoiceAccessShortcutController(mContext);
+        }
+
         WindowWakeUpPolicy getWindowWakeUpPolicy() {
             return new WindowWakeUpPolicy(mContext);
         }
@@ -2512,6 +2519,7 @@
                 com.android.internal.R.integer.config_keyguardDrawnTimeout);
         mKeyguardDelegate = injector.getKeyguardServiceDelegate();
         mTalkbackShortcutController = injector.getTalkbackShortcutController();
+        mVoiceAccessShortcutController = injector.getVoiceAccessShortcutController();
         mWindowWakeUpPolicy = injector.getWindowWakeUpPolicy();
         initKeyCombinationRules();
         initSingleKeyGestureRules(injector.getLooper());
@@ -4262,6 +4270,8 @@
                                 .isAccessibilityShortcutAvailable(false);
                     case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_TALKBACK:
                         return enableTalkbackAndMagnifierKeyGestures();
+                    case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_VOICE_ACCESS:
+                        return enableVoiceAccessKeyGestures();
                     default:
                         return false;
                 }
@@ -4492,6 +4502,14 @@
                     return true;
                 }
                 break;
+            case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_VOICE_ACCESS:
+                if (enableVoiceAccessKeyGestures()) {
+                    if (complete) {
+                        mVoiceAccessShortcutController.toggleVoiceAccess(mCurrentUserId);
+                    }
+                    return true;
+                }
+                break;
             case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION:
                 AppLaunchData data = event.getAppLaunchData();
                 if (complete && canLaunchApp && data != null
@@ -5893,7 +5911,7 @@
         if (mGestureLauncherService == null) {
             return false;
         }
-        mCameraGestureTriggered = false;
+        mPowerButtonLaunchGestureTriggered = false;
         final MutableBoolean outLaunched = new MutableBoolean(false);
         final boolean intercept =
                 mGestureLauncherService.interceptPowerKeyDown(event, interactive, outLaunched);
@@ -5903,9 +5921,9 @@
             // detector from processing the power key later on.
             return intercept;
         }
-        mCameraGestureTriggered = true;
+        mPowerButtonLaunchGestureTriggered = true;
         if (mRequestedOrSleepingDefaultDisplay) {
-            mCameraGestureTriggeredDuringGoingToSleep = true;
+            mPowerButtonLaunchGestureTriggeredDuringGoingToSleep = true;
             // Wake device up early to prevent display doing redundant turning off/on stuff.
             mWindowWakeUpPolicy.wakeUpFromPowerKeyCameraGesture();
         }
@@ -6282,13 +6300,13 @@
 
         if (mKeyguardDelegate != null) {
             mKeyguardDelegate.onFinishedGoingToSleep(pmSleepReason,
-                    mCameraGestureTriggeredDuringGoingToSleep);
+                    mPowerButtonLaunchGestureTriggeredDuringGoingToSleep);
         }
         if (mDisplayFoldController != null) {
             mDisplayFoldController.finishedGoingToSleep();
         }
-        mCameraGestureTriggeredDuringGoingToSleep = false;
-        mCameraGestureTriggered = false;
+        mPowerButtonLaunchGestureTriggeredDuringGoingToSleep = false;
+        mPowerButtonLaunchGestureTriggered = false;
     }
 
     // Called on the PowerManager's Notifier thread.
@@ -6319,10 +6337,10 @@
         mDefaultDisplayRotation.updateOrientationListener();
 
         if (mKeyguardDelegate != null) {
-            mKeyguardDelegate.onStartedWakingUp(pmWakeReason, mCameraGestureTriggered);
+            mKeyguardDelegate.onStartedWakingUp(pmWakeReason, mPowerButtonLaunchGestureTriggered);
         }
 
-        mCameraGestureTriggered = false;
+        mPowerButtonLaunchGestureTriggered = false;
     }
 
     // Called on the PowerManager's Notifier thread.
diff --git a/services/core/java/com/android/server/policy/TalkbackShortcutController.java b/services/core/java/com/android/server/policy/TalkbackShortcutController.java
index 9e16a7d..efda337 100644
--- a/services/core/java/com/android/server/policy/TalkbackShortcutController.java
+++ b/services/core/java/com/android/server/policy/TalkbackShortcutController.java
@@ -18,20 +18,15 @@
 
 import static com.android.internal.util.FrameworkStatsLog.ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_WEAR_TRIPLE_PRESS_GESTURE;
 
-import android.accessibilityservice.AccessibilityServiceInfo;
 import android.content.ComponentName;
 import android.content.Context;
-import android.content.pm.PackageManager;
-import android.content.pm.ServiceInfo;
 import android.os.UserHandle;
 import android.provider.Settings;
-import android.view.accessibility.AccessibilityManager;
 
 import com.android.internal.accessibility.util.AccessibilityStatsLogUtils;
 import com.android.internal.accessibility.util.AccessibilityUtils;
 import com.android.internal.annotations.VisibleForTesting;
 
-import java.util.List;
 import java.util.Set;
 
 /**
@@ -42,7 +37,6 @@
 class TalkbackShortcutController {
     private static final String TALKBACK_LABEL = "TalkBack";
     private final Context mContext;
-    private final PackageManager mPackageManager;
 
     public enum ShortcutSource {
         GESTURE,
@@ -51,7 +45,6 @@
 
     TalkbackShortcutController(Context context) {
         mContext = context;
-        mPackageManager = mContext.getPackageManager();
     }
 
     /**
@@ -63,7 +56,10 @@
     boolean toggleTalkback(int userId, ShortcutSource source) {
         final Set<ComponentName> enabledServices =
                 AccessibilityUtils.getEnabledServicesFromSettings(mContext, userId);
-        ComponentName componentName = getTalkbackComponent();
+        ComponentName componentName =
+                AccessibilityUtils.getInstalledAccessibilityServiceComponentNameByLabel(
+                        mContext, TALKBACK_LABEL);
+        ;
         if (componentName == null) {
             return false;
         }
@@ -83,21 +79,6 @@
         return isTalkbackAlreadyEnabled;
     }
 
-    private ComponentName getTalkbackComponent() {
-        AccessibilityManager accessibilityManager = mContext.getSystemService(
-                AccessibilityManager.class);
-        List<AccessibilityServiceInfo> serviceInfos =
-                accessibilityManager.getInstalledAccessibilityServiceList();
-
-        for (AccessibilityServiceInfo service : serviceInfos) {
-            final ServiceInfo serviceInfo = service.getResolveInfo().serviceInfo;
-            if (isTalkback(serviceInfo)) {
-                return new ComponentName(serviceInfo.packageName, serviceInfo.name);
-            }
-        }
-        return null;
-    }
-
     boolean isTalkBackShortcutGestureEnabled() {
         return Settings.System.getIntForUser(mContext.getContentResolver(),
                 Settings.System.WEAR_ACCESSIBILITY_GESTURE_ENABLED,
@@ -120,9 +101,4 @@
                 ACCESSIBILITY_SHORTCUT_REPORTED__SHORTCUT_TYPE__A11Y_WEAR_TRIPLE_PRESS_GESTURE,
                 /* serviceEnabled= */ true);
     }
-
-    private boolean isTalkback(ServiceInfo info) {
-        return TALKBACK_LABEL.equals(info.loadLabel(mPackageManager).toString())
-            && (info.applicationInfo.isSystemApp() || info.applicationInfo.isUpdatedSystemApp());
-    }
 }
diff --git a/services/core/java/com/android/server/policy/VoiceAccessShortcutController.java b/services/core/java/com/android/server/policy/VoiceAccessShortcutController.java
new file mode 100644
index 0000000..a37fb11
--- /dev/null
+++ b/services/core/java/com/android/server/policy/VoiceAccessShortcutController.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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 android.content.ComponentName;
+import android.content.Context;
+import android.util.Slog;
+
+import com.android.internal.accessibility.util.AccessibilityUtils;
+
+import androidx.annotation.VisibleForTesting;
+
+import java.util.Set;
+
+/** This class controls voice access shortcut related operations such as toggling, querying. */
+class VoiceAccessShortcutController {
+    private static final String TAG = VoiceAccessShortcutController.class.getSimpleName();
+    private static final String VOICE_ACCESS_LABEL = "Voice Access";
+
+    private final Context mContext;
+
+    VoiceAccessShortcutController(Context context) {
+        mContext = context;
+    }
+
+    /**
+     * A function that toggles voice access service.
+     *
+     * @return whether voice access is enabled after being toggled.
+     */
+    @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
+    boolean toggleVoiceAccess(int userId) {
+        final Set<ComponentName> enabledServices =
+                AccessibilityUtils.getEnabledServicesFromSettings(mContext, userId);
+        ComponentName componentName =
+                AccessibilityUtils.getInstalledAccessibilityServiceComponentNameByLabel(
+                        mContext, VOICE_ACCESS_LABEL);
+        if (componentName == null) {
+            Slog.e(TAG, "Toggle Voice Access failed due to componentName being null");
+            return false;
+        }
+
+        boolean newState = !enabledServices.contains(componentName);
+        AccessibilityUtils.setAccessibilityServiceState(mContext, componentName, newState, userId);
+
+        return newState;
+    }
+}
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 da8b01a..587447b 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
@@ -198,7 +198,7 @@
                 if (mKeyguardState.interactiveState == INTERACTIVE_STATE_AWAKE
                         || mKeyguardState.interactiveState == INTERACTIVE_STATE_WAKING) {
                     mKeyguardService.onStartedWakingUp(PowerManager.WAKE_REASON_UNKNOWN,
-                            false /* cameraGestureTriggered */);
+                            false /* powerButtonLaunchGestureTriggered */);
                 }
                 if (mKeyguardState.interactiveState == INTERACTIVE_STATE_AWAKE) {
                     mKeyguardService.onFinishedWakingUp();
@@ -319,10 +319,10 @@
     }
 
     public void onStartedWakingUp(
-            @PowerManager.WakeReason int pmWakeReason, boolean cameraGestureTriggered) {
+            @PowerManager.WakeReason int pmWakeReason, boolean powerButtonLaunchGestureTriggered) {
         if (mKeyguardService != null) {
             if (DEBUG) Log.v(TAG, "onStartedWakingUp()");
-            mKeyguardService.onStartedWakingUp(pmWakeReason, cameraGestureTriggered);
+            mKeyguardService.onStartedWakingUp(pmWakeReason, powerButtonLaunchGestureTriggered);
         }
         mKeyguardState.interactiveState = INTERACTIVE_STATE_WAKING;
     }
@@ -383,9 +383,11 @@
     }
 
     public void onFinishedGoingToSleep(
-            @PowerManager.GoToSleepReason int pmSleepReason, boolean cameraGestureTriggered) {
+            @PowerManager.GoToSleepReason int pmSleepReason,
+            boolean powerButtonLaunchGestureTriggered) {
         if (mKeyguardService != null) {
-            mKeyguardService.onFinishedGoingToSleep(pmSleepReason, cameraGestureTriggered);
+            mKeyguardService.onFinishedGoingToSleep(pmSleepReason,
+                    powerButtonLaunchGestureTriggered);
         }
         mKeyguardState.interactiveState = INTERACTIVE_STATE_SLEEP;
     }
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java
index cd789ea..f2342e0 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceWrapper.java
@@ -113,9 +113,10 @@
 
     @Override
     public void onFinishedGoingToSleep(
-            @PowerManager.GoToSleepReason int pmSleepReason, boolean cameraGestureTriggered) {
+            @PowerManager.GoToSleepReason int pmSleepReason,
+            boolean powerButtonLaunchGestureTriggered) {
         try {
-            mService.onFinishedGoingToSleep(pmSleepReason, cameraGestureTriggered);
+            mService.onFinishedGoingToSleep(pmSleepReason, powerButtonLaunchGestureTriggered);
         } catch (RemoteException e) {
             Slog.w(TAG , "Remote Exception", e);
         }
@@ -123,9 +124,9 @@
 
     @Override
     public void onStartedWakingUp(
-            @PowerManager.WakeReason int pmWakeReason, boolean cameraGestureTriggered) {
+            @PowerManager.WakeReason int pmWakeReason, boolean powerButtonLaunchGestureTriggered) {
         try {
-            mService.onStartedWakingUp(pmWakeReason, cameraGestureTriggered);
+            mService.onStartedWakingUp(pmWakeReason, powerButtonLaunchGestureTriggered);
         } catch (RemoteException e) {
             Slog.w(TAG , "Remote Exception", e);
         }
diff --git a/services/core/java/com/android/server/policy/role/RoleServicePlatformHelperImpl.java b/services/core/java/com/android/server/policy/role/RoleServicePlatformHelperImpl.java
index 7808c4e..e09ab60 100644
--- a/services/core/java/com/android/server/policy/role/RoleServicePlatformHelperImpl.java
+++ b/services/core/java/com/android/server/policy/role/RoleServicePlatformHelperImpl.java
@@ -28,7 +28,6 @@
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.ResolveInfo;
-import android.content.pm.Signature;
 import android.os.Environment;
 import android.permission.flags.Flags;
 import android.provider.Settings;
@@ -312,17 +311,10 @@
         DataOutputStream dataOutputStream = new DataOutputStream(new BufferedOutputStream(mdos));
         packageManagerInternal.forEachInstalledPackage(pkg -> {
             try {
-                dataOutputStream.writeUTF(pkg.getPackageName());
-                dataOutputStream.writeLong(pkg.getLongVersionCode());
+                dataOutputStream.writeUTF(pkg.getPath());
                 dataOutputStream.writeInt(packageManagerInternal.getApplicationEnabledState(
                         pkg.getPackageName(), userId));
 
-                final Set<String> requestedPermissions = pkg.getRequestedPermissions();
-                dataOutputStream.writeInt(requestedPermissions.size());
-                for (String permissionName : requestedPermissions) {
-                    dataOutputStream.writeUTF(permissionName);
-                }
-
                 final ArraySet<String> enabledComponents =
                         packageManagerInternal.getEnabledComponents(pkg.getPackageName(), userId);
                 final int enabledComponentsSize = CollectionUtils.size(enabledComponents);
@@ -337,10 +329,6 @@
                 for (int i = 0; i < disabledComponentsSize; i++) {
                     dataOutputStream.writeUTF(disabledComponents.valueAt(i));
                 }
-
-                for (final Signature signature : pkg.getSigningDetails().getSignatures()) {
-                    dataOutputStream.write(signature.toByteArray());
-                }
             } catch (IOException e) {
                 // Never happens for MessageDigestOutputStream and DataOutputStream.
                 throw new AssertionError(e);
diff --git a/services/core/java/com/android/server/power/hint/HintManagerService.java b/services/core/java/com/android/server/power/hint/HintManagerService.java
index c4e4c42..a6f2a37 100644
--- a/services/core/java/com/android/server/power/hint/HintManagerService.java
+++ b/services/core/java/com/android/server/power/hint/HintManagerService.java
@@ -121,6 +121,8 @@
     @VisibleForTesting final long mHintSessionPreferredRate;
 
     @VisibleForTesting static final int MAX_GRAPHICS_PIPELINE_THREADS_COUNT = 5;
+    private static final int DEFAULT_MAX_CPU_HEADROOM_THREADS_COUNT = 5;
+    private static final int DEFAULT_CHECK_HEADROOM_PROC_STAT_MIN_MILLIS = 50;
 
     // Multi-level map storing all active AppHintSessions.
     // First level is keyed by the UID of the client process creating the session.
@@ -206,12 +208,17 @@
             "persist.hms.check_headroom_affinity";
     private static final String PROPERTY_CHECK_HEADROOM_PROC_STAT_MIN_MILLIS =
             "persist.hms.check_headroom_proc_stat_min_millis";
+    private static final String PROPERTY_CPU_HEADROOM_TID_MAX_CNT =
+            "persist.hms.cpu_headroom_tid_max_cnt";
     private Boolean mFMQUsesIntegratedEventFlag = false;
 
     private final Object mCpuHeadroomLock = new Object();
     @VisibleForTesting
     final float mJiffyMillis;
+    private final boolean mCheckHeadroomTid;
+    private final boolean mCheckHeadroomAffinity;
     private final int mCheckHeadroomProcStatMinMillis;
+    private final int mCpuHeadroomMaxTidCnt;
     @GuardedBy("mCpuHeadroomLock")
     private long mLastCpuUserModeTimeCheckedMillis = 0;
     @GuardedBy("mCpuHeadroomLock")
@@ -339,13 +346,23 @@
             mUidToLastUserModeJiffies = new ArrayMap<>();
             long jiffyHz = Os.sysconf(OsConstants._SC_CLK_TCK);
             mJiffyMillis = 1000.0f / jiffyHz;
+            mCheckHeadroomTid = SystemProperties.getBoolean(PROPERTY_CHECK_HEADROOM_TID, true);
+            mCheckHeadroomAffinity = SystemProperties.getBoolean(PROPERTY_CHECK_HEADROOM_AFFINITY,
+                    true);
             mCheckHeadroomProcStatMinMillis = SystemProperties.getInt(
-                    PROPERTY_CHECK_HEADROOM_PROC_STAT_MIN_MILLIS, 50);
+                    PROPERTY_CHECK_HEADROOM_PROC_STAT_MIN_MILLIS,
+                    DEFAULT_CHECK_HEADROOM_PROC_STAT_MIN_MILLIS);
+            mCpuHeadroomMaxTidCnt = Math.min(SystemProperties.getInt(
+                    PROPERTY_CPU_HEADROOM_TID_MAX_CNT, DEFAULT_MAX_CPU_HEADROOM_THREADS_COUNT),
+                    mSupportInfo.headroom.cpuMaxTidCount);
         } else {
             mCpuHeadroomCache = null;
             mUidToLastUserModeJiffies = null;
             mJiffyMillis = 0.0f;
+            mCheckHeadroomTid = true;
+            mCheckHeadroomAffinity = true;
             mCheckHeadroomProcStatMinMillis = 0;
+            mCpuHeadroomMaxTidCnt = 0;
         }
         if (mSupportInfo.headroom.isGpuSupported) {
             mGpuHeadroomCache = new HeadroomCache<>(2, mSupportInfo.headroom.gpuMinIntervalMillis);
@@ -1577,8 +1594,7 @@
             if (params.usesDeviceHeadroom) {
                 halParams.tids = new int[]{};
             } else if (params.tids != null && params.tids.length > 0) {
-                if (UserHandle.getAppId(uid) != Process.SYSTEM_UID && SystemProperties.getBoolean(
-                        PROPERTY_CHECK_HEADROOM_TID, true)) {
+                if (UserHandle.getAppId(uid) != Process.SYSTEM_UID && mCheckHeadroomTid) {
                     final int tgid = Process.getThreadGroupLeader(Binder.getCallingPid());
                     for (int tid : params.tids) {
                         if (Process.getThreadGroupLeader(tid) != tgid) {
@@ -1588,18 +1604,15 @@
                         }
                     }
                 }
-                if (cpuHeadroomAffinityCheck() && params.tids.length > 1
-                        && SystemProperties.getBoolean(PROPERTY_CHECK_HEADROOM_AFFINITY, true)) {
+                if (cpuHeadroomAffinityCheck() && mCheckHeadroomAffinity
+                        && params.tids.length > 1) {
                     checkThreadAffinityForTids(params.tids);
                 }
                 halParams.tids = params.tids;
             }
-            if (halParams.calculationWindowMillis
-                    == mDefaultCpuHeadroomCalculationWindowMillis) {
-                synchronized (mCpuHeadroomLock) {
-                    final CpuHeadroomResult res = mCpuHeadroomCache.get(halParams);
-                    if (res != null) return res;
-                }
+            synchronized (mCpuHeadroomLock) {
+                final CpuHeadroomResult res = mCpuHeadroomCache.get(halParams);
+                if (res != null) return res;
             }
             final boolean shouldCheckUserModeCpuTime =
                     mEnforceCpuHeadroomUserModeCpuTimeCheck
@@ -1622,11 +1635,8 @@
                     Slog.wtf(TAG, "CPU headroom from Power HAL is invalid");
                     return null;
                 }
-                if (halParams.calculationWindowMillis
-                        == mDefaultCpuHeadroomCalculationWindowMillis) {
-                    synchronized (mCpuHeadroomLock) {
-                        mCpuHeadroomCache.add(halParams, result);
-                    }
+                synchronized (mCpuHeadroomLock) {
+                    mCpuHeadroomCache.add(halParams, result);
                 }
                 if (shouldCheckUserModeCpuTime) {
                     synchronized (mCpuHeadroomLock) {
@@ -1715,15 +1725,22 @@
                 throw new IllegalArgumentException(
                         "Unknown CPU headroom calculation type " + (int) params.calculationType);
             }
-            if (params.calculationWindowMillis < 50 || params.calculationWindowMillis > 10000) {
+            if (params.calculationWindowMillis < mSupportInfo.headroom.cpuMinCalculationWindowMillis
+                    || params.calculationWindowMillis
+                    > mSupportInfo.headroom.cpuMaxCalculationWindowMillis) {
                 throw new IllegalArgumentException(
-                        "Invalid CPU headroom calculation window, expected [50, 10000] but got "
+                        "Invalid CPU headroom calculation window, expected ["
+                                + mSupportInfo.headroom.cpuMinCalculationWindowMillis
+                                + ", "
+                                + mSupportInfo.headroom.cpuMaxCalculationWindowMillis
+                                + "] but got "
                                 + params.calculationWindowMillis);
             }
             if (!params.usesDeviceHeadroom) {
-                if (params.tids != null && params.tids.length > 5) {
+                if (params.tids != null && params.tids.length > mCpuHeadroomMaxTidCnt) {
                     throw new IllegalArgumentException(
-                            "More than 5 TIDs requested: " + params.tids.length);
+                            "More than " + mCpuHeadroomMaxTidCnt + " TIDs requested: "
+                                    + params.tids.length);
                 }
             }
         }
@@ -1737,12 +1754,9 @@
             final GpuHeadroomParams halParams = new GpuHeadroomParams();
             halParams.calculationType = params.calculationType;
             halParams.calculationWindowMillis = params.calculationWindowMillis;
-            if (halParams.calculationWindowMillis
-                    == mDefaultGpuHeadroomCalculationWindowMillis) {
-                synchronized (mGpuHeadroomLock) {
-                    final GpuHeadroomResult res = mGpuHeadroomCache.get(halParams);
-                    if (res != null) return res;
-                }
+            synchronized (mGpuHeadroomLock) {
+                final GpuHeadroomResult res = mGpuHeadroomCache.get(halParams);
+                if (res != null) return res;
             }
             // return from HAL directly
             try {
@@ -1751,11 +1765,8 @@
                     Slog.wtf(TAG, "GPU headroom from Power HAL is invalid");
                     return null;
                 }
-                if (halParams.calculationWindowMillis
-                        == mDefaultGpuHeadroomCalculationWindowMillis) {
-                    synchronized (mGpuHeadroomLock) {
-                        mGpuHeadroomCache.add(halParams, headroom);
-                    }
+                synchronized (mGpuHeadroomLock) {
+                    mGpuHeadroomCache.add(halParams, headroom);
                 }
                 return headroom;
             } catch (RemoteException e) {
@@ -1784,9 +1795,13 @@
                 throw new IllegalArgumentException(
                         "Unknown GPU headroom calculation type " + (int) params.calculationType);
             }
-            if (params.calculationWindowMillis < 50 || params.calculationWindowMillis > 10000) {
+            if (params.calculationWindowMillis < mSupportInfo.headroom.gpuMinCalculationWindowMillis
+                    || params.calculationWindowMillis
+                    > mSupportInfo.headroom.gpuMaxCalculationWindowMillis) {
                 throw new IllegalArgumentException(
-                        "Invalid GPU headroom calculation window, expected [50, 10000] but got "
+                        "Invalid GPU headroom calculation window, expected ["
+                                + mSupportInfo.headroom.gpuMinCalculationWindowMillis + ", "
+                                + mSupportInfo.headroom.gpuMaxCalculationWindowMillis + "] but got "
                                 + params.calculationWindowMillis);
             }
         }
@@ -1819,9 +1834,15 @@
         @Override
         public IHintManager.HintManagerClientData
                 registerClient(@NonNull IHintManager.IHintManagerClient clientBinder) {
+            return getClientData();
+        }
+
+        @Override
+        public IHintManager.HintManagerClientData getClientData() {
             IHintManager.HintManagerClientData out = new IHintManager.HintManagerClientData();
             out.preferredRateNanos = mHintSessionPreferredRate;
             out.maxGraphicsPipelineThreads = getMaxGraphicsPipelineThreadsCount();
+            out.maxCpuHeadroomThreads = DEFAULT_MAX_CPU_HEADROOM_THREADS_COUNT;
             out.powerHalVersion = mPowerHalVersion;
             out.supportInfo = mSupportInfo;
             return out;
@@ -1850,23 +1871,40 @@
                     }
                 }
             }
-            pw.println("CPU Headroom Interval: " + mSupportInfo.headroom.cpuMinIntervalMillis);
-            pw.println("GPU Headroom Interval: " + mSupportInfo.headroom.gpuMinIntervalMillis);
-            try {
-                CpuHeadroomParamsInternal params = new CpuHeadroomParamsInternal();
-                params.usesDeviceHeadroom = true;
-                CpuHeadroomResult ret = getCpuHeadroom(params);
-                pw.println("CPU headroom: " + (ret == null ? "N/A" : ret.getGlobalHeadroom()));
-            } catch (Exception e) {
-                Slog.d(TAG, "Failed to dump CPU headroom", e);
-                pw.println("CPU headroom: N/A");
+            pw.println("CPU Headroom Supported: " + mSupportInfo.headroom.isCpuSupported);
+            if (mSupportInfo.headroom.isCpuSupported) {
+                pw.println("CPU Headroom Interval: " + mSupportInfo.headroom.cpuMinIntervalMillis);
+                pw.println("CPU Headroom TID Max Count: " + mCpuHeadroomMaxTidCnt);
+                pw.println("CPU Headroom TID Max Count From HAL: "
+                        + mSupportInfo.headroom.cpuMaxTidCount);
+                pw.println("CPU Headroom Calculation Window Range: ["
+                        + mSupportInfo.headroom.cpuMinCalculationWindowMillis + ", "
+                        + mSupportInfo.headroom.cpuMaxCalculationWindowMillis + "]");
+                try {
+                    CpuHeadroomParamsInternal params = new CpuHeadroomParamsInternal();
+                    params.usesDeviceHeadroom = true;
+                    CpuHeadroomResult ret = getCpuHeadroom(params);
+                    pw.println("CPU headroom: " + (ret == null ? "N/A" : ret.getGlobalHeadroom()));
+                } catch (Exception e) {
+                    Slog.d(TAG, "Failed to dump CPU headroom", e);
+                    pw.println("CPU headroom: N/A");
+                }
             }
-            try {
-                GpuHeadroomResult ret = getGpuHeadroom(new GpuHeadroomParamsInternal());
-                pw.println("GPU headroom: " + (ret == null ? "N/A" : ret.getGlobalHeadroom()));
-            } catch (Exception e) {
-                Slog.d(TAG, "Failed to dump GPU headroom", e);
-                pw.println("GPU headroom: N/A");
+            pw.println("GPU Headroom Supported: " + mSupportInfo.headroom.isGpuSupported);
+            if (mSupportInfo.headroom.isGpuSupported) {
+                pw.println("GPU Headroom Interval: " + mSupportInfo.headroom.gpuMinIntervalMillis);
+                pw.println("GPU Headroom Calculation Window Range: ["
+                        + mSupportInfo.headroom.gpuMinCalculationWindowMillis + ", "
+                        + mSupportInfo.headroom.gpuMaxCalculationWindowMillis + "]");
+                try {
+                    GpuHeadroomParamsInternal params = new GpuHeadroomParamsInternal();
+                    params.calculationWindowMillis = mDefaultGpuHeadroomCalculationWindowMillis;
+                    GpuHeadroomResult ret = getGpuHeadroom(params);
+                    pw.println("GPU headroom: " + (ret == null ? "N/A" : ret.getGlobalHeadroom()));
+                } catch (Exception e) {
+                    Slog.d(TAG, "Failed to dump GPU headroom", e);
+                    pw.println("GPU headroom: N/A");
+                }
             }
         }
 
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 95690cd..9206cce 100644
--- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
@@ -759,34 +759,29 @@
         @Override
         public void handleMessage(Message msg) {
             BatteryCallback cb = mCallback;
+            if (cb == null) {
+                return;
+            }
             switch (msg.what) {
                 case MSG_REPORT_CPU_UPDATE_NEEDED:
-                    if (cb != null) {
-                        cb.batteryNeedsCpuUpdate();
-                    }
+                    cb.batteryNeedsCpuUpdate();
                     break;
                 case MSG_REPORT_POWER_CHANGE:
-                    if (cb != null) {
-                        cb.batteryPowerChanged(msg.arg1 != 0);
-                    }
+                    cb.batteryPowerChanged(msg.arg1 != 0);
                     break;
                 case MSG_REPORT_CHARGING:
-                    if (cb != null) {
-                        final String action;
-                        synchronized (BatteryStatsImpl.this) {
-                            action = mCharging ? BatteryManager.ACTION_CHARGING
-                                    : BatteryManager.ACTION_DISCHARGING;
-                        }
-                        Intent intent = new Intent(action);
-                        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-                        cb.batterySendBroadcast(intent);
+                    final String action;
+                    synchronized (BatteryStatsImpl.this) {
+                        action = mCharging ? BatteryManager.ACTION_CHARGING
+                                : BatteryManager.ACTION_DISCHARGING;
                     }
+                    Intent intent = new Intent(action);
+                    intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+                    cb.batterySendBroadcast(intent);
                     break;
                 case MSG_REPORT_RESET_STATS:
-                    if (cb != null) {
-                        cb.batteryStatsReset();
-                    }
-                }
+                    cb.batteryStatsReset();
+            }
         }
     }
 
@@ -2210,6 +2205,11 @@
                 getWakelockDurationRetriever() {
             return mWakelockDurationRetriever;
         }
+
+        @Override
+        public NetworkStats networkStatsDelta(NetworkStats stats, NetworkStats oldStats) {
+            return BatteryStatsImpl.this.networkStatsDelta(stats, oldStats);
+        }
     }
 
     private final PowerStatsCollectorInjector mPowerStatsCollectorInjector =
@@ -12397,83 +12397,13 @@
         return networkStatsManager.getWifiUidStats();
     }
 
-    static class NetworkStatsDelta {
-        int mUid;
-        int mSet;
-        long mRxBytes;
-        long mRxPackets;
-        long mTxBytes;
-        long mTxPackets;
-
-        public int getUid() {
-            return mUid;
+    @VisibleForTesting
+    protected NetworkStats networkStatsDelta(@NonNull NetworkStats stats,
+            @Nullable NetworkStats oldStats) {
+        if (oldStats == null) {
+            return stats;
         }
-
-
-        public int getSet() {
-            return mSet;
-        }
-
-        public long getRxBytes() {
-            return mRxBytes;
-        }
-
-        public long getRxPackets() {
-            return mRxPackets;
-        }
-
-        public long getTxBytes() {
-            return mTxBytes;
-        }
-
-        public long getTxPackets() {
-            return mTxPackets;
-        }
-
-        @Override
-        public String toString() {
-            return "NetworkStatsDelta{mUid=" + mUid + ", mSet=" + mSet + ", mRxBytes=" + mRxBytes
-                    + ", mRxPackets=" + mRxPackets + ", mTxBytes=" + mTxBytes + ", mTxPackets="
-                    + mTxPackets + '}';
-        }
-    }
-
-    static List<NetworkStatsDelta> computeDelta(NetworkStats currentStats,
-            NetworkStats lastStats) {
-        List<NetworkStatsDelta> deltaList = new ArrayList<>();
-        for (NetworkStats.Entry entry : currentStats) {
-            NetworkStatsDelta delta = new NetworkStatsDelta();
-            delta.mUid = entry.getUid();
-            delta.mSet = entry.getSet();
-            NetworkStats.Entry lastEntry = null;
-            if (lastStats != null) {
-                for (NetworkStats.Entry e : lastStats) {
-                    if (e.getUid() == entry.getUid() && e.getSet() == entry.getSet()
-                            && e.getTag() == entry.getTag()
-                            && e.getMetered() == entry.getMetered()
-                            && e.getRoaming() == entry.getRoaming()
-                            && e.getDefaultNetwork() == entry.getDefaultNetwork()
-                            /*&& Objects.equals(e.getIface(), entry.getIface())*/) {
-                        lastEntry = e;
-                        break;
-                    }
-                }
-            }
-            if (lastEntry != null) {
-                delta.mRxBytes = Math.max(0, entry.getRxBytes() - lastEntry.getRxBytes());
-                delta.mRxPackets = Math.max(0, entry.getRxPackets() - lastEntry.getRxPackets());
-                delta.mTxBytes = Math.max(0, entry.getTxBytes() - lastEntry.getTxBytes());
-                delta.mTxPackets = Math.max(0, entry.getTxPackets() - lastEntry.getTxPackets());
-            } else {
-                delta.mRxBytes = entry.getRxBytes();
-                delta.mRxPackets = entry.getRxPackets();
-                delta.mTxBytes = entry.getTxBytes();
-                delta.mTxPackets = entry.getTxPackets();
-            }
-            deltaList.add(delta);
-        }
-
-        return deltaList;
+        return stats.subtract(oldStats);
     }
 
     /**
@@ -12491,12 +12421,12 @@
             }
         }
 
+        NetworkStats delta;
         // Grab a separate lock to acquire the network stats, which may do I/O.
-        List<NetworkStatsDelta> delta;
         synchronized (mWifiNetworkLock) {
             final NetworkStats latestStats = readWifiNetworkStatsLocked(networkStatsManager);
             if (latestStats != null) {
-                delta = computeDelta(latestStats, mLastWifiNetworkStats);
+                delta = networkStatsDelta(latestStats, mLastWifiNetworkStats);
                 mLastWifiNetworkStats = latestStats;
             } else {
                 delta = null;
@@ -12506,15 +12436,15 @@
     }
 
     private void onWifiPowerStatsRetrieved(WifiActivityEnergyInfo wifiActivityEnergyInfo,
-            List<NetworkStatsDelta> networkStatsDeltas, long elapsedRealtimeMs, long uptimeMs) {
+            NetworkStats networkStatsDelta, long elapsedRealtimeMs, long uptimeMs) {
         // Do not populate consumed energy, because energy attribution is done by
         // WifiPowerStatsProcessor.
-        updateWifiBatteryStats(wifiActivityEnergyInfo, networkStatsDeltas, POWER_DATA_UNAVAILABLE,
+        updateWifiBatteryStats(wifiActivityEnergyInfo, networkStatsDelta, POWER_DATA_UNAVAILABLE,
                 elapsedRealtimeMs, uptimeMs);
     }
 
     private void updateWifiBatteryStats(WifiActivityEnergyInfo info,
-            List<NetworkStatsDelta> delta, long consumedChargeUC, long elapsedRealtimeMs,
+            NetworkStats delta, long consumedChargeUC, long elapsedRealtimeMs,
             long uptimeMs) {
         synchronized (this) {
             if (!mOnBatteryInternal || mIgnoreNextExternalStats) {
@@ -12540,7 +12470,7 @@
             long totalTxPackets = 0;
             long totalRxPackets = 0;
             if (delta != null) {
-                for (NetworkStatsDelta entry : delta) {
+                for (NetworkStats.Entry entry : delta) {
                     if (DEBUG_ENERGY) {
                         Slog.d(TAG, "Wifi uid " + entry.getUid()
                                 + ": delta rx=" + entry.getRxBytes()
@@ -12751,20 +12681,24 @@
 
                 // Distribute the remaining Tx power appropriately between all apps that transmitted
                 // packets.
-                for (int i = 0; i < txPackets.size(); i++) {
-                    final int uid = txPackets.keyAt(i);
-                    final long myTxTimeMs = (txPackets.valueAt(i) * leftOverTxTimeMs)
-                            / totalTxPackets;
-                    txTimesMs.incrementValue(uid, myTxTimeMs);
+                if (totalTxPackets != 0 && leftOverTxTimeMs != 0) {
+                    for (int i = 0; i < txPackets.size(); i++) {
+                        final int uid = txPackets.keyAt(i);
+                        final long myTxTimeMs = (txPackets.valueAt(i) * leftOverTxTimeMs)
+                                / totalTxPackets;
+                        txTimesMs.incrementValue(uid, myTxTimeMs);
+                    }
                 }
 
                 // Distribute the remaining Rx power appropriately between all apps that received
                 // packets.
-                for (int i = 0; i < rxPackets.size(); i++) {
-                    final int uid = rxPackets.keyAt(i);
-                    final long myRxTimeMs = (rxPackets.valueAt(i) * leftOverRxTimeMs)
-                            / totalRxPackets;
-                    rxTimesMs.incrementValue(uid, myRxTimeMs);
+                if (totalRxPackets != 0 && leftOverRxTimeMs != 0) {
+                    for (int i = 0; i < rxPackets.size(); i++) {
+                        final int uid = rxPackets.keyAt(i);
+                        final long myRxTimeMs = (rxPackets.valueAt(i) * leftOverRxTimeMs)
+                                / totalRxPackets;
+                        rxTimesMs.incrementValue(uid, myRxTimeMs);
+                    }
                 }
 
                 for (int i = 0; i < txTimesMs.size(); i++) {
@@ -12880,11 +12814,11 @@
         mLastModemActivityInfo = activityInfo;
 
         // Grab a separate lock to acquire the network stats, which may do I/O.
-        List<NetworkStatsDelta> delta = null;
+        NetworkStats delta = null;
         synchronized (mModemNetworkLock) {
             final NetworkStats latestStats = readMobileNetworkStatsLocked(networkStatsManager);
             if (latestStats != null) {
-                delta = computeDelta(latestStats, mLastModemNetworkStats);
+                delta = networkStatsDelta(latestStats, mLastModemNetworkStats);
                 mLastModemNetworkStats = latestStats;
             }
         }
@@ -12893,15 +12827,15 @@
     }
 
     private void onMobileRadioPowerStatsRetrieved(ModemActivityInfo modemActivityInfo,
-            List<NetworkStatsDelta> networkStatsDeltas, long elapsedRealtimeMs, long uptimeMs) {
+            NetworkStats networkStatsDelta, long elapsedRealtimeMs, long uptimeMs) {
         // Do not populate consumed energy, because energy attribution is done by
         // MobileRadioPowerStatsProcessor.
-        updateCellularBatteryStats(modemActivityInfo, networkStatsDeltas, POWER_DATA_UNAVAILABLE,
+        updateCellularBatteryStats(modemActivityInfo, networkStatsDelta, POWER_DATA_UNAVAILABLE,
                 elapsedRealtimeMs, uptimeMs);
     }
 
     private void updateCellularBatteryStats(@Nullable ModemActivityInfo deltaInfo,
-            @Nullable List<NetworkStatsDelta> delta, long consumedChargeUC, long elapsedRealtimeMs,
+            @Nullable NetworkStats delta, long consumedChargeUC, long elapsedRealtimeMs,
             long uptimeMs) {
         // Add modem tx power to history.
         addModemTxPowerToHistory(deltaInfo, elapsedRealtimeMs, uptimeMs);
@@ -13004,7 +12938,7 @@
             long totalRxPackets = 0;
             long totalTxPackets = 0;
             if (delta != null) {
-                for (NetworkStatsDelta entry : delta) {
+                for (NetworkStats.Entry entry : delta) {
                     if (entry.getRxPackets() == 0 && entry.getTxPackets() == 0) {
                         continue;
                     }
@@ -13045,7 +12979,7 @@
                 // Now distribute proportional blame to the apps that did networking.
                 long totalPackets = totalRxPackets + totalTxPackets;
                 if (totalPackets > 0) {
-                    for (NetworkStatsDelta entry : delta) {
+                    for (NetworkStats.Entry entry : delta) {
                         if (entry.getRxPackets() == 0 && entry.getTxPackets() == 0) {
                             continue;
                         }
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 cbd6fab..f971e2e 100644
--- a/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/MobileRadioPowerStatsCollector.java
@@ -38,7 +38,6 @@
 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.LongSupplier;
@@ -71,7 +70,7 @@
     interface Observer {
         void onMobileRadioPowerStatsRetrieved(
                 @Nullable ModemActivityInfo modemActivityDelta,
-                @Nullable List<BatteryStatsImpl.NetworkStatsDelta> networkStatsDeltas,
+                @Nullable NetworkStats networkStatsDeltas,
                 long elapsedRealtimeMs, long uptimeMs);
     }
 
@@ -86,6 +85,8 @@
         TelephonyManager getTelephonyManager();
         LongSupplier getCallDurationSupplier();
         LongSupplier getPhoneSignalScanDurationSupplier();
+
+        NetworkStats networkStatsDelta(NetworkStats stats, NetworkStats oldStats);
     }
 
     private final Injector mInjector;
@@ -190,7 +191,7 @@
         mPowerStats.uidStats.clear();
 
         ModemActivityInfo modemActivityDelta = collectModemActivityInfo();
-        List<BatteryStatsImpl.NetworkStatsDelta> networkStatsDeltas = collectNetworkStats();
+        NetworkStats networkStatsDeltas = collectNetworkStats();
 
         mConsumedEnergyHelper.collectConsumedEnergy(mPowerStats, mLayout);
 
@@ -288,17 +289,15 @@
         return deltaInfo;
     }
 
-    private List<BatteryStatsImpl.NetworkStatsDelta> collectNetworkStats() {
+    private NetworkStats collectNetworkStats() {
         NetworkStats networkStats = mNetworkStatsSupplier.get();
         if (networkStats == null) {
             return null;
         }
 
-        List<BatteryStatsImpl.NetworkStatsDelta> delta =
-                BatteryStatsImpl.computeDelta(networkStats, mLastNetworkStats);
+        NetworkStats delta = mInjector.networkStatsDelta(networkStats, mLastNetworkStats);
         mLastNetworkStats = networkStats;
-        for (int i = delta.size() - 1; i >= 0; i--) {
-            BatteryStatsImpl.NetworkStatsDelta uidDelta = delta.get(i);
+        for (NetworkStats.Entry uidDelta : delta) {
             long rxBytes = uidDelta.getRxBytes();
             long txBytes = uidDelta.getTxBytes();
             long rxPackets = uidDelta.getRxPackets();
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 d83d355..b688d4b 100644
--- a/services/core/java/com/android/server/power/stats/PowerStatsStore.java
+++ b/services/core/java/com/android/server/power/stats/PowerStatsStore.java
@@ -36,6 +36,7 @@
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.nio.channels.Channel;
 import java.nio.channels.FileChannel;
 import java.nio.channels.FileLock;
 import java.nio.charset.StandardCharsets;
@@ -242,8 +243,10 @@
 
         // Lock the directory from access by other JVMs
         try {
-            mLockFile.getParentFile().mkdirs();
-            mLockFile.createNewFile();
+            if (!mLockFile.exists()) {
+                mLockFile.getParentFile().mkdirs();
+                mLockFile.createNewFile();
+            }
             mJvmLock = FileChannel.open(mLockFile.toPath(), StandardOpenOption.WRITE).lock();
         } catch (IOException e) {
             Slog.e(TAG, "Cannot lock snapshot directory", e);
@@ -252,10 +255,13 @@
 
     private void unlockStoreDirectory() {
         try {
-            mJvmLock.close();
+            Channel channel = mJvmLock.acquiredBy();
+            mJvmLock.release();
+            channel.close();
         } catch (IOException e) {
             Slog.e(TAG, "Cannot unlock snapshot directory", e);
         } finally {
+            mJvmLock = null;
             mFileLock.unlock();
         }
     }
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 1fdeac9..5440bcf 100644
--- a/services/core/java/com/android/server/power/stats/WifiPowerStatsCollector.java
+++ b/services/core/java/com/android/server/power/stats/WifiPowerStatsCollector.java
@@ -31,7 +31,6 @@
 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.Supplier;
@@ -43,7 +42,7 @@
 
     interface Observer {
         void onWifiPowerStatsRetrieved(WifiActivityEnergyInfo info,
-                List<BatteryStatsImpl.NetworkStatsDelta> delta, long elapsedRealtimeMs,
+                NetworkStats delta, long elapsedRealtimeMs,
                 long uptimeMs);
     }
 
@@ -66,6 +65,8 @@
         Supplier<NetworkStats> getWifiNetworkStatsSupplier();
         WifiManager getWifiManager();
         WifiStatsRetriever getWifiStatsRetriever();
+
+        NetworkStats networkStatsDelta(NetworkStats stats, NetworkStats oldStats);
     }
 
     private final Injector mInjector;
@@ -161,7 +162,7 @@
         } else {
             collectWifiActivityStats();
         }
-        List<BatteryStatsImpl.NetworkStatsDelta> networkStatsDeltas = collectNetworkStats();
+        NetworkStats networkStatsDeltas = collectNetworkStats();
         collectWifiScanTime();
 
         mConsumedEnergyHelper.collectConsumedEnergy(mPowerStats, mLayout);
@@ -227,17 +228,15 @@
         mPowerStats.durationMs = duration;
     }
 
-    private List<BatteryStatsImpl.NetworkStatsDelta> collectNetworkStats() {
+    private NetworkStats collectNetworkStats() {
         NetworkStats networkStats = mNetworkStatsSupplier.get();
         if (networkStats == null) {
             return null;
         }
 
-        List<BatteryStatsImpl.NetworkStatsDelta> delta =
-                BatteryStatsImpl.computeDelta(networkStats, mLastNetworkStats);
+        NetworkStats delta = mInjector.networkStatsDelta(networkStats, mLastNetworkStats);
         mLastNetworkStats = networkStats;
-        for (int i = delta.size() - 1; i >= 0; i--) {
-            BatteryStatsImpl.NetworkStatsDelta uidDelta = delta.get(i);
+        for (NetworkStats.Entry uidDelta : delta) {
             long rxBytes = uidDelta.getRxBytes();
             long txBytes = uidDelta.getTxBytes();
             long rxPackets = uidDelta.getRxPackets();
diff --git a/services/core/java/com/android/server/power/stats/flags.aconfig b/services/core/java/com/android/server/power/stats/flags.aconfig
index 5e04881..c8dbbd29 100644
--- a/services/core/java/com/android/server/power/stats/flags.aconfig
+++ b/services/core/java/com/android/server/power/stats/flags.aconfig
@@ -87,3 +87,13 @@
         purpose: PURPOSE_BUGFIX
     }
 }
+
+flag {
+    name: "extended_battery_history_continuous_collection_enabled"
+    namespace: "backstage_power"
+    description: "Disable automatic reset of battery stats history on full charge"
+    bug: "381940953"
+    metadata {
+        purpose: PURPOSE_BUGFIX
+    }
+}
diff --git a/services/core/java/com/android/server/resources/ResourcesManagerShellCommand.java b/services/core/java/com/android/server/resources/ResourcesManagerShellCommand.java
index a75d110..1773971 100644
--- a/services/core/java/com/android/server/resources/ResourcesManagerShellCommand.java
+++ b/services/core/java/com/android/server/resources/ResourcesManagerShellCommand.java
@@ -88,6 +88,5 @@
         out.println("    Print this help text.");
         out.println("  dump <PROCESS>");
         out.println("    Dump the Resources objects in use as well as the history of Resources");
-
     }
 }
diff --git a/services/core/java/com/android/server/rollback/RollbackStore.java b/services/core/java/com/android/server/rollback/RollbackStore.java
index 14539d5..50db1e4 100644
--- a/services/core/java/com/android/server/rollback/RollbackStore.java
+++ b/services/core/java/com/android/server/rollback/RollbackStore.java
@@ -84,8 +84,12 @@
      */
     private static List<Rollback> loadRollbacks(File rollbackDataDir) {
         List<Rollback> rollbacks = new ArrayList<>();
-        rollbackDataDir.mkdirs();
-        for (File rollbackDir : rollbackDataDir.listFiles()) {
+        File[] rollbackDirs = rollbackDataDir.listFiles();
+        if (rollbackDirs == null) {
+            Slog.e(TAG, "Folder doesn't exist: " + rollbackDataDir);
+            return rollbacks;
+        }
+        for (File rollbackDir : rollbackDirs) {
             if (rollbackDir.isDirectory()) {
                 try {
                     rollbacks.add(loadRollback(rollbackDir));
diff --git a/services/core/java/com/android/server/stats/pull/AggregatedMobileDataStatsPuller.java b/services/core/java/com/android/server/stats/pull/AggregatedMobileDataStatsPuller.java
index 2088e41..3831352 100644
--- a/services/core/java/com/android/server/stats/pull/AggregatedMobileDataStatsPuller.java
+++ b/services/core/java/com/android/server/stats/pull/AggregatedMobileDataStatsPuller.java
@@ -142,11 +142,8 @@
     private final RateLimiter mRateLimiter;
 
     AggregatedMobileDataStatsPuller(@NonNull NetworkStatsManager networkStatsManager) {
-        if (DEBUG) {
-            if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
-                Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER,
-                        TAG + "-AggregatedMobileDataStatsPullerInit");
-            }
+        if (DEBUG && Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
+            Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, TAG + "-Init");
         }
 
         mRateLimiter = new RateLimiter(/* window= */ Duration.ofSeconds(1));
@@ -173,10 +170,16 @@
 
     public void noteUidProcessState(int uid, int state, long unusedElapsedRealtime,
                                     long unusedUptime) {
-        mMobileDataStatsHandler.post(
+        if (mRateLimiter.tryAcquire()) {
+            mMobileDataStatsHandler.post(
                 () -> {
                     noteUidProcessStateImpl(uid, state);
                 });
+        } else {
+            synchronized (mLock) {
+                mUidPreviousState.put(uid, state);
+            }
+        }
     }
 
     public int pullDataBytesTransfer(List<StatsEvent> data) {
@@ -209,29 +212,27 @@
     }
 
     private void noteUidProcessStateImpl(int uid, int state) {
-        if (mRateLimiter.tryAcquire()) {
-            // noteUidProcessStateImpl can be called back to back several times while
-            // the updateNetworkStats loops over several stats for multiple uids
-            // and during the first call in a batch of proc state change event it can
-            // contain info for uid with unknown previous state yet which can happen due to a few
-            // reasons:
-            // - app was just started
-            // - app was started before the ActivityManagerService
-            // as result stats would be created with state == ActivityManager.PROCESS_STATE_UNKNOWN
-            if (mNetworkStatsManager != null) {
-                updateNetworkStats(mNetworkStatsManager);
-            } else {
-                Slog.w(TAG, "noteUidProcessStateLocked() can not get mNetworkStatsManager");
-            }
+        // noteUidProcessStateImpl can be called back to back several times while
+        // the updateNetworkStats loops over several stats for multiple uids
+        // and during the first call in a batch of proc state change event it can
+        // contain info for uid with unknown previous state yet which can happen due to a few
+        // reasons:
+        // - app was just started
+        // - app was started before the ActivityManagerService
+        // as result stats would be created with state == ActivityManager.PROCESS_STATE_UNKNOWN
+        if (mNetworkStatsManager != null) {
+            updateNetworkStats(mNetworkStatsManager);
+        } else {
+            Slog.w(TAG, "noteUidProcessStateLocked() can not get mNetworkStatsManager");
         }
-        mUidPreviousState.put(uid, state);
+        synchronized (mLock) {
+            mUidPreviousState.put(uid, state);
+        }
     }
 
     private void updateNetworkStats(NetworkStatsManager networkStatsManager) {
-        if (DEBUG) {
-            if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
-                Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, TAG + "-updateNetworkStats");
-            }
+        if (DEBUG && Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
+            Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, TAG + "-updateNetworkStats");
         }
 
         final NetworkStats latestStats = networkStatsManager.getMobileUidStats();
@@ -256,20 +257,25 @@
     }
 
     private void updateNetworkStatsDelta(NetworkStats delta) {
+        if (DEBUG && Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
+            Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, TAG + "-updateNetworkStatsDelta");
+        }
         synchronized (mLock) {
             for (NetworkStats.Entry entry : delta) {
-                if (entry.getRxPackets() == 0 && entry.getTxPackets() == 0) {
-                    continue;
-                }
-                MobileDataStats stats = getUidStatsForPreviousStateLocked(entry.getUid());
-                if (stats != null) {
-                    stats.addTxBytes(entry.getTxBytes());
-                    stats.addRxBytes(entry.getRxBytes());
-                    stats.addTxPackets(entry.getTxPackets());
-                    stats.addRxPackets(entry.getRxPackets());
+                if (entry.getRxPackets() != 0 || entry.getTxPackets() != 0) {
+                    MobileDataStats stats = getUidStatsForPreviousStateLocked(entry.getUid());
+                    if (stats != null) {
+                        stats.addTxBytes(entry.getTxBytes());
+                        stats.addRxBytes(entry.getRxBytes());
+                        stats.addTxPackets(entry.getTxPackets());
+                        stats.addRxPackets(entry.getRxPackets());
+                    }
                 }
             }
         }
+        if (DEBUG) {
+            Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
+        }
     }
 
     @GuardedBy("mLock")
@@ -298,18 +304,12 @@
     }
 
     private static boolean isEmpty(NetworkStats stats) {
-        long totalRxPackets = 0;
-        long totalTxPackets = 0;
         for (NetworkStats.Entry entry : stats) {
-            if (entry.getRxPackets() == 0 && entry.getTxPackets() == 0) {
-                continue;
+            if (entry.getRxPackets() != 0 || entry.getTxPackets() != 0) {
+                // at least one non empty entry located
+                return false;
             }
-            totalRxPackets += entry.getRxPackets();
-            totalTxPackets += entry.getTxPackets();
-            // at least one non empty entry located
-            break;
         }
-        final long totalPackets = totalRxPackets + totalTxPackets;
-        return totalPackets == 0;
+        return true;
     }
 }
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index 83cb72e..1f162a2 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -113,6 +113,11 @@
 
     void startAssist(Bundle args);
     void onCameraLaunchGestureDetected(int source);
+
+    /**
+     * Notifies SysUI that the wallet launch gesture has been detected.
+     */
+    void onWalletLaunchGestureDetected();
     void setDisableFlags(int displayId, int flags, String cause);
     void toggleSplitScreen();
     void appTransitionFinished(int displayId);
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index c18918f..4ed5f90 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -419,6 +419,17 @@
             }
         }
 
+        @Override
+        public void onWalletLaunchGestureDetected() {
+            IStatusBar bar = mBar;
+            if (bar != null) {
+                try {
+                    bar.onWalletLaunchGestureDetected();
+                } catch (RemoteException e) {
+                }
+            }
+        }
+
         /**
          * Notifies the status bar that a Emergency Action launch gesture has been detected.
          *
diff --git a/services/core/java/com/android/server/timedetector/ServerFlags.java b/services/core/java/com/android/server/timedetector/ServerFlags.java
index 2049a02..b651c7b 100644
--- a/services/core/java/com/android/server/timedetector/ServerFlags.java
+++ b/services/core/java/com/android/server/timedetector/ServerFlags.java
@@ -72,8 +72,12 @@
             KEY_TIME_ZONE_DETECTOR_AUTO_DETECTION_ENABLED_DEFAULT,
             KEY_TIME_ZONE_DETECTOR_TELEPHONY_FALLBACK_SUPPORTED,
             KEY_ENHANCED_METRICS_COLLECTION_ENABLED,
+            KEY_TIME_ZONE_NOTIFICATIONS_SUPPORTED,
+            KEY_TIME_ZONE_NOTIFICATIONS_ENABLED_DEFAULT,
+            KEY_TIME_ZONE_NOTIFICATIONS_TRACKING_SUPPORTED,
+            KEY_TIME_ZONE_MANUAL_CHANGE_TRACKING_SUPPORTED
     })
-    @Target({ ElementType.TYPE_USE, ElementType.TYPE_PARAMETER })
+    @Target({ElementType.TYPE_USE, ElementType.TYPE_PARAMETER})
     @Retention(RetentionPolicy.SOURCE)
     @interface DeviceConfigKey {}
 
@@ -192,6 +196,31 @@
             "enhanced_metrics_collection_enabled";
 
     /**
+     * The key to control support for time zone notifications under certain circumstances.
+     */
+    public static final @DeviceConfigKey String KEY_TIME_ZONE_NOTIFICATIONS_SUPPORTED =
+            "time_zone_notifications_supported";
+
+    /**
+     * The key for the default value used to determine whether time zone notifications is enabled
+     * when the user hasn't explicitly set it yet.
+     */
+    public static final @DeviceConfigKey String KEY_TIME_ZONE_NOTIFICATIONS_ENABLED_DEFAULT =
+            "time_zone_notifications_enabled_default";
+
+    /**
+     * The key to control support for time zone notifications tracking under certain circumstances.
+     */
+    public static final @DeviceConfigKey String KEY_TIME_ZONE_NOTIFICATIONS_TRACKING_SUPPORTED =
+            "time_zone_notifications_tracking_supported";
+
+    /**
+     * The key to control support for time zone manual change tracking under certain circumstances.
+     */
+    public static final @DeviceConfigKey String KEY_TIME_ZONE_MANUAL_CHANGE_TRACKING_SUPPORTED =
+            "time_zone_manual_change_tracking_supported";
+
+    /**
      * The registered listeners and the keys to trigger on. The value is explicitly a HashSet to
      * ensure O(1) lookup performance when working out whether a listener should trigger.
      */
diff --git a/services/core/java/com/android/server/timedetector/ServiceConfigAccessorImpl.java b/services/core/java/com/android/server/timedetector/ServiceConfigAccessorImpl.java
index 3579246..0495f54 100644
--- a/services/core/java/com/android/server/timedetector/ServiceConfigAccessorImpl.java
+++ b/services/core/java/com/android/server/timedetector/ServiceConfigAccessorImpl.java
@@ -286,7 +286,8 @@
         // This check is racey, but the whole settings update process is racey. This check prevents
         // a ConfigurationChangeListener callback triggering due to ContentObserver's still
         // triggering *sometimes* for no-op updates. Because callbacks are async this is necessary
-        // for stable behavior during tests.
+        // for stable behavior during tests. This behavior is copied from
+        // setAutoDetectionEnabledIfRequired and assumed to be the correct way.
         if (getAutoDetectionEnabledSetting() != enabled) {
             Settings.Global.putInt(mCr, Settings.Global.AUTO_TIME, enabled ? 1 : 0);
         }
diff --git a/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java b/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java
index fc659c5..c4c86a42 100644
--- a/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java
+++ b/services/core/java/com/android/server/timezonedetector/ConfigurationInternal.java
@@ -65,6 +65,10 @@
     private final boolean mUserConfigAllowed;
     private final boolean mLocationEnabledSetting;
     private final boolean mGeoDetectionEnabledSetting;
+    private final boolean mNotificationsSupported;
+    private final boolean mNotificationsEnabledSetting;
+    private final boolean mNotificationTrackingSupported;
+    private final boolean mManualChangeTrackingSupported;
 
     private ConfigurationInternal(Builder builder) {
         mTelephonyDetectionSupported = builder.mTelephonyDetectionSupported;
@@ -78,6 +82,10 @@
         mUserConfigAllowed = builder.mUserConfigAllowed;
         mLocationEnabledSetting = builder.mLocationEnabledSetting;
         mGeoDetectionEnabledSetting = builder.mGeoDetectionEnabledSetting;
+        mNotificationsSupported = builder.mNotificationsSupported;
+        mNotificationsEnabledSetting = builder.mNotificationsEnabledSetting;
+        mNotificationTrackingSupported = builder.mNotificationsTrackingSupported;
+        mManualChangeTrackingSupported = builder.mManualChangeTrackingSupported;
     }
 
     /** Returns true if the device supports any form of auto time zone detection. */
@@ -104,6 +112,27 @@
     }
 
     /**
+     * Returns true if the device supports time-related notifications.
+     */
+    public boolean areNotificationsSupported() {
+        return mNotificationsSupported;
+    }
+
+    /**
+     * Returns true if the device supports tracking of time-related notifications.
+     */
+    public boolean isNotificationTrackingSupported() {
+        return areNotificationsSupported() && mNotificationTrackingSupported;
+    }
+
+    /**
+     * Returns true if the device supports tracking of time zone manual changes.
+     */
+    public boolean isManualChangeTrackingSupported() {
+        return mManualChangeTrackingSupported;
+    }
+
+    /**
      * Returns {@code true} if location time zone detection should run when auto time zone detection
      * is enabled on supported devices, even when the user has not enabled the algorithm explicitly
      * in settings. Enabled for internal testing only. See {@link #isGeoDetectionExecutionEnabled()}
@@ -223,6 +252,15 @@
                 && getGeoDetectionRunInBackgroundEnabledSetting();
     }
 
+    /** Returns true if time-related notifications can be shown on this device. */
+    public boolean getNotificationsEnabledBehavior() {
+        return areNotificationsSupported() && getNotificationsEnabledSetting();
+    }
+
+    private boolean getNotificationsEnabledSetting() {
+        return mNotificationsEnabledSetting;
+    }
+
     @NonNull
     public TimeZoneCapabilities asCapabilities(boolean bypassUserPolicyChecks) {
         UserHandle userHandle = UserHandle.of(mUserId);
@@ -283,6 +321,14 @@
         }
         builder.setSetManualTimeZoneCapability(suggestManualTimeZoneCapability);
 
+        final @CapabilityState int configureNotificationsEnabledCapability;
+        if (areNotificationsSupported()) {
+            configureNotificationsEnabledCapability = CAPABILITY_POSSESSED;
+        } else {
+            configureNotificationsEnabledCapability = CAPABILITY_NOT_SUPPORTED;
+        }
+        builder.setConfigureNotificationsEnabledCapability(configureNotificationsEnabledCapability);
+
         return builder.build();
     }
 
@@ -291,6 +337,7 @@
         return new TimeZoneConfiguration.Builder()
                 .setAutoDetectionEnabled(getAutoDetectionEnabledSetting())
                 .setGeoDetectionEnabled(getGeoDetectionEnabledSetting())
+                .setNotificationsEnabled(getNotificationsEnabledSetting())
                 .build();
     }
 
@@ -307,6 +354,9 @@
         if (newConfiguration.hasIsGeoDetectionEnabled()) {
             builder.setGeoDetectionEnabledSetting(newConfiguration.isGeoDetectionEnabled());
         }
+        if (newConfiguration.hasIsNotificationsEnabled()) {
+            builder.setNotificationsEnabledSetting(newConfiguration.areNotificationsEnabled());
+        }
         return builder.build();
     }
 
@@ -328,7 +378,11 @@
                 && mEnhancedMetricsCollectionEnabled == that.mEnhancedMetricsCollectionEnabled
                 && mAutoDetectionEnabledSetting == that.mAutoDetectionEnabledSetting
                 && mLocationEnabledSetting == that.mLocationEnabledSetting
-                && mGeoDetectionEnabledSetting == that.mGeoDetectionEnabledSetting;
+                && mGeoDetectionEnabledSetting == that.mGeoDetectionEnabledSetting
+                && mNotificationsSupported == that.mNotificationsSupported
+                && mNotificationsEnabledSetting == that.mNotificationsEnabledSetting
+                && mNotificationTrackingSupported == that.mNotificationTrackingSupported
+                && mManualChangeTrackingSupported == that.mManualChangeTrackingSupported;
     }
 
     @Override
@@ -336,7 +390,9 @@
         return Objects.hash(mUserId, mUserConfigAllowed, mTelephonyDetectionSupported,
                 mGeoDetectionSupported, mTelephonyFallbackSupported,
                 mGeoDetectionRunInBackgroundEnabled, mEnhancedMetricsCollectionEnabled,
-                mAutoDetectionEnabledSetting, mLocationEnabledSetting, mGeoDetectionEnabledSetting);
+                mAutoDetectionEnabledSetting, mLocationEnabledSetting, mGeoDetectionEnabledSetting,
+                mNotificationsSupported, mNotificationsEnabledSetting,
+                mNotificationTrackingSupported, mManualChangeTrackingSupported);
     }
 
     @Override
@@ -352,6 +408,10 @@
                 + ", mAutoDetectionEnabledSetting=" + mAutoDetectionEnabledSetting
                 + ", mLocationEnabledSetting=" + mLocationEnabledSetting
                 + ", mGeoDetectionEnabledSetting=" + mGeoDetectionEnabledSetting
+                + ", mNotificationsSupported=" + mNotificationsSupported
+                + ", mNotificationsEnabledSetting=" + mNotificationsEnabledSetting
+                + ", mNotificationTrackingSupported=" + mNotificationTrackingSupported
+                + ", mManualChangeTrackingSupported=" + mManualChangeTrackingSupported
                 + '}';
     }
 
@@ -370,6 +430,10 @@
         private boolean mAutoDetectionEnabledSetting;
         private boolean mLocationEnabledSetting;
         private boolean mGeoDetectionEnabledSetting;
+        private boolean mNotificationsSupported;
+        private boolean mNotificationsEnabledSetting;
+        private boolean mNotificationsTrackingSupported;
+        private boolean mManualChangeTrackingSupported;
 
         /**
          * Creates a new Builder.
@@ -390,6 +454,10 @@
             this.mAutoDetectionEnabledSetting = toCopy.mAutoDetectionEnabledSetting;
             this.mLocationEnabledSetting = toCopy.mLocationEnabledSetting;
             this.mGeoDetectionEnabledSetting = toCopy.mGeoDetectionEnabledSetting;
+            this.mNotificationsSupported = toCopy.mNotificationsSupported;
+            this.mNotificationsEnabledSetting = toCopy.mNotificationsEnabledSetting;
+            this.mNotificationsTrackingSupported = toCopy.mNotificationTrackingSupported;
+            this.mManualChangeTrackingSupported = toCopy.mManualChangeTrackingSupported;
         }
 
         /**
@@ -475,6 +543,38 @@
             return this;
         }
 
+        /**
+         * Sets the value of the time notification setting for this user.
+         */
+        public Builder setNotificationsEnabledSetting(boolean enabled) {
+            mNotificationsEnabledSetting = enabled;
+            return this;
+        }
+
+        /**
+         * Sets whether time zone notifications are supported on this device.
+         */
+        public Builder setNotificationsSupported(boolean enabled) {
+            mNotificationsSupported = enabled;
+            return this;
+        }
+
+        /**
+         * Sets whether time zone notification tracking is supported on this device.
+         */
+        public Builder setNotificationsTrackingSupported(boolean supported) {
+            mNotificationsTrackingSupported = supported;
+            return this;
+        }
+
+        /**
+         * Sets whether time zone manual change tracking are supported on this device.
+         */
+        public Builder setManualChangeTrackingSupported(boolean supported) {
+            mManualChangeTrackingSupported = supported;
+            return this;
+        }
+
         /** Returns a new {@link ConfigurationInternal}. */
         @NonNull
         public ConfigurationInternal build() {
diff --git a/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessorImpl.java b/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessorImpl.java
index f1248a3..d809fc6 100644
--- a/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessorImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/ServiceConfigAccessorImpl.java
@@ -68,7 +68,11 @@
             ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_DEFAULT,
             ServerFlags.KEY_LOCATION_TIME_ZONE_DETECTION_SETTING_ENABLED_OVERRIDE,
             ServerFlags.KEY_TIME_ZONE_DETECTOR_AUTO_DETECTION_ENABLED_DEFAULT,
-            ServerFlags.KEY_TIME_ZONE_DETECTOR_TELEPHONY_FALLBACK_SUPPORTED
+            ServerFlags.KEY_TIME_ZONE_DETECTOR_TELEPHONY_FALLBACK_SUPPORTED,
+            ServerFlags.KEY_TIME_ZONE_NOTIFICATIONS_SUPPORTED,
+            ServerFlags.KEY_TIME_ZONE_NOTIFICATIONS_ENABLED_DEFAULT,
+            ServerFlags.KEY_TIME_ZONE_NOTIFICATIONS_TRACKING_SUPPORTED,
+            ServerFlags.KEY_TIME_ZONE_MANUAL_CHANGE_TRACKING_SUPPORTED
     );
 
     /**
@@ -100,11 +104,16 @@
     @Nullable
     private static ServiceConfigAccessor sInstance;
 
-    @NonNull private final Context mContext;
-    @NonNull private final ServerFlags mServerFlags;
-    @NonNull private final ContentResolver mCr;
-    @NonNull private final UserManager mUserManager;
-    @NonNull private final LocationManager mLocationManager;
+    @NonNull
+    private final Context mContext;
+    @NonNull
+    private final ServerFlags mServerFlags;
+    @NonNull
+    private final ContentResolver mCr;
+    @NonNull
+    private final UserManager mUserManager;
+    @NonNull
+    private final LocationManager mLocationManager;
 
     @GuardedBy("this")
     @NonNull
@@ -193,6 +202,9 @@
         contentResolver.registerContentObserver(
                 Settings.Global.getUriFor(Settings.Global.AUTO_TIME_ZONE_EXPLICIT), true,
                 contentObserver);
+        contentResolver.registerContentObserver(
+                Settings.Global.getUriFor(Settings.Global.TIME_ZONE_NOTIFICATIONS), true,
+                contentObserver);
 
         // Add async callbacks for user scoped location settings being changed.
         contentResolver.registerContentObserver(
@@ -331,6 +343,14 @@
                 setGeoDetectionEnabledSettingIfRequired(userId, geoDetectionEnabledSetting);
             }
         }
+
+        if (areNotificationsSupported()) {
+            if (requestedConfigurationUpdates.hasIsNotificationsEnabled()) {
+                setNotificationsEnabledSetting(
+                        requestedConfigurationUpdates.areNotificationsEnabled());
+            }
+            setNotificationsEnabledIfRequired(newConfiguration.areNotificationsEnabled());
+        }
     }
 
     @Override
@@ -348,6 +368,10 @@
                 .setUserConfigAllowed(isUserConfigAllowed(userId))
                 .setLocationEnabledSetting(getLocationEnabledSetting(userId))
                 .setGeoDetectionEnabledSetting(getGeoDetectionEnabledSetting(userId))
+                .setNotificationsSupported(areNotificationsSupported())
+                .setNotificationsEnabledSetting(getNotificationsEnabledSetting())
+                .setNotificationsTrackingSupported(isNotificationTrackingSupported())
+                .setManualChangeTrackingSupported(isManualChangeTrackingSupported())
                 .build();
     }
 
@@ -421,6 +445,49 @@
         }
     }
 
+    private boolean areNotificationsSupported() {
+        return mServerFlags.getBoolean(
+                ServerFlags.KEY_TIME_ZONE_NOTIFICATIONS_SUPPORTED,
+                getConfigBoolean(R.bool.config_enableTimeZoneNotificationsSupported));
+    }
+
+    private boolean isNotificationTrackingSupported() {
+        return mServerFlags.getBoolean(
+                ServerFlags.KEY_TIME_ZONE_NOTIFICATIONS_TRACKING_SUPPORTED,
+                getConfigBoolean(R.bool.config_enableTimeZoneNotificationsTrackingSupported));
+    }
+
+    private boolean isManualChangeTrackingSupported() {
+        return mServerFlags.getBoolean(
+                ServerFlags.KEY_TIME_ZONE_MANUAL_CHANGE_TRACKING_SUPPORTED,
+                getConfigBoolean(R.bool.config_enableTimeZoneManualChangeTrackingSupported));
+    }
+
+    private boolean getNotificationsEnabledSetting() {
+        final boolean notificationsEnabledByDefault = areNotificationsEnabledByDefault();
+        return Settings.Global.getInt(mCr, Settings.Global.TIME_ZONE_NOTIFICATIONS,
+                (notificationsEnabledByDefault ? 1 : 0) /* defaultValue */) != 0;
+    }
+
+    private boolean areNotificationsEnabledByDefault() {
+        return mServerFlags.getBoolean(
+                ServerFlags.KEY_TIME_ZONE_NOTIFICATIONS_ENABLED_DEFAULT, true);
+    }
+
+    private void setNotificationsEnabledSetting(boolean enabled) {
+        Settings.Global.putInt(mCr, Settings.Global.TIME_ZONE_NOTIFICATIONS, enabled ? 1 : 0);
+    }
+
+    private void setNotificationsEnabledIfRequired(boolean enabled) {
+        // This check is racey, but the whole settings update process is racey. This check prevents
+        // a ConfigurationChangeListener callback triggering due to ContentObserver's still
+        // triggering *sometimes* for no-op updates. Because callbacks are async this is necessary
+        // for stable behavior during tests.
+        if (getNotificationsEnabledSetting() != enabled) {
+            Settings.Global.putInt(mCr, Settings.Global.TIME_ZONE_NOTIFICATIONS, enabled ? 1 : 0);
+        }
+    }
+
     @Override
     public void addLocationTimeZoneManagerConfigListener(
             @NonNull StateChangeListener listener) {
@@ -441,8 +508,7 @@
 
     @Override
     public boolean isGeoTimeZoneDetectionFeatureSupportedInConfig() {
-        return mContext.getResources().getBoolean(
-                com.android.internal.R.bool.config_enableGeolocationTimeZoneDetection);
+        return getConfigBoolean(R.bool.config_enableGeolocationTimeZoneDetection);
     }
 
     @Override
@@ -660,8 +726,7 @@
     private boolean isTelephonyFallbackSupported() {
         return mServerFlags.getBoolean(
                 ServerFlags.KEY_TIME_ZONE_DETECTOR_TELEPHONY_FALLBACK_SUPPORTED,
-                getConfigBoolean(
-                        com.android.internal.R.bool.config_supportTelephonyTimeZoneFallback));
+                getConfigBoolean(R.bool.config_supportTelephonyTimeZoneFallback));
     }
 
     private boolean getConfigBoolean(int providerEnabledConfigId) {
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneChangeListener.java b/services/core/java/com/android/server/timezonedetector/TimeZoneChangeListener.java
new file mode 100644
index 0000000..e14326c
--- /dev/null
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneChangeListener.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 com.android.server.timezonedetector;
+
+import android.annotation.CurrentTimeMillisLong;
+import android.annotation.ElapsedRealtimeLong;
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.util.IndentingPrintWriter;
+
+import com.android.server.SystemTimeZone.TimeZoneConfidence;
+import com.android.server.timezonedetector.TimeZoneDetectorStrategy.Origin;
+
+import java.util.Objects;
+
+public interface TimeZoneChangeListener {
+
+    /** Record a time zone change. */
+    void process(TimeZoneChangeEvent event);
+
+    /** Dump internal state. */
+    void dump(IndentingPrintWriter ipw);
+
+    class TimeZoneChangeEvent {
+
+        private final @ElapsedRealtimeLong long mElapsedRealtimeMillis;
+        private final @CurrentTimeMillisLong long mUnixEpochTimeMillis;
+        private final @Origin int mOrigin;
+        private final @UserIdInt int mUserId;
+        private final String mOldZoneId;
+        private final String mNewZoneId;
+        private final @TimeZoneConfidence int mNewConfidence;
+        private final String mCause;
+
+        public TimeZoneChangeEvent(@ElapsedRealtimeLong long elapsedRealtimeMillis,
+                @CurrentTimeMillisLong long unixEpochTimeMillis,
+                @Origin int origin, @UserIdInt int userId, @NonNull String oldZoneId,
+                @NonNull String newZoneId, int newConfidence, @NonNull String cause) {
+            mElapsedRealtimeMillis = elapsedRealtimeMillis;
+            mUnixEpochTimeMillis = unixEpochTimeMillis;
+            mOrigin = origin;
+            mUserId = userId;
+            mOldZoneId = Objects.requireNonNull(oldZoneId);
+            mNewZoneId = Objects.requireNonNull(newZoneId);
+            mNewConfidence = newConfidence;
+            mCause = Objects.requireNonNull(cause);
+        }
+
+        public @ElapsedRealtimeLong long getElapsedRealtimeMillis() {
+            return mElapsedRealtimeMillis;
+        }
+
+        public @CurrentTimeMillisLong long getUnixEpochTimeMillis() {
+            return mUnixEpochTimeMillis;
+        }
+
+        public @Origin int getOrigin() {
+            return mOrigin;
+        }
+
+        /**
+         * The ID of the user that triggered the change.
+         *
+         * <p>If automatic time zone is turned on, the user ID returned is the system's user id.
+         */
+        public @UserIdInt int getUserId() {
+            return mUserId;
+        }
+
+        public String getOldZoneId() {
+            return mOldZoneId;
+        }
+
+        public String getNewZoneId() {
+            return mNewZoneId;
+        }
+
+        @Override
+        public String toString() {
+            return "TimeZoneChangeEvent{"
+                    + "mElapsedRealtimeMillis=" + mElapsedRealtimeMillis
+                    + ", mUnixEpochTimeMillis=" + mUnixEpochTimeMillis
+                    + ", mOrigin=" + mOrigin
+                    + ", mUserId=" + mUserId
+                    + ", mOldZoneId='" + mOldZoneId + '\''
+                    + ", mNewZoneId='" + mNewZoneId + '\''
+                    + ", mNewConfidence=" + mNewConfidence
+                    + ", mCause='" + mCause + '\''
+                    + '}';
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
index d914544..af02ad8 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.annotation.UserIdInt;
 import android.app.time.ITimeZoneDetectorListener;
 import android.app.time.TimeZoneCapabilitiesAndConfig;
@@ -73,6 +74,7 @@
         }
 
         @Override
+        @RequiresPermission("android.permission.INTERACT_ACROSS_USERS_FULL")
         public void onStart() {
             // Obtain / create the shared dependencies.
             Context context = getContext();
@@ -81,7 +83,7 @@
             ServiceConfigAccessor serviceConfigAccessor =
                     ServiceConfigAccessorImpl.getInstance(context);
             TimeZoneDetectorStrategy timeZoneDetectorStrategy =
-                    TimeZoneDetectorStrategyImpl.create(handler, serviceConfigAccessor);
+                    TimeZoneDetectorStrategyImpl.create(context, handler, serviceConfigAccessor);
             DeviceActivityMonitor deviceActivityMonitor =
                     DeviceActivityMonitorImpl.create(context, handler);
 
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
index 37e67c9..8cfbe9d 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategy.java
@@ -15,6 +15,7 @@
  */
 package com.android.server.timezonedetector;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.UserIdInt;
 import android.app.time.TimeZoneCapabilitiesAndConfig;
@@ -24,6 +25,11 @@
 import android.app.timezonedetector.TelephonyTimeZoneSuggestion;
 import android.util.IndentingPrintWriter;
 
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
 /**
  * The interface for the class that is responsible for setting the time zone on a device, used by
  * {@link TimeZoneDetectorService} and {@link TimeZoneDetectorInternal}.
@@ -97,6 +103,22 @@
  * @hide
  */
 public interface TimeZoneDetectorStrategy extends Dumpable {
+    @IntDef({ ORIGIN_UNKNOWN, ORIGIN_MANUAL, ORIGIN_TELEPHONY, ORIGIN_LOCATION })
+    @Retention(RetentionPolicy.SOURCE)
+    @Target({ ElementType.TYPE_USE, ElementType.TYPE_PARAMETER })
+    @interface Origin {}
+
+    /** Used when the origin of the time zone value cannot be inferred. */
+    @Origin int ORIGIN_UNKNOWN = 0;
+
+    /** Used when a time zone value originated from a user / manual settings. */
+    @Origin int ORIGIN_MANUAL = 1;
+
+    /** Used when a time zone value originated from a telephony signal. */
+    @Origin int ORIGIN_TELEPHONY = 2;
+
+    /** Used when a time zone value originated from a location signal. */
+    @Origin int ORIGIN_LOCATION = 3;
 
     /**
      * Adds a listener that will be triggered when something changes that could affect the result
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
index dddb46f..19a28dd 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorStrategyImpl.java
@@ -28,6 +28,7 @@
 import android.annotation.ElapsedRealtimeLong;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
 import android.annotation.UserIdInt;
 import android.app.time.DetectorStatusTypes;
 import android.app.time.LocationTimeZoneAlgorithmStatus;
@@ -39,8 +40,10 @@
 import android.app.time.TimeZoneState;
 import android.app.timezonedetector.ManualTimeZoneSuggestion;
 import android.app.timezonedetector.TelephonyTimeZoneSuggestion;
+import android.content.Context;
 import android.os.Handler;
 import android.os.TimestampedValue;
+import android.os.UserHandle;
 import android.util.IndentingPrintWriter;
 import android.util.Slog;
 
@@ -72,12 +75,14 @@
         /**
          * Returns the device's currently configured time zone. May return an empty string.
          */
-        @NonNull String getDeviceTimeZone();
+        @NonNull
+        String getDeviceTimeZone();
 
         /**
          * Returns the confidence of the device's current time zone.
          */
-        @TimeZoneConfidence int getDeviceTimeZoneConfidence();
+        @TimeZoneConfidence
+        int getDeviceTimeZoneConfidence();
 
         /**
          * Sets the device's time zone, associated confidence, and records a debug log entry.
@@ -115,7 +120,7 @@
     /**
      * The abstract score for an empty or invalid telephony suggestion.
      *
-     * Used to score telephony suggestions where there is no zone.
+     * <p>Used to score telephony suggestions where there is no zone.
      */
     @VisibleForTesting
     public static final int TELEPHONY_SCORE_NONE = 0;
@@ -123,11 +128,11 @@
     /**
      * The abstract score for a low quality telephony suggestion.
      *
-     * Used to score suggestions where:
-     * The suggested zone ID is one of several possibilities, and the possibilities have different
-     * offsets.
+     * <p>Used to score suggestions where:
+     * The suggested zone ID is one of several possibilities,
+     * and the possibilities have different offsets.
      *
-     * You would have to be quite desperate to want to use this choice.
+     * <p>You would have to be quite desperate to want to use this choice.
      */
     @VisibleForTesting
     public static final int TELEPHONY_SCORE_LOW = 1;
@@ -135,7 +140,7 @@
     /**
      * The abstract score for a medium quality telephony suggestion.
      *
-     * Used for:
+     * <p>Used for:
      * The suggested zone ID is one of several possibilities but at least the possibilities have the
      * same offset. Users would get the correct time but for the wrong reason. i.e. their device may
      * switch to DST at the wrong time and (for example) their calendar events.
@@ -146,7 +151,7 @@
     /**
      * The abstract score for a high quality telephony suggestion.
      *
-     * Used for:
+     * <p>Used for:
      * The suggestion was for one zone ID and the answer was unambiguous and likely correct given
      * the info available.
      */
@@ -156,7 +161,7 @@
     /**
      * The abstract score for a highest quality telephony suggestion.
      *
-     * Used for:
+     * <p>Used for:
      * Suggestions that must "win" because they constitute test or emulator zone ID.
      */
     @VisibleForTesting
@@ -206,7 +211,8 @@
     private final ServiceConfigAccessor mServiceConfigAccessor;
 
     @GuardedBy("this")
-    @NonNull private final List<StateChangeListener> mStateChangeListeners = new ArrayList<>();
+    @NonNull
+    private final List<StateChangeListener> mStateChangeListeners = new ArrayList<>();
 
     /**
      * A snapshot of the current detector status. A local copy is cached because it is relatively
@@ -244,8 +250,10 @@
     /**
      * Creates a new instance of {@link TimeZoneDetectorStrategyImpl}.
      */
+    @RequiresPermission("android.permission.INTERACT_ACROSS_USERS_FULL")
     public static TimeZoneDetectorStrategyImpl create(
-            @NonNull Handler handler, @NonNull ServiceConfigAccessor serviceConfigAccessor) {
+            @NonNull Context context, @NonNull Handler handler,
+            @NonNull ServiceConfigAccessor serviceConfigAccessor) {
 
         Environment environment = new EnvironmentImpl(handler);
         return new TimeZoneDetectorStrategyImpl(serviceConfigAccessor, environment);
@@ -468,7 +476,7 @@
         // later disables automatic time zone detection.
         mLatestManualSuggestion.set(suggestion);
 
-        setDeviceTimeZoneIfRequired(timeZoneId, cause);
+        setDeviceTimeZoneIfRequired(timeZoneId, ORIGIN_MANUAL, userId, cause);
         return true;
     }
 
@@ -685,7 +693,7 @@
 
         // GeolocationTimeZoneSuggestion has no measure of quality. We assume all suggestions are
         // reliable.
-        String zoneId;
+        String timeZoneId;
 
         // Introduce bias towards the device's current zone when there are multiple zone suggested.
         String deviceTimeZone = mEnvironment.getDeviceTimeZone();
@@ -694,11 +702,12 @@
                 Slog.d(LOG_TAG,
                         "Geo tz suggestion contains current device time zone. Applying bias.");
             }
-            zoneId = deviceTimeZone;
+            timeZoneId = deviceTimeZone;
         } else {
-            zoneId = zoneIds.get(0);
+            timeZoneId = zoneIds.get(0);
         }
-        setDeviceTimeZoneIfRequired(zoneId, detectionReason);
+        setDeviceTimeZoneIfRequired(timeZoneId, ORIGIN_LOCATION, UserHandle.USER_SYSTEM,
+                detectionReason);
         return true;
     }
 
@@ -779,8 +788,8 @@
 
         // Paranoia: Every suggestion above the SCORE_USAGE_THRESHOLD should have a non-null time
         // zone ID.
-        String zoneId = bestTelephonySuggestion.suggestion.getZoneId();
-        if (zoneId == null) {
+        String timeZoneId = bestTelephonySuggestion.suggestion.getZoneId();
+        if (timeZoneId == null) {
             Slog.w(LOG_TAG, "Empty zone suggestion scored higher than expected. This is an error:"
                     + " bestTelephonySuggestion=" + bestTelephonySuggestion
                     + ", detectionReason=" + detectionReason);
@@ -790,11 +799,12 @@
         String cause = "Found good suggestion:"
                 + " bestTelephonySuggestion=" + bestTelephonySuggestion
                 + ", detectionReason=" + detectionReason;
-        setDeviceTimeZoneIfRequired(zoneId, cause);
+        setDeviceTimeZoneIfRequired(timeZoneId, ORIGIN_TELEPHONY, UserHandle.USER_SYSTEM, cause);
     }
 
     @GuardedBy("this")
-    private void setDeviceTimeZoneIfRequired(@NonNull String newZoneId, @NonNull String cause) {
+    private void setDeviceTimeZoneIfRequired(@NonNull String newZoneId, @Origin int origin,
+            @UserIdInt int userId, @NonNull String cause) {
         String currentZoneId = mEnvironment.getDeviceTimeZone();
         // All manual and automatic suggestions are considered high confidence as low-quality
         // suggestions are not currently passed on.
diff --git a/services/core/java/com/android/server/vibrator/BasicToPwleSegmentAdapter.java b/services/core/java/com/android/server/vibrator/BasicToPwleSegmentAdapter.java
index 54ae047..0b676ff 100644
--- a/services/core/java/com/android/server/vibrator/BasicToPwleSegmentAdapter.java
+++ b/services/core/java/com/android/server/vibrator/BasicToPwleSegmentAdapter.java
@@ -100,6 +100,11 @@
         }
 
         VibratorInfo.FrequencyProfile frequencyProfile = info.getFrequencyProfile();
+        if (frequencyProfile.isEmpty()) {
+            // The frequency profile has an invalid frequency range, so keep the segments unchanged.
+            return repeatIndex;
+        }
+
         float[] frequenciesHz = frequencyProfile.getFrequenciesHz();
         float[] accelerationsGs = frequencyProfile.getOutputAccelerationsGs();
 
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index 89c7a3d..6f308aa 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -1631,7 +1631,7 @@
 
         int positionToLog = APP_COMPAT_STATE_CHANGED__LETTERBOX_POSITION__NOT_LETTERBOXED_POSITION;
         if (isAppCompateStateChangedToLetterboxed(state)) {
-            positionToLog = activity.mAppCompatController.getAppCompatReachabilityOverrides()
+            positionToLog = activity.mAppCompatController.getReachabilityOverrides()
                     .getLetterboxPositionForLogging();
         }
         FrameworkStatsLog.write(FrameworkStatsLog.APP_COMPAT_STATE_CHANGED,
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index e4ad56f..3d53078 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2668,11 +2668,7 @@
         }
         // Only do transfer after transaction has done when starting window exist.
         if (mStartingData != null) {
-            final boolean isWaitingForSyncTransactionCommit =
-                    Flags.removeStartingWindowWaitForMultiTransitions()
-                            ? getSyncTransactionCommitCallbackDepth() > 0
-                            : mStartingData.mWaitForSyncTransactionCommit;
-            if (isWaitingForSyncTransactionCommit) {
+            if (getSyncTransactionCommitCallbackDepth() > 0) {
                 mStartingData.mRemoveAfterTransaction = AFTER_TRANSACTION_COPY_TO_CLIENT;
                 return true;
             }
@@ -2835,21 +2831,12 @@
     }
 
     @Override
-    void waitForSyncTransactionCommit(ArraySet<WindowContainer> wcAwaitingCommit) {
-        super.waitForSyncTransactionCommit(wcAwaitingCommit);
-        if (mStartingData != null) {
-            mStartingData.mWaitForSyncTransactionCommit = true;
-        }
-    }
-
-    @Override
     void onSyncTransactionCommitted(SurfaceControl.Transaction t) {
         super.onSyncTransactionCommitted(t);
         if (mStartingData == null) {
             return;
         }
         final StartingData lastData = mStartingData;
-        lastData.mWaitForSyncTransactionCommit = false;
         if (lastData.mRemoveAfterTransaction == AFTER_TRANSACTION_REMOVE_DIRECTLY) {
             removeStartingWindowAnimation(lastData.mPrepareRemoveAnimation);
         } else if (lastData.mRemoveAfterTransaction == AFTER_TRANSACTION_COPY_TO_CLIENT) {
@@ -2879,12 +2866,7 @@
         final boolean animate;
         final boolean hasImeSurface;
         if (mStartingData != null) {
-            final boolean isWaitingForSyncTransactionCommit =
-                    Flags.removeStartingWindowWaitForMultiTransitions()
-                            ? getSyncTransactionCommitCallbackDepth() > 0
-                            : mStartingData.mWaitForSyncTransactionCommit;
-            if (isWaitingForSyncTransactionCommit
-                    || mSyncState != SYNC_STATE_NONE) {
+            if (getSyncTransactionCommitCallbackDepth() > 0 || mSyncState != SYNC_STATE_NONE) {
                 mStartingData.mRemoveAfterTransaction = AFTER_TRANSACTION_REMOVE_DIRECTLY;
                 mStartingData.mPrepareRemoveAnimation = prepareAnimation;
                 return;
@@ -3227,7 +3209,7 @@
                 true /* forActivity */)) {
             return false;
         }
-        if (mAppCompatController.mAllowRestrictedResizability.getAsBoolean()) {
+        if (mAppCompatController.getResizeOverrides().allowRestrictedResizability()) {
             return false;
         }
         // If the user preference respects aspect ratio, then it becomes non-resizable.
@@ -3258,8 +3240,8 @@
             // The caller will check both application and activity level property.
             return true;
         }
-        return !AppCompatController.allowRestrictedResizability(wms.mContext.getPackageManager(),
-                appInfo.packageName);
+        return !AppCompatResizeOverrides.allowRestrictedResizability(
+                wms.mContext.getPackageManager(), appInfo.packageName);
     }
 
     boolean isResizeable() {
@@ -4279,7 +4261,7 @@
     }
 
     void finishRelaunching() {
-        mAppCompatController.getAppCompatOrientationOverrides()
+        mAppCompatController.getOrientationOverrides()
                 .setRelaunchingAfterRequestedOrientationChanged(false);
         mTaskSupervisor.getActivityMetricsLogger().notifyActivityRelaunched(this);
 
@@ -8240,7 +8222,7 @@
                 mLastReportedConfiguration.getMergedConfiguration())) {
             ensureActivityConfiguration(false /* ignoreVisibility */);
             if (mPendingRelaunchCount > originalRelaunchingCount) {
-                mAppCompatController.getAppCompatOrientationOverrides()
+                mAppCompatController.getOrientationOverrides()
                         .setRelaunchingAfterRequestedOrientationChanged(true);
             }
             if (mTransitionController.inPlayingTransition(this)) {
@@ -8453,8 +8435,8 @@
      */
     @ActivityInfo.SizeChangesSupportMode
     private int supportsSizeChanges() {
-        if (mAppCompatController.getAppCompatResizeOverrides()
-                .shouldOverrideForceNonResizeApp()) {
+        final AppCompatResizeOverrides resizeOverrides = mAppCompatController.getResizeOverrides();
+        if (resizeOverrides.shouldOverrideForceNonResizeApp()) {
             return SIZE_CHANGES_UNSUPPORTED_OVERRIDE;
         }
 
@@ -8462,8 +8444,7 @@
             return SIZE_CHANGES_SUPPORTED_METADATA;
         }
 
-        if (mAppCompatController.getAppCompatResizeOverrides()
-                .shouldOverrideForceResizeApp()) {
+        if (resizeOverrides.shouldOverrideForceResizeApp()) {
             return SIZE_CHANGES_SUPPORTED_OVERRIDE;
         }
 
@@ -8763,7 +8744,7 @@
             navBarInsets = Insets.NONE;
         }
         final AppCompatReachabilityOverrides reachabilityOverrides =
-                mAppCompatController.getAppCompatReachabilityOverrides();
+                mAppCompatController.getReachabilityOverrides();
         // Horizontal position
         int offsetX = 0;
         if (parentBounds.width() != screenResolvedBoundsWidth) {
@@ -10236,10 +10217,10 @@
                 mAppCompatController.getAppCompatAspectRatioOverrides()
                         .shouldOverrideMinAspectRatio());
         proto.write(SHOULD_IGNORE_ORIENTATION_REQUEST_LOOP,
-                mAppCompatController.getAppCompatOrientationOverrides()
+                mAppCompatController.getOrientationOverrides()
                         .shouldIgnoreOrientationRequestLoop());
         proto.write(SHOULD_OVERRIDE_FORCE_RESIZE_APP,
-                mAppCompatController.getAppCompatResizeOverrides().shouldOverrideForceResizeApp());
+                mAppCompatController.getResizeOverrides().shouldOverrideForceResizeApp());
         proto.write(SHOULD_ENABLE_USER_ASPECT_RATIO_SETTINGS,
                 mAppCompatController.getAppCompatAspectRatioOverrides()
                         .shouldEnableUserAspectRatioSettings());
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 2971e8e..9e2c00e 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -5015,7 +5015,7 @@
         return showDialogs;
     }
 
-    private void updateFontScaleIfNeeded(@UserIdInt int userId) {
+    void updateFontScaleIfNeeded(@UserIdInt int userId) {
         if (userId != getCurrentUserId()) {
             return;
         }
@@ -7175,7 +7175,23 @@
         @Override
         public void onHandleAppCrash(@NonNull WindowProcessController wpc) {
             synchronized (mGlobalLock) {
-                wpc.handleAppCrash();
+                final boolean hasVisibleActivity;
+                mTaskSupervisor.beginDeferResume();
+                try {
+                    hasVisibleActivity = wpc.handleAppCrash();
+                } finally {
+                    mTaskSupervisor.endDeferResume();
+                }
+
+                if (hasVisibleActivity) {
+                    deferWindowLayout();
+                    try {
+                        mRootWindowContainer.ensureVisibilityOnVisibleActivityDiedOrCrashed(
+                                "onHandleAppCrash");
+                    } finally {
+                        continueWindowLayout();
+                    }
+                }
             }
         }
 
diff --git a/services/core/java/com/android/server/wm/AppCompatController.java b/services/core/java/com/android/server/wm/AppCompatController.java
index 4433d64..6d0e8ea 100644
--- a/services/core/java/com/android/server/wm/AppCompatController.java
+++ b/services/core/java/com/android/server/wm/AppCompatController.java
@@ -15,23 +15,17 @@
  */
 package com.android.server.wm;
 
-import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY;
-
 import android.annotation.NonNull;
 import android.content.pm.PackageManager;
 
 import com.android.server.wm.utils.OptPropFactory;
 
 import java.io.PrintWriter;
-import java.util.function.BooleanSupplier;
 
 /**
  * Allows the interaction with all the app compat policies and configurations
  */
 class AppCompatController {
-
-    @NonNull
-    private final ActivityRecord mActivityRecord;
     @NonNull
     private final TransparentPolicy mTransparentPolicy;
     @NonNull
@@ -39,7 +33,7 @@
     @NonNull
     private final AppCompatAspectRatioPolicy mAppCompatAspectRatioPolicy;
     @NonNull
-    private final AppCompatReachabilityPolicy mAppCompatReachabilityPolicy;
+    private final AppCompatReachabilityPolicy mReachabilityPolicy;
     @NonNull
     private final DesktopAppCompatAspectRatioPolicy mDesktopAppCompatAspectRatioPolicy;
     @NonNull
@@ -50,56 +44,28 @@
     private final AppCompatLetterboxPolicy mAppCompatLetterboxPolicy;
     @NonNull
     private final AppCompatSizeCompatModePolicy mAppCompatSizeCompatModePolicy;
-    @NonNull
-    final BooleanSupplier mAllowRestrictedResizability;
 
     AppCompatController(@NonNull WindowManagerService wmService,
                         @NonNull ActivityRecord activityRecord) {
-        mActivityRecord = activityRecord;
         final PackageManager packageManager = wmService.mContext.getPackageManager();
         final OptPropFactory optPropBuilder = new OptPropFactory(packageManager,
                 activityRecord.packageName);
         mAppCompatDeviceStateQuery = new AppCompatDeviceStateQuery(activityRecord);
         mTransparentPolicy = new TransparentPolicy(activityRecord,
                 wmService.mAppCompatConfiguration);
-        mAppCompatOverrides = new AppCompatOverrides(activityRecord,
+        mAppCompatOverrides = new AppCompatOverrides(activityRecord, packageManager,
                 wmService.mAppCompatConfiguration, optPropBuilder, mAppCompatDeviceStateQuery);
         mOrientationPolicy = new AppCompatOrientationPolicy(activityRecord, mAppCompatOverrides);
         mAppCompatAspectRatioPolicy = new AppCompatAspectRatioPolicy(activityRecord,
                 mTransparentPolicy, mAppCompatOverrides);
-        mAppCompatReachabilityPolicy = new AppCompatReachabilityPolicy(mActivityRecord,
+        mReachabilityPolicy = new AppCompatReachabilityPolicy(activityRecord,
                 wmService.mAppCompatConfiguration);
-        mAppCompatLetterboxPolicy = new AppCompatLetterboxPolicy(mActivityRecord,
+        mAppCompatLetterboxPolicy = new AppCompatLetterboxPolicy(activityRecord,
                 wmService.mAppCompatConfiguration);
         mDesktopAppCompatAspectRatioPolicy = new DesktopAppCompatAspectRatioPolicy(activityRecord,
                 mAppCompatOverrides, mTransparentPolicy, wmService.mAppCompatConfiguration);
-        mAppCompatSizeCompatModePolicy = new AppCompatSizeCompatModePolicy(mActivityRecord,
+        mAppCompatSizeCompatModePolicy = new AppCompatSizeCompatModePolicy(activityRecord,
                 mAppCompatOverrides);
-        mAllowRestrictedResizability = AppCompatUtils.asLazy(() -> {
-            // Application level.
-            if (allowRestrictedResizability(packageManager, mActivityRecord.packageName)) {
-                return true;
-            }
-            // Activity level.
-            try {
-                return packageManager.getPropertyAsUser(
-                        PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY,
-                        mActivityRecord.mActivityComponent.getPackageName(),
-                        mActivityRecord.mActivityComponent.getClassName(),
-                        mActivityRecord.mUserId).getBoolean();
-            } catch (PackageManager.NameNotFoundException e) {
-                return false;
-            }
-        });
-    }
-
-    static boolean allowRestrictedResizability(PackageManager pm, String packageName) {
-        try {
-            return pm.getProperty(PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY, packageName)
-                    .getBoolean();
-        } catch (PackageManager.NameNotFoundException e) {
-            return false;
-        }
     }
 
     @NonNull
@@ -123,8 +89,8 @@
     }
 
     @NonNull
-    AppCompatOrientationOverrides getAppCompatOrientationOverrides() {
-        return mAppCompatOverrides.getAppCompatOrientationOverrides();
+    AppCompatOrientationOverrides getOrientationOverrides() {
+        return mAppCompatOverrides.getOrientationOverrides();
     }
 
     @NonNull
@@ -138,13 +104,13 @@
     }
 
     @NonNull
-    AppCompatResizeOverrides getAppCompatResizeOverrides() {
-        return mAppCompatOverrides.getAppCompatResizeOverrides();
+    AppCompatResizeOverrides getResizeOverrides() {
+        return mAppCompatOverrides.getResizeOverrides();
     }
 
     @NonNull
-    AppCompatReachabilityPolicy getAppCompatReachabilityPolicy() {
-        return mAppCompatReachabilityPolicy;
+    AppCompatReachabilityPolicy getReachabilityPolicy() {
+        return mReachabilityPolicy;
     }
 
     @NonNull
@@ -158,8 +124,8 @@
     }
 
     @NonNull
-    AppCompatReachabilityOverrides getAppCompatReachabilityOverrides() {
-        return mAppCompatOverrides.getAppCompatReachabilityOverrides();
+    AppCompatReachabilityOverrides getReachabilityOverrides() {
+        return mAppCompatOverrides.getReachabilityOverrides();
     }
 
     @NonNull
diff --git a/services/core/java/com/android/server/wm/AppCompatLetterboxPolicy.java b/services/core/java/com/android/server/wm/AppCompatLetterboxPolicy.java
index e929fb4..4494586 100644
--- a/services/core/java/com/android/server/wm/AppCompatLetterboxPolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatLetterboxPolicy.java
@@ -154,7 +154,7 @@
 
     @VisibleForTesting
     boolean shouldShowLetterboxUi(@NonNull WindowState mainWindow) {
-        if (mActivityRecord.mAppCompatController.getAppCompatOrientationOverrides()
+        if (mActivityRecord.mAppCompatController.getOrientationOverrides()
                 .getIsRelaunchingAfterRequestedOrientationChanged()) {
             return mLastShouldShowLetterboxUi;
         }
@@ -205,7 +205,7 @@
         }
         pw.println(prefix + "  letterboxReason="
                 + AppCompatUtils.getLetterboxReasonString(mActivityRecord, mainWin));
-        mActivityRecord.mAppCompatController.getAppCompatReachabilityPolicy().dump(pw, prefix);
+        mActivityRecord.mAppCompatController.getReachabilityPolicy().dump(pw, prefix);
         final AppCompatLetterboxOverrides letterboxOverride = mActivityRecord.mAppCompatController
                 .getAppCompatLetterboxOverrides();
         pw.println(prefix + "  letterboxBackgroundColor=" + Integer.toHexString(
@@ -276,12 +276,12 @@
                 final AppCompatLetterboxOverrides letterboxOverrides = mActivityRecord
                         .mAppCompatController.getAppCompatLetterboxOverrides();
                 final AppCompatReachabilityPolicy reachabilityPolicy = mActivityRecord
-                        .mAppCompatController.getAppCompatReachabilityPolicy();
+                        .mAppCompatController.getReachabilityPolicy();
                 mLetterbox = new Letterbox(() -> mActivityRecord.makeChildSurface(null),
                         mActivityRecord.mWmService.mTransactionFactory,
                         reachabilityPolicy, letterboxOverrides,
                         this::getLetterboxParentSurface);
-                mActivityRecord.mAppCompatController.getAppCompatReachabilityPolicy()
+                mActivityRecord.mAppCompatController.getReachabilityPolicy()
                         .setLetterboxInnerBoundsSupplier(mLetterbox::getInnerFrame);
             }
             final Point letterboxPosition = new Point();
@@ -291,7 +291,7 @@
             final Rect innerFrame = new Rect();
             calculateLetterboxInnerBounds(mActivityRecord, w, innerFrame);
             mLetterbox.layout(spaceToFill, innerFrame, letterboxPosition);
-            if (mActivityRecord.mAppCompatController.getAppCompatReachabilityOverrides()
+            if (mActivityRecord.mAppCompatController.getReachabilityOverrides()
                     .isDoubleTapEvent()) {
                 // We need to notify Shell that letterbox position has changed.
                 mActivityRecord.getTask().dispatchTaskInfoChangedIfNeeded(true /* force */);
@@ -321,7 +321,7 @@
                 mLetterbox.destroy();
                 mLetterbox = null;
             }
-            mActivityRecord.mAppCompatController.getAppCompatReachabilityPolicy()
+            mActivityRecord.mAppCompatController.getReachabilityPolicy()
                     .setLetterboxInnerBoundsSupplier(null);
         }
 
@@ -415,7 +415,7 @@
             calculateLetterboxPosition(mActivityRecord, mLetterboxPosition);
             calculateLetterboxOuterBounds(mActivityRecord, mOuterBounds);
             calculateLetterboxInnerBounds(mActivityRecord, w, mInnerBounds);
-            mActivityRecord.mAppCompatController.getAppCompatReachabilityPolicy()
+            mActivityRecord.mAppCompatController.getReachabilityPolicy()
                     .setLetterboxInnerBoundsSupplier(() -> mInnerBounds);
         }
 
@@ -438,7 +438,7 @@
             mLetterboxPosition.set(0, 0);
             mInnerBounds.setEmpty();
             mOuterBounds.setEmpty();
-            mActivityRecord.mAppCompatController.getAppCompatReachabilityPolicy()
+            mActivityRecord.mAppCompatController.getReachabilityPolicy()
                     .setLetterboxInnerBoundsSupplier(null);
         }
 
diff --git a/services/core/java/com/android/server/wm/AppCompatOrientationOverrides.java b/services/core/java/com/android/server/wm/AppCompatOrientationOverrides.java
index c84711d..af83668 100644
--- a/services/core/java/com/android/server/wm/AppCompatOrientationOverrides.java
+++ b/services/core/java/com/android/server/wm/AppCompatOrientationOverrides.java
@@ -113,7 +113,7 @@
         // Task to ensure that Activity Embedding is excluded.
         return mActivityRecord.isVisibleRequested() && mActivityRecord.getTaskFragment() != null
                 && mActivityRecord.getTaskFragment().getWindowingMode() == WINDOWING_MODE_FULLSCREEN
-                && mActivityRecord.mAppCompatController.getAppCompatOrientationOverrides()
+                && mActivityRecord.mAppCompatController.getOrientationOverrides()
                     .isOverrideRespectRequestedOrientationEnabled();
     }
 
diff --git a/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java b/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java
index 16e2029..fc758ef 100644
--- a/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatOrientationPolicy.java
@@ -94,7 +94,7 @@
             return SCREEN_ORIENTATION_PORTRAIT;
         }
 
-        if (mAppCompatOverrides.getAppCompatOrientationOverrides()
+        if (mAppCompatOverrides.getOrientationOverrides()
                 .isAllowOrientationOverrideOptOut()) {
             return candidate;
         }
@@ -108,7 +108,7 @@
         }
 
         final AppCompatOrientationOverrides.OrientationOverridesState capabilityState =
-                mAppCompatOverrides.getAppCompatOrientationOverrides()
+                mAppCompatOverrides.getOrientationOverrides()
                         .mOrientationOverridesState;
 
         if (capabilityState.mIsOverrideToReverseLandscapeOrientationEnabled
@@ -170,7 +170,7 @@
     boolean shouldIgnoreRequestedOrientation(
             @ActivityInfo.ScreenOrientation int requestedOrientation) {
         final AppCompatOrientationOverrides orientationOverrides =
-                mAppCompatOverrides.getAppCompatOrientationOverrides();
+                mAppCompatOverrides.getOrientationOverrides();
         if (orientationOverrides.shouldEnableIgnoreOrientationRequest()) {
             if (orientationOverrides.getIsRelaunchingAfterRequestedOrientationChanged()) {
                 Slog.w(TAG, "Ignoring orientation update to "
diff --git a/services/core/java/com/android/server/wm/AppCompatOverrides.java b/services/core/java/com/android/server/wm/AppCompatOverrides.java
index 2f03105..9fb54db 100644
--- a/services/core/java/com/android/server/wm/AppCompatOverrides.java
+++ b/services/core/java/com/android/server/wm/AppCompatOverrides.java
@@ -17,6 +17,7 @@
 package com.android.server.wm;
 
 import android.annotation.NonNull;
+import android.content.pm.PackageManager;
 
 import com.android.server.wm.utils.OptPropFactory;
 
@@ -26,7 +27,7 @@
 public class AppCompatOverrides {
 
     @NonNull
-    private final AppCompatOrientationOverrides mAppCompatOrientationOverrides;
+    private final AppCompatOrientationOverrides mOrientationOverrides;
     @NonNull
     private final AppCompatCameraOverrides mAppCompatCameraOverrides;
     @NonNull
@@ -34,35 +35,37 @@
     @NonNull
     private final AppCompatFocusOverrides mAppCompatFocusOverrides;
     @NonNull
-    private final AppCompatResizeOverrides mAppCompatResizeOverrides;
+    private final AppCompatResizeOverrides mResizeOverrides;
     @NonNull
-    private final AppCompatReachabilityOverrides mAppCompatReachabilityOverrides;
+    private final AppCompatReachabilityOverrides mReachabilityOverrides;
     @NonNull
     private final AppCompatLetterboxOverrides mAppCompatLetterboxOverrides;
 
     AppCompatOverrides(@NonNull ActivityRecord activityRecord,
+            @NonNull PackageManager packageManager,
             @NonNull AppCompatConfiguration appCompatConfiguration,
             @NonNull OptPropFactory optPropBuilder,
             @NonNull AppCompatDeviceStateQuery appCompatDeviceStateQuery) {
         mAppCompatCameraOverrides = new AppCompatCameraOverrides(activityRecord,
                 appCompatConfiguration, optPropBuilder);
-        mAppCompatOrientationOverrides = new AppCompatOrientationOverrides(activityRecord,
+        mOrientationOverrides = new AppCompatOrientationOverrides(activityRecord,
                 appCompatConfiguration, optPropBuilder, mAppCompatCameraOverrides);
-        mAppCompatReachabilityOverrides = new AppCompatReachabilityOverrides(activityRecord,
+        mReachabilityOverrides = new AppCompatReachabilityOverrides(activityRecord,
                 appCompatConfiguration, appCompatDeviceStateQuery);
         mAppCompatAspectRatioOverrides = new AppCompatAspectRatioOverrides(activityRecord,
                 appCompatConfiguration, optPropBuilder, appCompatDeviceStateQuery,
-                mAppCompatReachabilityOverrides);
+                mReachabilityOverrides);
         mAppCompatFocusOverrides = new AppCompatFocusOverrides(activityRecord,
                 appCompatConfiguration, optPropBuilder);
-        mAppCompatResizeOverrides = new AppCompatResizeOverrides(activityRecord, optPropBuilder);
+        mResizeOverrides = new AppCompatResizeOverrides(activityRecord, packageManager,
+                optPropBuilder);
         mAppCompatLetterboxOverrides = new AppCompatLetterboxOverrides(activityRecord,
                 appCompatConfiguration);
     }
 
     @NonNull
-    AppCompatOrientationOverrides getAppCompatOrientationOverrides() {
-        return mAppCompatOrientationOverrides;
+    AppCompatOrientationOverrides getOrientationOverrides() {
+        return mOrientationOverrides;
     }
 
     @NonNull
@@ -81,13 +84,13 @@
     }
 
     @NonNull
-    AppCompatResizeOverrides getAppCompatResizeOverrides() {
-        return mAppCompatResizeOverrides;
+    AppCompatResizeOverrides getResizeOverrides() {
+        return mResizeOverrides;
     }
 
     @NonNull
-    AppCompatReachabilityOverrides getAppCompatReachabilityOverrides() {
-        return mAppCompatReachabilityOverrides;
+    AppCompatReachabilityOverrides getReachabilityOverrides() {
+        return mReachabilityOverrides;
     }
 
     @NonNull
diff --git a/services/core/java/com/android/server/wm/AppCompatReachabilityPolicy.java b/services/core/java/com/android/server/wm/AppCompatReachabilityPolicy.java
index d03a803..087edc1 100644
--- a/services/core/java/com/android/server/wm/AppCompatReachabilityPolicy.java
+++ b/services/core/java/com/android/server/wm/AppCompatReachabilityPolicy.java
@@ -77,7 +77,7 @@
 
     void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
         final AppCompatReachabilityOverrides reachabilityOverrides =
-                mActivityRecord.mAppCompatController.getAppCompatReachabilityOverrides();
+                mActivityRecord.mAppCompatController.getReachabilityOverrides();
         pw.println(prefix + "  isVerticalThinLetterboxed=" + reachabilityOverrides
                 .isVerticalThinLetterboxed());
         pw.println(prefix + "  isHorizontalThinLetterboxed=" + reachabilityOverrides
@@ -96,7 +96,7 @@
 
     private void handleHorizontalDoubleTap(int x) {
         final AppCompatReachabilityOverrides reachabilityOverrides =
-                mActivityRecord.mAppCompatController.getAppCompatReachabilityOverrides();
+                mActivityRecord.mAppCompatController.getReachabilityOverrides();
         if (!reachabilityOverrides.isHorizontalReachabilityEnabled()
                 || mActivityRecord.isInTransition()) {
             return;
@@ -142,7 +142,7 @@
 
     private void handleVerticalDoubleTap(int y) {
         final AppCompatReachabilityOverrides reachabilityOverrides =
-                mActivityRecord.mAppCompatController.getAppCompatReachabilityOverrides();
+                mActivityRecord.mAppCompatController.getReachabilityOverrides();
         if (!reachabilityOverrides.isVerticalReachabilityEnabled()
                 || mActivityRecord.isInTransition()) {
             return;
diff --git a/services/core/java/com/android/server/wm/AppCompatResizeOverrides.java b/services/core/java/com/android/server/wm/AppCompatResizeOverrides.java
index 60c1825..fa53153 100644
--- a/services/core/java/com/android/server/wm/AppCompatResizeOverrides.java
+++ b/services/core/java/com/android/server/wm/AppCompatResizeOverrides.java
@@ -19,13 +19,17 @@
 import static android.content.pm.ActivityInfo.FORCE_NON_RESIZE_APP;
 import static android.content.pm.ActivityInfo.FORCE_RESIZE_APP;
 import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES;
+import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY;
 
 import static com.android.server.wm.AppCompatUtils.isChangeEnabled;
 
 import android.annotation.NonNull;
+import android.content.pm.PackageManager;
 
 import com.android.server.wm.utils.OptPropFactory;
 
+import java.util.function.BooleanSupplier;
+
 /**
  * Encapsulate app compat logic about resizability.
  */
@@ -37,11 +41,40 @@
     @NonNull
     private final OptPropFactory.OptProp mAllowForceResizeOverrideOptProp;
 
+    @NonNull
+    private final BooleanSupplier mAllowRestrictedResizability;
+
     AppCompatResizeOverrides(@NonNull ActivityRecord activityRecord,
+            @NonNull PackageManager packageManager,
             @NonNull OptPropFactory optPropBuilder) {
         mActivityRecord = activityRecord;
         mAllowForceResizeOverrideOptProp = optPropBuilder.create(
                 PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES);
+        mAllowRestrictedResizability = AppCompatUtils.asLazy(() -> {
+            // Application level.
+            if (allowRestrictedResizability(packageManager, mActivityRecord.packageName)) {
+                return true;
+            }
+            // Activity level.
+            try {
+                return packageManager.getPropertyAsUser(
+                        PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY,
+                        mActivityRecord.mActivityComponent.getPackageName(),
+                        mActivityRecord.mActivityComponent.getClassName(),
+                        mActivityRecord.mUserId).getBoolean();
+            } catch (PackageManager.NameNotFoundException e) {
+                return false;
+            }
+        });
+    }
+
+    static boolean allowRestrictedResizability(PackageManager pm, String packageName) {
+        try {
+            return pm.getProperty(PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY, packageName)
+                    .getBoolean();
+        } catch (PackageManager.NameNotFoundException e) {
+            return false;
+        }
     }
 
     /**
@@ -75,4 +108,9 @@
         return mAllowForceResizeOverrideOptProp.shouldEnableWithOptInOverrideAndOptOutProperty(
                 isChangeEnabled(mActivityRecord, FORCE_NON_RESIZE_APP));
     }
+
+    /** @see android.view.WindowManager#PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY */
+    boolean allowRestrictedResizability() {
+        return mAllowRestrictedResizability.getAsBoolean();
+    }
 }
diff --git a/services/core/java/com/android/server/wm/AppCompatUtils.java b/services/core/java/com/android/server/wm/AppCompatUtils.java
index 9f88bc9..e28dddc 100644
--- a/services/core/java/com/android/server/wm/AppCompatUtils.java
+++ b/services/core/java/com/android/server/wm/AppCompatUtils.java
@@ -138,7 +138,7 @@
             return;
         }
         final AppCompatReachabilityOverrides reachabilityOverrides = top.mAppCompatController
-                .getAppCompatReachabilityOverrides();
+                .getReachabilityOverrides();
         final boolean isTopActivityResumed = top.getOrganizedTask() == task && top.isState(RESUMED);
         final boolean isTopActivityVisible = top.getOrganizedTask() == task && top.isVisible();
         // Whether the direct top activity is in size compat mode.
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 492d84f..a972ecb 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -218,11 +218,6 @@
         if (mDisplayContent.mAtmService.mBackNavigationController.isMonitoringFinishTransition()) {
             tmpOpenApps = new ArraySet<>(mDisplayContent.mOpeningApps);
             tmpCloseApps = new ArraySet<>(mDisplayContent.mClosingApps);
-            if (mDisplayContent.mAtmService.mBackNavigationController
-                    .removeIfContainsBackAnimationTargets(tmpOpenApps, tmpCloseApps)) {
-                mDisplayContent.mAtmService.mBackNavigationController
-                        .clearBackAnimations(false /* cancel */);
-            }
         }
 
         @TransitionOldType final int transit = getTransitCompatType(
diff --git a/services/core/java/com/android/server/wm/BLASTSyncEngine.java b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
index dbe0faf..6eda3cf 100644
--- a/services/core/java/com/android/server/wm/BLASTSyncEngine.java
+++ b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
@@ -95,8 +95,17 @@
 
     interface TransactionReadyListener {
         void onTransactionReady(int mSyncId, SurfaceControl.Transaction transaction);
+        default void onTransactionCommitted() {}
         default void onTransactionCommitTimeout() {}
         default void onReadyTimeout() {}
+
+        default void onReadyTraceStart(String name, int id) {
+            Trace.asyncTraceBegin(TRACE_TAG_WINDOW_MANAGER, name, id);
+        }
+
+        default void onReadyTraceEnd(String name, int id) {
+            Trace.asyncTraceEnd(TRACE_TAG_WINDOW_MANAGER, name, id);
+        }
     }
 
     /**
@@ -149,8 +158,8 @@
                 }
             };
             if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
-                mTraceName = name + "SyncGroupReady";
-                Trace.asyncTraceBegin(TRACE_TAG_WINDOW_MANAGER, mTraceName, id);
+                mTraceName = name + "-SyncReady#" + id;
+                listener.onReadyTraceStart(mTraceName, id);
             }
         }
 
@@ -209,29 +218,26 @@
 
         private void finishNow() {
             if (mTraceName != null) {
-                Trace.asyncTraceEnd(TRACE_TAG_WINDOW_MANAGER, mTraceName, mSyncId);
+                mListener.onReadyTraceEnd(mTraceName, mSyncId);
             }
             ProtoLog.v(WM_DEBUG_SYNC_ENGINE, "SyncGroup %d: Finished!", mSyncId);
             SurfaceControl.Transaction merged = mWm.mTransactionFactory.get();
             if (mOrphanTransaction != null) {
                 merged.merge(mOrphanTransaction);
             }
-            for (WindowContainer wc : mRootMembers) {
-                wc.finishSync(merged, this, false /* cancel */);
-            }
 
-            final ArraySet<WindowContainer> wcAwaitingCommit = new ArraySet<>();
-            for (WindowContainer wc : mRootMembers) {
-                wc.waitForSyncTransactionCommit(wcAwaitingCommit);
-            }
-
-            final int syncId = mSyncId;
             final long mergedTxId = merged.getId();
-            final String syncName = mSyncName;
             class CommitCallback implements Runnable {
+                final ArraySet<WindowContainer> mWcAwaitingCommit = new ArraySet<>();
+
                 // Can run a second time if the action completes after the timeout.
                 boolean ran = false;
                 public void onCommitted(SurfaceControl.Transaction t) {
+                    mListener.onTransactionCommitted();
+                    if (mTraceName != null) {
+                        Trace.instant(TRACE_TAG_WINDOW_MANAGER,
+                                mSyncName + "#" + mSyncId + "-committed");
+                    }
                     // Don't wait to hold the global lock to remove the timeout runnable
                     mHandler.removeCallbacks(this);
                     synchronized (mWm.mGlobalLock) {
@@ -239,11 +245,11 @@
                             return;
                         }
                         ran = true;
-                        for (WindowContainer wc : wcAwaitingCommit) {
-                            wc.onSyncTransactionCommitted(t);
+                        for (int i = mWcAwaitingCommit.size() - 1; i >= 0; --i) {
+                            mWcAwaitingCommit.valueAt(i).onSyncTransactionCommitted(t);
                         }
                         t.apply();
-                        wcAwaitingCommit.clear();
+                        mWcAwaitingCommit.clear();
                     }
                 }
 
@@ -254,7 +260,7 @@
                     // a trace. Since these kind of ANRs can trigger such an issue,
                     // try and ensure we will have some visibility in both cases.
                     Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "onTransactionCommitTimeout");
-                    Slog.e(TAG, "WM sent Transaction (#" + syncId + ", " + syncName + ", tx="
+                    Slog.e(TAG, "WM sent Transaction (#" + mSyncId + ", " + mSyncName + ", tx="
                             + mergedTxId + ") to organizer, but never received commit callback."
                             + " Application ANR likely to follow.");
                     Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
@@ -266,6 +272,12 @@
                 }
             };
             CommitCallback callback = new CommitCallback();
+            for (int i = mRootMembers.size() - 1; i >= 0; --i) {
+                final WindowContainer<?> wc = mRootMembers.valueAt(i);
+                wc.finishSync(merged, this, false /* cancel */);
+                wc.waitForSyncTransactionCommit(callback.mWcAwaitingCommit);
+            }
+
             merged.addTransactionCommittedListener(Runnable::run,
                     () -> callback.onCommitted(new SurfaceControl.Transaction()));
             mHandler.postDelayed(callback, BLAST_TIMEOUT_DURATION);
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index 819395a..37575f0 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -22,10 +22,8 @@
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_APP_PROGRESS_GENERATION_ALLOWED;
 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_NONE;
 import static android.view.WindowManager.TRANSIT_PREPARE_BACK_NAVIGATION;
-import static android.view.WindowManager.TRANSIT_TO_BACK;
 import static android.window.SystemOverrideOnBackInvokedCallback.OVERRIDE_FINISH_AND_REMOVE_TASK;
 import static android.window.SystemOverrideOnBackInvokedCallback.OVERRIDE_UNDEFINED;
 
@@ -50,7 +48,6 @@
 import android.os.RemoteException;
 import android.os.SystemProperties;
 import android.text.TextUtils;
-import android.util.ArraySet;
 import android.util.Pair;
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
@@ -657,12 +654,6 @@
                 && displayContent.isFixedRotationLaunchingApp(openActivity);
     }
 
-    private boolean isWaitBackTransition() {
-        // Ignore mWaitTransition while flag is enabled.
-        return mAnimationHandler.mComposed && (Flags.migratePredictiveBackTransition()
-                || mAnimationHandler.mWaitTransition);
-    }
-
     boolean isKeyguardOccluded(WindowState focusWindow) {
         final KeyguardController kc = mWindowManagerService.mAtmService.mKeyguardController;
         final int displayId = focusWindow.getDisplayId();
@@ -700,65 +691,14 @@
         return false;
     }
 
-    // For legacy transition.
-    /**
-     *  Once we find the transition targets match back animation targets, remove the target from
-     *  list, so that transition won't count them in since the close animation was finished.
-     *
-     *  @return {@code true} if the participants of this transition was animated by back gesture
-     *  animations, and shouldn't join next transition.
-     */
-    boolean removeIfContainsBackAnimationTargets(ArraySet<ActivityRecord> openApps,
-            ArraySet<ActivityRecord> closeApps) {
-        if (!isMonitoringFinishTransition()) {
-            return false;
-        }
-        mTmpCloseApps.addAll(closeApps);
-        final boolean matchAnimationTargets = removeIfWaitForBackTransition(openApps, closeApps);
-        if (!matchAnimationTargets) {
-            mNavigationMonitor.onTransitionReadyWhileNavigate(mTmpOpenApps, mTmpCloseApps);
-        }
-        mTmpCloseApps.clear();
-        return matchAnimationTargets;
-    }
-
-    boolean removeIfWaitForBackTransition(ArraySet<ActivityRecord> openApps,
-            ArraySet<ActivityRecord> closeApps) {
-        if (!isWaitBackTransition()) {
-            return false;
-        }
-        // Note: TmpOpenApps is empty. Unlike shell transition, the open apps will be removed from
-        // mOpeningApps if there is no visibility change.
-        if (mAnimationHandler.containsBackAnimationTargets(mTmpOpenApps, mTmpCloseApps)) {
-            // remove close target from close list, open target from open list;
-            // but the open target can be in close list.
-            for (int i = openApps.size() - 1; i >= 0; --i) {
-                final ActivityRecord ar = openApps.valueAt(i);
-                if (mAnimationHandler.isTarget(ar, true /* open */)) {
-                    openApps.removeAt(i);
-                }
-            }
-            for (int i = closeApps.size() - 1; i >= 0; --i) {
-                final ActivityRecord ar = closeApps.valueAt(i);
-                if (mAnimationHandler.isTarget(ar, false /* open */)) {
-                    closeApps.removeAt(i);
-                }
-            }
-            return true;
-        }
-        return false;
-    }
-
     void removePredictiveSurfaceIfNeeded(ActivityRecord openActivity) {
         mAnimationHandler.markWindowHasDrawn(openActivity);
     }
 
     boolean isStartingSurfaceShown(ActivityRecord openActivity) {
-        if (!Flags.migratePredictiveBackTransition()) {
-            return false;
-        }
         return mAnimationHandler.isStartingSurfaceDrawn(openActivity);
     }
+
     @VisibleForTesting
     class NavigationMonitor {
         // The window which triggering the back navigation.
@@ -823,24 +763,6 @@
             mObserver.sendResult(result);
         }
 
-        /**
-         * Notify an unexpected transition has happened during back navigation.
-         */
-        private void onTransitionReadyWhileNavigate(ArrayList<WindowContainer> opening,
-                ArrayList<WindowContainer> closing) {
-            if (!isMonitorForRemote() && !isMonitorAnimationOrTransition()) {
-                return;
-            }
-            final ArrayList<WindowContainer> all = new ArrayList<>(opening);
-            all.addAll(closing);
-            for (int i = all.size() - 1; i >= 0; --i) {
-                if (all.get(i).hasChild(mNavigatingWindow)) {
-                    cancelBackNavigating("transitionHappens");
-                    break;
-                }
-            }
-        }
-
         private boolean atSameDisplay(WindowState newFocus) {
             if (mNavigatingWindow == null) {
                 return false;
@@ -922,8 +844,7 @@
         if (targets.isEmpty()) {
             return;
         }
-        final boolean migratePredictToTransition = Flags.migratePredictiveBackTransition();
-        if (migratePredictToTransition && !mAnimationHandler.mComposed) {
+        if (!mAnimationHandler.mComposed) {
             return;
         } else if (!isMonitoringFinishTransition()) {
             return;
@@ -949,63 +870,40 @@
                 mTmpCloseApps.add(wc);
             }
         }
-        final boolean matchAnimationTargets;
-        if (migratePredictToTransition) {
-            matchAnimationTargets =
-                    mAnimationHandler.containsBackAnimationTargets(mTmpOpenApps, mTmpCloseApps);
-        } else {
-            matchAnimationTargets = isWaitBackTransition()
-                && (transition.mType == TRANSIT_CLOSE || transition.mType == TRANSIT_TO_BACK)
-                && mAnimationHandler.containsBackAnimationTargets(mTmpOpenApps, mTmpCloseApps);
-        }
+        final boolean matchAnimationTargets = mAnimationHandler
+                .containsBackAnimationTargets(mTmpOpenApps, mTmpCloseApps);
         ProtoLog.d(WM_DEBUG_BACK_PREVIEW,
                 "onTransactionReady, opening: %s, closing: %s, animating: %s, match: %b",
                 mTmpOpenApps, mTmpCloseApps, mAnimationHandler, matchAnimationTargets);
         // Don't cancel transition, let transition handler to handle it
-        if (!matchAnimationTargets && !migratePredictToTransition) {
-            mNavigationMonitor.onTransitionReadyWhileNavigate(mTmpOpenApps, mTmpCloseApps);
-        } else {
-            if (mAnimationHandler.mPrepareCloseTransition != null) {
-                Slog.e(TAG, "Gesture animation is applied on another transition?");
-                return;
-            }
-            mAnimationHandler.mPrepareCloseTransition = transition;
-            if (!migratePredictToTransition) {
-                // Because the target will reparent to transition root, so it cannot be controlled
-                // by animation leash. Hide the close target when transition starts.
-                startTransaction.hide(mAnimationHandler.mCloseAdaptor.mTarget.getSurfaceControl());
-            }
-            // Flag target matches and prepare to remove windowless surface.
-            mAnimationHandler.markStartingSurfaceMatch(startTransaction);
-            // release animation leash
-            if (mAnimationHandler.mOpenAnimAdaptor.mCloseTransaction != null) {
-                finishTransaction.merge(mAnimationHandler.mOpenAnimAdaptor.mCloseTransaction);
-                mAnimationHandler.mOpenAnimAdaptor.mCloseTransaction = null;
-            }
+        if (mAnimationHandler.mPrepareCloseTransition != null) {
+            Slog.e(TAG, "Gesture animation is applied on another transition?");
+            return;
+        }
+        mAnimationHandler.mPrepareCloseTransition = transition;
+        // Flag target matches and prepare to remove windowless surface.
+        mAnimationHandler.markStartingSurfaceMatch(startTransaction);
+        // release animation leash
+        if (mAnimationHandler.mOpenAnimAdaptor.mCloseTransaction != null) {
+            finishTransaction.merge(mAnimationHandler.mOpenAnimAdaptor.mCloseTransaction);
+            mAnimationHandler.mOpenAnimAdaptor.mCloseTransaction = null;
         }
         mTmpOpenApps.clear();
         mTmpCloseApps.clear();
     }
 
     boolean isMonitorTransitionTarget(WindowContainer wc) {
-        if (Flags.migratePredictiveBackTransition()) {
-            if (!mAnimationHandler.mComposed) {
-                return false;
-            }
-            if (mAnimationHandler.mSwitchType == AnimationHandler.TASK_SWITCH
-                    && wc.asActivityRecord() != null
-                    || (mAnimationHandler.mSwitchType == AnimationHandler.ACTIVITY_SWITCH
-                    && wc.asTask() != null)) {
-                return false;
-            }
-            return (mAnimationHandler.isTarget(wc, true /* open */)
-                    || mAnimationHandler.isTarget(wc, false /* open */));
-        } else if ((isWaitBackTransition() && mAnimationHandler.mPrepareCloseTransition != null)
-                || (mAnimationHandler.mOpenAnimAdaptor != null
-                && mAnimationHandler.mOpenAnimAdaptor.mPreparedOpenTransition != null)) {
-            return mAnimationHandler.isTarget(wc, wc.isVisibleRequested() /* open */);
+        if (!mAnimationHandler.mComposed) {
+            return false;
         }
-        return false;
+        if (mAnimationHandler.mSwitchType == AnimationHandler.TASK_SWITCH
+                && wc.asActivityRecord() != null
+                || (mAnimationHandler.mSwitchType == AnimationHandler.ACTIVITY_SWITCH
+                && wc.asTask() != null)) {
+            return false;
+        }
+        return (mAnimationHandler.isTarget(wc, true /* open */)
+                || mAnimationHandler.isTarget(wc, false /* open */));
     }
 
     boolean shouldPauseTouch(WindowContainer wc) {
@@ -1163,12 +1061,10 @@
             WindowContainer[] open = builder.mOpenTargets;
             if (isActivitySwitch(close, open)) {
                 mSwitchType = ACTIVITY_SWITCH;
-                if (Flags.migratePredictiveBackTransition()) {
-                    final Pair<WindowContainer, WindowContainer[]> replaced =
-                            promoteToTFIfNeeded(close, open);
-                    close = replaced.first;
-                    open = replaced.second;
-                }
+                final Pair<WindowContainer, WindowContainer[]> replaced =
+                        promoteToTFIfNeeded(close, open);
+                close = replaced.first;
+                open = replaced.second;
             } else if (isTaskSwitch(close, open)) {
                 mSwitchType = TASK_SWITCH;
             } else if (isDialogClose(close)) {
@@ -1988,19 +1884,11 @@
                                 // animation was canceled
                                 return;
                             }
-                            if (Flags.migratePredictiveBackTransition()) {
-                                if (mOpenAnimAdaptor == null
-                                        || mOpenAnimAdaptor.mPreparedOpenTransition == null) {
-                                    // no open nor close transition, this is window animation
-                                    if (!triggerBack) {
-                                        clearBackAnimateTarget(true /* cancel */);
-                                    }
-                                }
-                            } else {
+                            if (mOpenAnimAdaptor == null
+                                    || mOpenAnimAdaptor.mPreparedOpenTransition == null) {
+                                // no open nor close transition, this is window animation
                                 if (!triggerBack) {
                                     clearBackAnimateTarget(true /* cancel */);
-                                } else {
-                                    mWaitTransition = true;
                                 }
                             }
                         }
@@ -2097,8 +1985,6 @@
     }
 
     private static Transition setLaunchBehind(@NonNull ActivityRecord[] activities) {
-        final boolean migrateBackTransition = Flags.migratePredictiveBackTransition();
-        final boolean unifyBackNavigationTransition = Flags.unifyBackNavigationTransition();
         final ArrayList<ActivityRecord> affects = new ArrayList<>();
         for (int i = activities.length - 1; i >= 0; --i) {
             final ActivityRecord activity = activities[i];
@@ -2112,23 +1998,11 @@
         }
 
         final TransitionController tc = activities[0].mTransitionController;
-        final Transition prepareOpen = migrateBackTransition && !unifyBackNavigationTransition
+        final Transition prepareOpen = !Flags.unifyBackNavigationTransition()
                 && !tc.isCollecting() ? tc.createTransition(TRANSIT_PREPARE_BACK_NAVIGATION) : null;
 
-        DisplayContent commonDisplay = null;
         for (int i = affects.size() - 1; i >= 0; --i) {
             final ActivityRecord activity = affects.get(i);
-            if (!migrateBackTransition && !activity.isVisibleRequested()) {
-                // The transition could commit the visibility and in the finishing state, that could
-                // skip commitVisibility call in setVisibility cause the activity won't visible
-                // here.
-                // Call it again to make sure the activity could be visible while handling the
-                // pending animation.
-                // Do not performLayout during prepare animation, because it could cause focus
-                // window change. Let that happen after the BackNavigationInfo has returned to
-                // shell.
-                activity.commitVisibility(true, false /* performLayout */);
-            }
             activity.mTransitionController.mSnapshotController
                     .mActivitySnapshotController.addOnBackPressedActivity(activity);
             activity.mLaunchTaskBehind = true;
@@ -2137,16 +2011,11 @@
                     "Setting Activity.mLauncherTaskBehind to true. Activity=%s", activity);
             activity.mTaskSupervisor.mStoppingActivities.remove(activity);
 
-            if (!migrateBackTransition) {
-                commonDisplay = activity.getDisplayContent();
-            } else if (activity.shouldBeVisible()) {
+            if (activity.shouldBeVisible()) {
                 activity.ensureActivityConfiguration(true /* ignoreVisibility */);
                 activity.makeVisibleIfNeeded(null /* starting */, true /* notifyToClient */);
             }
         }
-        if (commonDisplay != null) {
-            commonDisplay.ensureActivitiesVisible(null /* starting */, true /* notifyClients */);
-        }
         if (prepareOpen != null) {
             if (prepareOpen.hasChanges()) {
                 tc.requestStartTransition(prepareOpen,
@@ -2172,17 +2041,10 @@
                 "Setting Activity.mLauncherTaskBehind to false. Activity=%s",
                 activity);
         if (cancel) {
-            final boolean migrateBackTransition = Flags.migratePredictiveBackTransition();
             // could be visible if transition is canceled due to top activity is finishing.
-            if (migrateBackTransition) {
-                if (finishTransition && !activity.shouldBeVisible()) {
-                    activity.commitVisibility(false /* visible */, false /* performLayout */,
-                            true /* fromTransition */);
-                }
-            } else {
-                // Restore the launch-behind state
-                // TODO b/347168362 Change status directly during collecting for a transition.
-                activity.mTaskSupervisor.scheduleLaunchTaskBehindComplete(activity.token);
+            if (finishTransition && !activity.shouldBeVisible()) {
+                activity.commitVisibility(false /* visible */, false /* performLayout */,
+                        true /* fromTransition */);
             }
             // Ignore all change
             activity.mTransitionController.mSnapshotController
@@ -2205,9 +2067,6 @@
 
     /** 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;
     }
@@ -2248,17 +2107,6 @@
                 mPendingAnimationBuilder = null;
             }
         }
-        if (result.getBoolean(BackNavigationInfo.KEY_GESTURE_FINISHED)) {
-            synchronized (mWindowManagerService.mGlobalLock) {
-                final AnimationHandler ah = mAnimationHandler;
-                if (!ah.mComposed || ah.mWaitTransition || ah.mOpenActivities == null
-                        || (ah.mSwitchType != AnimationHandler.TASK_SWITCH
-                        && ah.mSwitchType != AnimationHandler.ACTIVITY_SWITCH)) {
-                    return;
-                }
-                setLaunchBehind(mAnimationHandler.mOpenActivities);
-            }
-        }
     }
 
     static TaskSnapshot getSnapshot(@NonNull WindowContainer w,
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
index 66b77b9..b9febb83 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java
@@ -50,6 +50,7 @@
 import static com.android.window.flags.Flags.balRequireOptInByPendingIntentCreator;
 import static com.android.window.flags.Flags.balShowToastsBlocked;
 import static com.android.window.flags.Flags.balStrictModeRo;
+import static com.android.window.flags.Flags.balStrictModeGracePeriod;
 
 import static java.lang.annotation.RetentionPolicy.SOURCE;
 import static java.util.Objects.requireNonNull;
@@ -516,7 +517,9 @@
                 return !callerExplicitOptOut();
             }
             return mCheckedOptions.getPendingIntentCreatorBackgroundActivityStartMode()
-                    == MODE_BACKGROUND_ACTIVITY_START_ALLOWED;
+                    != MODE_BACKGROUND_ACTIVITY_START_DENIED
+                    && mCheckedOptions.getPendingIntentCreatorBackgroundActivityStartMode()
+                    != MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED;
         }
 
         public boolean realCallerExplicitOptInOrAutoOptIn() {
@@ -524,7 +527,9 @@
                 return !realCallerExplicitOptOut();
             }
             return mCheckedOptions.getPendingIntentBackgroundActivityStartMode()
-                    == MODE_BACKGROUND_ACTIVITY_START_ALLOWED;
+                    != MODE_BACKGROUND_ACTIVITY_START_DENIED
+                    && mCheckedOptions.getPendingIntentBackgroundActivityStartMode()
+                    != MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED;
         }
 
         public boolean callerExplicitOptOut() {
@@ -573,7 +578,10 @@
             sb.append("; balAllowedByPiCreator: ").append(mBalAllowedByPiCreator);
             sb.append("; balAllowedByPiCreatorWithHardening: ")
                     .append(mBalAllowedByPiCreatorWithHardening);
-            sb.append("; resultIfPiCreatorAllowsBal: ").append(mResultForCaller);
+            if (mResultForCaller != null) {
+                sb.append("; resultIfPiCreatorAllowsBal: ")
+                        .append(balCodeToString(mResultForCaller.mCode));
+            }
             sb.append("; callerStartMode: ").append(balStartModeToString(
                     mCheckedOptions.getPendingIntentCreatorBackgroundActivityStartMode()));
             sb.append("; hasRealCaller: ").append(hasRealCaller());
@@ -602,7 +610,10 @@
                             .append(mRealCallerApp.hasActivityInVisibleTask());
                 }
                 sb.append("; balAllowedByPiSender: ").append(mBalAllowedByPiSender);
-                sb.append("; resultIfPiSenderAllowsBal: ").append(mResultForRealCaller);
+                if (mResultForRealCaller != null) {
+                    sb.append("; resultIfPiSenderAllowsBal: ")
+                            .append(balCodeToString(mResultForRealCaller.mCode));
+                }
                 sb.append("; realCallerStartMode: ").append(balStartModeToString(
                         mCheckedOptions.getPendingIntentBackgroundActivityStartMode()));
             }
@@ -1908,7 +1919,14 @@
                             (state.mOriginatingPendingIntent != null));
         }
 
-        logIfOnlyAllowedBy(finalVerdict, state, BAL_ALLOW_GRACE_PERIOD);
+        if (logIfOnlyAllowedBy(finalVerdict, state, BAL_ALLOW_GRACE_PERIOD)) {
+            if (balStrictModeRo() && balStrictModeGracePeriod()) {
+                String abortDebugMessage = "Activity start is only allowed by grace period. "
+                        + "This may stop working in the future. "
+                        + "intent: " + state.mIntent;
+                strictModeLaunchAborted(state.mRealCallingUid, abortDebugMessage);
+            }
+        }
         logIfOnlyAllowedBy(finalVerdict, state, BAL_ALLOW_NON_APP_VISIBLE_WINDOW);
 
         if (balImprovedMetrics()) {
@@ -1952,24 +1970,29 @@
      * Logs details about the activity starts if the only reason it is allowed is the provided
      * {@code balCode}.
      */
-    private static void logIfOnlyAllowedBy(BalVerdict finalVerdict, BalState state, int balCode) {
+    private static boolean logIfOnlyAllowedBy(BalVerdict finalVerdict, BalState state,
+            int balCode) {
         if (finalVerdict.getRawCode() == balCode) {
             if (state.realCallerExplicitOptInOrAutoOptIn()
                     && state.mResultForRealCaller != null
                     && state.mResultForRealCaller.allows()
                     && state.mResultForRealCaller.getRawCode() != balCode) {
                 // real caller could allow with a different exemption
+                return false;
             } else if (state.callerExplicitOptInOrAutoOptIn()
                     && state.mResultForCaller != null
                     && state.mResultForCaller.allows()
                     && state.mResultForCaller.getRawCode() != balCode) {
                 // caller could allow with a different exemption
+                return false;
             } else {
                 // log to determine grace period length distribution
                 Slog.wtf(TAG, "Activity start ONLY allowed by " + balCodeToString(balCode) + " "
                         + finalVerdict.mMessage + ": " + state);
+                return true;
             }
         }
+        return false;
     }
 
     @VisibleForTesting
diff --git a/services/core/java/com/android/server/wm/ContentRecorder.java b/services/core/java/com/android/server/wm/ContentRecorder.java
index a4e58ef..d6ae651 100644
--- a/services/core/java/com/android/server/wm/ContentRecorder.java
+++ b/services/core/java/com/android/server/wm/ContentRecorder.java
@@ -108,9 +108,7 @@
 
     ContentRecorder(@NonNull DisplayContent displayContent) {
         this(displayContent, new RemoteMediaProjectionManagerWrapper(displayContent.mDisplayId),
-                new DisplayManagerFlags().isConnectedDisplayManagementEnabled()
-                        && !new DisplayManagerFlags()
-                                    .isPixelAnisotropyCorrectionInLogicalDisplayEnabled()
+                !new DisplayManagerFlags().isPixelAnisotropyCorrectionInLogicalDisplayEnabled()
                         && displayContent.getDisplayInfo().type == Display.TYPE_EXTERNAL);
     }
 
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index f40d636..b932ef3 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -264,7 +264,7 @@
         // that should be respected, Check all activities in display to make sure any eligible
         // activity should be respected.
         final ActivityRecord activity = mDisplayContent.getActivity((r) ->
-                r.mAppCompatController.getAppCompatOrientationOverrides()
+                r.mAppCompatController.getOrientationOverrides()
                     .shouldRespectRequestedOrientationDueToOverride());
         return activity != null;
     }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 5fe1ceb..dd23f57 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -2964,7 +2964,7 @@
         if (!handlesOrientationChangeFromDescendant(orientation)) {
             ActivityRecord topActivity = topRunningActivity(/* considerKeyguardState= */ true);
             if (topActivity != null && topActivity.mAppCompatController
-                    .getAppCompatOrientationOverrides()
+                    .getOrientationOverrides()
                         .shouldUseDisplayLandscapeNaturalOrientation()) {
                 ProtoLog.v(WM_DEBUG_ORIENTATION,
                         "Display id=%d is ignoring orientation request for %d, return %d"
@@ -7084,9 +7084,22 @@
 
     class RemoteInsetsControlTarget implements InsetsControlTarget {
         private final IDisplayWindowInsetsController mRemoteInsetsController;
-        private @InsetsType int mRequestedVisibleTypes = WindowInsets.Type.defaultVisible();
         private final boolean mCanShowTransient;
 
+        /** The actual requested visible inset types for this display */
+        private @InsetsType int mRequestedVisibleTypes = WindowInsets.Type.defaultVisible();
+
+        /** The component name of the top focused window on this display */
+        private ComponentName mTopFocusedComponentName = null;
+
+        /**
+         * The inset types that the top focused window is currently requesting to be visible.
+         * This may be different than the actual visible types above depending on the remote
+         * insets controller implementation.
+         */
+        private @InsetsType int mTopFocusedRequestedVisibleTypes =
+                WindowInsets.Type.defaultVisible();
+
         RemoteInsetsControlTarget(IDisplayWindowInsetsController controller) {
             mRemoteInsetsController = controller;
             mCanShowTransient = mWmService.mContext.getResources().getBoolean(
@@ -7096,11 +7109,17 @@
         /**
          * Notifies the remote insets controller that the top focused window has changed.
          *
-         * @param component The application component that is open in the top focussed window.
+         * @param component The application component that is open in the top focused window.
          * @param requestedVisibleTypes The insets types requested visible by the focused window.
          */
         void topFocusedWindowChanged(ComponentName component,
                 @InsetsType int requestedVisibleTypes) {
+            if (mTopFocusedComponentName != null && mTopFocusedComponentName.equals(component)
+                    && mTopFocusedRequestedVisibleTypes == requestedVisibleTypes) {
+                return;
+            }
+            mTopFocusedComponentName = component;
+            mTopFocusedRequestedVisibleTypes = requestedVisibleTypes;
             try {
                 mRemoteInsetsController.topFocusedWindowChanged(component, requestedVisibleTypes);
             } catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index cba606c..54ae80c 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -101,7 +101,10 @@
             // isLeashReadyForDispatching (used to dispatch the leash of the control) is
             // depending on mGivenInsetsReady. Therefore, triggering notifyControlChanged here
             // again, so that the control with leash can be eventually dispatched
-            if (!mGivenInsetsReady && isServerVisible() && !givenInsetsPending) {
+            if (!mGivenInsetsReady && isServerVisible() && !givenInsetsPending
+                    && mControlTarget != null) {
+                ProtoLog.d(WM_DEBUG_IME,
+                        "onPostLayout: IME control ready to be dispatched, ws=%s", ws);
                 mGivenInsetsReady = true;
                 ImeTracker.forLogging().onProgress(mStatsToken,
                         ImeTracker.PHASE_WM_POST_LAYOUT_NOTIFY_CONTROLS_CHANGED);
@@ -117,6 +120,8 @@
                         ImeTracker.PHASE_WM_POST_LAYOUT_NOTIFY_CONTROLS_CHANGED);
                 mStatsToken = null;
             } else if (wasServerVisible && !isServerVisible()) {
+                ProtoLog.d(WM_DEBUG_IME, "onPostLayout: setImeShowing(false) was: %s, ws=%s",
+                        isImeShowing(), ws);
                 setImeShowing(false);
             }
         }
@@ -620,6 +625,7 @@
             // request (cancelling the initial show) or hide request (aborting the initial show).
             logIsScheduledAndReadyToShowIme(!visible /* aborted */);
         }
+        ProtoLog.d(WM_DEBUG_IME, "receiveImeStatsToken: visible=%s", visible);
         if (visible) {
             ImeTracker.forLogging().onCancelled(
                     mStatsToken, ImeTracker.PHASE_WM_ABORT_SHOW_IME_POST_LAYOUT);
diff --git a/services/core/java/com/android/server/wm/InputConfigAdapter.java b/services/core/java/com/android/server/wm/InputConfigAdapter.java
index ae6e724..e3ffe71 100644
--- a/services/core/java/com/android/server/wm/InputConfigAdapter.java
+++ b/services/core/java/com/android/server/wm/InputConfigAdapter.java
@@ -76,9 +76,6 @@
                     LayoutParams.FLAG_NOT_TOUCHABLE,
                     InputConfig.NOT_TOUCHABLE, false /* inverted */),
             new FlagMapping(
-                    LayoutParams.FLAG_SPLIT_TOUCH,
-                    InputConfig.PREVENT_SPLITTING, true /* inverted */),
-            new FlagMapping(
                     LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
                     InputConfig.WATCH_OUTSIDE_TOUCH, false /* inverted */),
             new FlagMapping(
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 7276007..d1585d0 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -384,7 +384,7 @@
         }
         final boolean serverVisibleChanged = mServerVisible != isServerVisible;
         setServerVisible(isServerVisible);
-        if (mControl != null) {
+        if (mControl != null && mControlTarget != null) {
             final boolean positionChanged = updateInsetsControlPosition(windowState);
             if (!(positionChanged || mHasPendingPosition)
                     // The insets hint would be updated while changing the position. Here updates it
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index ce85184..9df65f6 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -371,7 +371,7 @@
         array.add(provider);
     }
 
-    void notifyControlChanged(InsetsControlTarget target, InsetsSourceProvider provider) {
+    void notifyControlChanged(@NonNull InsetsControlTarget target, InsetsSourceProvider provider) {
         addToPendingControlMaps(target, provider);
         notifyPendingInsetsControlChanged();
     }
diff --git a/services/core/java/com/android/server/wm/PinnedTaskController.java b/services/core/java/com/android/server/wm/PinnedTaskController.java
index 755d4c8..6dd7d35 100644
--- a/services/core/java/com/android/server/wm/PinnedTaskController.java
+++ b/services/core/java/com/android/server/wm/PinnedTaskController.java
@@ -348,12 +348,14 @@
      * Notifies listeners that the PIP needs to be adjusted for the IME.
      */
     private void notifyImeVisibilityChanged(boolean imeVisible, int imeHeight) {
-        if (mPinnedTaskListener != null) {
-            try {
-                mPinnedTaskListener.onImeVisibilityChanged(imeVisible, imeHeight);
-            } catch (RemoteException e) {
-                Slog.e(TAG_WM, "Error delivering bounds changed event.", e);
-            }
+        if (mPinnedTaskListener == null) {
+            return;
+        }
+
+        try {
+            mPinnedTaskListener.onImeVisibilityChanged(imeVisible, imeHeight);
+        } catch (RemoteException e) {
+            Slog.e(TAG_WM, "Error delivering ime visibility changed event.", e);
         }
     }
 
@@ -361,15 +363,14 @@
      * Notifies listeners that the PIP movement bounds have changed.
      */
     private void notifyMovementBoundsChanged(boolean fromImeAdjustment) {
-        synchronized (mService.mGlobalLock) {
-            if (mPinnedTaskListener == null) {
-                return;
-            }
-            try {
-                mPinnedTaskListener.onMovementBoundsChanged(fromImeAdjustment);
-            } catch (RemoteException e) {
-                Slog.e(TAG_WM, "Error delivering actions changed event.", e);
-            }
+        if (mPinnedTaskListener == null) {
+            return;
+        }
+
+        try {
+            mPinnedTaskListener.onMovementBoundsChanged(fromImeAdjustment);
+        } catch (RemoteException e) {
+            Slog.e(TAG_WM, "Error delivering movement bounds changed event.", e);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/StartingData.java b/services/core/java/com/android/server/wm/StartingData.java
index 896612d..7349224 100644
--- a/services/core/java/com/android/server/wm/StartingData.java
+++ b/services/core/java/com/android/server/wm/StartingData.java
@@ -63,18 +63,6 @@
 
     /**
      * For Shell transition.
-     * There will be a transition happen on attached activity, do not remove starting window during
-     * this period, because the transaction to show app window may not apply before remove starting
-     * window.
-     * Note this isn't equal to transition playing, the period should be
-     * Sync finishNow -> Start transaction apply.
-     * @deprecated TODO(b/362347290): cleanup after fix ramp up
-     */
-    @Deprecated
-    boolean mWaitForSyncTransactionCommit;
-
-    /**
-     * For Shell transition.
      * This starting window should be removed after applying the start transaction of transition,
      * which ensures the app window has shown.
      */
@@ -114,7 +102,6 @@
     public String toString() {
         return getClass().getSimpleName() + "{"
                 + Integer.toHexString(System.identityHashCode(this))
-                + " waitForSyncTransactionCommit=" + mWaitForSyncTransactionCommit
                 + " removeAfterTransaction= " + mRemoveAfterTransaction
                 + "}";
     }
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 3634bc9..d962b6b 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -845,7 +845,7 @@
      */
     void positionTaskBehindHome(Task task) {
         final Task home = getOrCreateRootHomeTask();
-        final WindowContainer homeParent = home.getParent();
+        final WindowContainer homeParent = home != null ? home.getParent() : null;
         final Task homeParentTask = homeParent != null ? homeParent.asTask() : null;
         if (homeParentTask == null) {
             // reparent throws if parent didn't change...
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 367adc3..a031aca 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -1339,14 +1339,24 @@
                     mTmpRect.set(getBounds());
                     for (int j = adjacentTaskFragments.size() - 1; j >= 0; --j) {
                         final TaskFragment taskFragment = adjacentTaskFragments.get(j);
-                        final TaskFragment adjacentTaskFragment =
-                                taskFragment.mAdjacentTaskFragment;
-                        if (adjacentTaskFragment == this) {
+                        if (taskFragment.isAdjacentTo(this)) {
                             continue;
                         }
-                        if (mTmpRect.intersect(taskFragment.getBounds())
-                                || mTmpRect.intersect(adjacentTaskFragment.getBounds())) {
-                            return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+                        if (Flags.allowMultipleAdjacentTaskFragments()) {
+                            final boolean isOccluding = mTmpRect.intersect(taskFragment.getBounds())
+                                    || taskFragment.forOtherAdjacentTaskFragments(adjacentTf -> {
+                                        return mTmpRect.intersect(adjacentTf.getBounds());
+                                    });
+                            if (isOccluding) {
+                                return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+                            }
+                        } else {
+                            final TaskFragment adjacentTaskFragment =
+                                    taskFragment.mAdjacentTaskFragment;
+                            if (mTmpRect.intersect(taskFragment.getBounds())
+                                    || mTmpRect.intersect(adjacentTaskFragment.getBounds())) {
+                                return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+                            }
                         }
                     }
                 }
@@ -1374,20 +1384,38 @@
             }
 
             final TaskFragment otherTaskFrag = other.asTaskFragment();
-            if (otherTaskFrag != null && otherTaskFrag.mAdjacentTaskFragment != null) {
-                if (adjacentTaskFragments.contains(otherTaskFrag.mAdjacentTaskFragment)) {
-                    if (otherTaskFrag.isTranslucent(starting)
-                            || otherTaskFrag.mAdjacentTaskFragment.isTranslucent(starting)) {
-                        // Can be visible behind a translucent adjacent TaskFragments.
-                        gotTranslucentFullscreen = true;
-                        gotTranslucentAdjacent = true;
-                        continue;
+            if (otherTaskFrag != null && otherTaskFrag.hasAdjacentTaskFragment()) {
+                if (Flags.allowMultipleAdjacentTaskFragments()) {
+                    final boolean hasTraversedAdj = otherTaskFrag.forOtherAdjacentTaskFragments(
+                            adjacentTaskFragments::contains);
+                    if (hasTraversedAdj) {
+                        final boolean isTranslucent = otherTaskFrag.isTranslucent(starting)
+                                || otherTaskFrag.forOtherAdjacentTaskFragments(adjacentTf -> {
+                                    return adjacentTf.isTranslucent(starting);
+                                });
+                        if (isTranslucent) {
+                            // Can be visible behind a translucent adjacent TaskFragments.
+                            gotTranslucentFullscreen = true;
+                            gotTranslucentAdjacent = true;
+                            continue;
+                        }
+                        // Can not be visible behind adjacent TaskFragments.
+                        return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
                     }
-                    // Can not be visible behind adjacent TaskFragments.
-                    return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
                 } else {
-                    adjacentTaskFragments.add(otherTaskFrag);
+                    if (adjacentTaskFragments.contains(otherTaskFrag.mAdjacentTaskFragment)) {
+                        if (otherTaskFrag.isTranslucent(starting)
+                                || otherTaskFrag.mAdjacentTaskFragment.isTranslucent(starting)) {
+                            // Can be visible behind a translucent adjacent TaskFragments.
+                            gotTranslucentFullscreen = true;
+                            gotTranslucentAdjacent = true;
+                            continue;
+                        }
+                        // Can not be visible behind adjacent TaskFragments.
+                        return TASK_FRAGMENT_VISIBILITY_INVISIBLE;
+                    }
                 }
+                adjacentTaskFragments.add(otherTaskFrag);
             }
 
         }
@@ -2725,7 +2753,12 @@
         if (!forceUpdate && width == mLastSurfaceSize.x && height == mLastSurfaceSize.y) {
             return;
         }
-        t.setWindowCrop(mSurfaceControl, width, height);
+        if (fillsParent()) {
+            // Rely on parent's crop.
+            t.setCrop(mSurfaceControl, null);
+        } else {
+            t.setWindowCrop(mSurfaceControl, width, height);
+        }
         mLastSurfaceSize.set(width, height);
     }
 
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index a3d71db..a964678 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -137,9 +137,6 @@
     private static final String TAG = "Transition";
     private static final String TRACE_NAME_PLAY_TRANSITION = "playing";
 
-    /** The default package for resources */
-    private static final String DEFAULT_PACKAGE = "android";
-
     /** The transition has been created but isn't collecting yet. */
     private static final int STATE_PENDING = -1;
 
@@ -1415,6 +1412,7 @@
                         if (!tr.isAttached() || !tr.isVisibleRequested()
                                 || !tr.inPinnedWindowingMode()) return;
                         final ActivityRecord currTop = tr.getTopNonFinishingActivity();
+                        if (currTop == null) return;
                         if (currTop.inPinnedWindowingMode()) return;
                         Slog.e(TAG, "Enter-PIP was started but not completed, this is a Shell/SysUI"
                                 + " bug. This state breaks gesture-nav, so attempting clean-up.");
@@ -1980,7 +1978,7 @@
         } else {
             // No player registered or it's not enabled, so just finish/apply immediately
             if (!mIsPlayerEnabled) {
-                mLogger.mSendTimeNs = SystemClock.uptimeNanos();
+                mLogger.mSendTimeNs = SystemClock.elapsedRealtimeNanos();
                 ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS,
                         "Apply and finish immediately because player is disabled "
                                 + "for transition #%d .", mSyncId);
@@ -2083,6 +2081,12 @@
         }
     }
 
+    // Note that this method is not called in WM lock.
+    @Override
+    public void onTransactionCommitted() {
+        mLogger.mTransactionCommitTimeNs = SystemClock.elapsedRealtimeNanos();
+    }
+
     @Override
     public void onTransactionCommitTimeout() {
         if (mCleanupTransaction == null) return;
@@ -3405,6 +3409,16 @@
         Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_WINDOW_MANAGER, TAG, cookie);
     }
 
+    @Override
+    public void onReadyTraceStart(String name, int id) {
+        asyncTraceBegin(name, id);
+    }
+
+    @Override
+    public void onReadyTraceEnd(String name, int id) {
+        asyncTraceEnd(id);
+    }
+
     boolean hasChanged(WindowContainer wc) {
         final ChangeInfo chg = mChanges.get(wc);
         if (chg == null) return false;
@@ -3984,7 +3998,7 @@
         /** @return true if all tracked subtrees are ready. */
         boolean allReady() {
             ProtoLog.v(WmProtoLogGroups.WM_DEBUG_WINDOW_TRANSITIONS,
-                    " allReady query: used=%b " + "override=%b defer=%d states=[%s]", mUsed,
+                    " allReady query: used=%b override=%b defer=%d states=[%s]", mUsed,
                     mReadyOverride, mDeferReadyDepth, groupsToString());
             // If the readiness has never been touched, mUsed will be false. We never want to
             // consider a transition ready if nothing has been reported on it.
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index f3c03cb..ff9e5a2 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -774,7 +774,7 @@
                     "Disabling player for transition #%d because display isn't enabled yet",
                     transition.getSyncId());
             transition.mIsPlayerEnabled = false;
-            transition.mLogger.mRequestTimeNs = SystemClock.uptimeNanos();
+            transition.mLogger.mRequestTimeNs = SystemClock.elapsedRealtimeNanos();
             mAtm.mH.post(() -> mAtm.mWindowOrganizerController.startTransition(
                     transition.getToken(), null));
             return transition;
@@ -1694,6 +1694,7 @@
         long mStartTimeNs;
         long mReadyTimeNs;
         long mSendTimeNs;
+        long mTransactionCommitTimeNs;
         long mFinishTimeNs;
         long mAbortTimeNs;
         TransitionRequestInfo mRequest;
@@ -1746,6 +1747,9 @@
             sb.append(" started=").append(toMsString(mStartTimeNs - mCreateTimeNs));
             sb.append(" ready=").append(toMsString(mReadyTimeNs - mCreateTimeNs));
             sb.append(" sent=").append(toMsString(mSendTimeNs - mCreateTimeNs));
+            if (mTransactionCommitTimeNs != 0) {
+                sb.append(" commit=").append(toMsString(mTransactionCommitTimeNs - mSendTimeNs));
+            }
             sb.append(" finished=").append(toMsString(mFinishTimeNs - mCreateTimeNs));
             return sb.toString();
         }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 965b224..92e0931 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -313,6 +313,7 @@
 import android.window.ActivityWindowInfo;
 import android.window.AddToSurfaceSyncGroupResult;
 import android.window.ClientWindowFrames;
+import android.window.ConfigurationChangeSetting;
 import android.window.IGlobalDragListener;
 import android.window.IScreenRecordingCallback;
 import android.window.ISurfaceSyncGroupCompletedListener;
@@ -1457,6 +1458,12 @@
             }
         }, mTransactionFactory);
         mSystemPerformanceHinter.mTraceTag = TRACE_TAG_WINDOW_MANAGER;
+
+        if (Flags.condenseConfigurationChangeForSimpleMode()) {
+            LocalServices.addService(
+                    ConfigurationChangeSetting.ConfigurationChangeSettingInternal.class,
+                    new ConfigurationChangeSettingInternalImpl());
+        }
     }
 
     DisplayAreaPolicy.Provider getDisplayAreaPolicyProvider() {
@@ -6187,21 +6194,32 @@
         final long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
-                final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
-                if (displayContent != null) {
-                    displayContent.setForcedDensity(density, targetUserId);
-                } else {
-                    DisplayInfo info = mDisplayManagerInternal.getDisplayInfo(displayId);
-                    if (info != null) {
-                        mDisplayWindowSettings.setForcedDensity(info, density, userId);
-                    }
-                }
+                setForcedDensityLockedInternal(displayId, density, targetUserId);
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
     }
 
+    @GuardedBy("mGlobalLock")
+    private void setForcedDensityLockedInternal(final int displayId, final int density,
+            @UserIdInt final int targetUserId) {
+        final DisplayContent displayContent = mRoot.getDisplayContent(displayId);
+        if (displayContent != null) {
+            displayContent.setForcedDensity(density, targetUserId);
+            return;
+        }
+
+        final DisplayInfo info = mDisplayManagerInternal.getDisplayInfo(displayId);
+        if (info == null) {
+            ProtoLog.e(WM_ERROR, "Failed to get information about logical display %d. "
+                    + "Skip setting forced display density.", displayId);
+            return;
+        }
+
+        mDisplayWindowSettings.setForcedDensity(info, density, targetUserId);
+    }
+
     @EnforcePermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
     @Override
     public void clearForcedDisplayDensityForUser(int displayId, int userId) {
@@ -6217,12 +6235,51 @@
                 if (displayContent != null) {
                     displayContent.setForcedDensity(displayContent.getInitialDisplayDensity(),
                             callingUserId);
-                } else {
-                    DisplayInfo info = mDisplayManagerInternal.getDisplayInfo(displayId);
-                    if (info != null) {
-                        mDisplayWindowSettings.setForcedDensity(info, info.logicalDensityDpi,
-                                userId);
+                    return;
+                }
+
+                final DisplayInfo info = mDisplayManagerInternal.getDisplayInfo(displayId);
+                if (info == null) {
+                    ProtoLog.e(WM_ERROR, "Failed to get information about logical display %d. "
+                            + "Skip clearing forced display density.", displayId);
+                    return;
+                }
+
+                mDisplayWindowSettings.setForcedDensity(info, info.logicalDensityDpi,
+                        userId);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    @EnforcePermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
+    @Override
+    public void setConfigurationChangeSettingsForUser(
+            @NonNull List<ConfigurationChangeSetting> settings, int userId) {
+        setConfigurationChangeSettingsForUser_enforcePermission();
+        if (!Flags.condenseConfigurationChangeForSimpleMode()) {
+            throw new IllegalStateException(
+                    "setConfigurationChangeSettingsForUser shouldn't be called when "
+                            + "condenseConfigurationChangeForSimpleMode is disabled, "
+                            + "please enable the flag.");
+        }
+
+        final int callingUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(),
+                Binder.getCallingUid(), userId, false, true,
+                "setConfigurationChangeSettingsForUser",
+                null);
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                mAtmService.deferWindowLayout();
+                try {
+                    // Apply the settings.
+                    for (int i = 0; i < settings.size(); i++) {
+                        settings.get(i).apply(callingUserId);
                     }
+                } finally {
+                    mAtmService.continueWindowLayout();
                 }
             }
         } finally {
@@ -10120,9 +10177,10 @@
             throw new SecurityException("Access denied to process: " + pid
                     + ", must have permission " + Manifest.permission.ACCESS_FPS_COUNTER);
         }
-
-        if (mRoot.anyTaskForId(taskId) == null) {
-            throw new IllegalArgumentException("no task with taskId: " + taskId);
+        synchronized (mGlobalLock) {
+            if (mRoot.anyTaskForId(taskId) == null) {
+                throw new IllegalArgumentException("no task with taskId: " + taskId);
+            }
         }
 
         mTaskFpsCallbackController.registerListener(taskId, callback);
@@ -10540,4 +10598,48 @@
             }
         });
     }
+
+    private final class ConfigurationChangeSettingInternalImpl implements
+            ConfigurationChangeSetting.ConfigurationChangeSettingInternal {
+        @NonNull
+        @Override
+        public ConfigurationChangeSetting createImplFromParcel(
+                @ConfigurationChangeSetting.SettingType int settingType, @NonNull Parcel in) {
+            switch (settingType) {
+                case ConfigurationChangeSetting.SETTING_TYPE_DISPLAY_DENSITY:
+                    return new DensitySettingImpl(in);
+                case ConfigurationChangeSetting.SETTING_TYPE_FONT_SCALE:
+                    return new FontScaleSettingImpl(in);
+                default:
+                    throw new IllegalArgumentException("Unknown setting type " + settingType);
+            }
+        }
+
+        private final class DensitySettingImpl extends ConfigurationChangeSetting.DensitySetting {
+            private DensitySettingImpl(Parcel in) {
+                super(in);
+            }
+
+            @Override
+            @GuardedBy("mGlobalLock")
+            public void apply(@UserIdInt int userId) {
+                setForcedDensityLockedInternal(mDisplayId, mDensity, userId);
+            }
+        }
+
+        private final class FontScaleSettingImpl extends
+                ConfigurationChangeSetting.FontScaleSetting {
+            private FontScaleSettingImpl(Parcel in) {
+                super(in);
+            }
+
+            @Override
+            @GuardedBy("mGlobalLock")
+            public void apply(@UserIdInt int userId) {
+                Settings.System.putFloat(mContext.getContentResolver(),
+                        Settings.System.FONT_SCALE, mFontScaleFactor);
+                mAtmService.updateFontScaleIfNeeded(userId);
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index e45ada9..f0f1b2e 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -595,7 +595,10 @@
                 }
                 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()) {
+                if (effects == TRANSACT_EFFECTS_NONE && transition.mParticipants.isEmpty()
+                        // Always send the remote transition even if it is no-op because the remote
+                        // handler may still want to handle it.
+                        && remoteTransition == null) {
                     transition.abort();
                     return;
                 }
@@ -2661,10 +2664,19 @@
 
     private int deleteTaskFragment(@NonNull TaskFragment taskFragment,
             @Nullable Transition transition) {
-        if (transition != null) transition.collectExistenceChange(taskFragment);
+        final boolean isEmpty = taskFragment.getNonFinishingActivityCount() == 0;
+        if (transition != null && (taskFragment.isVisibleRequested()
+                // In case to update existing change type.
+                || transition.mChanges.containsKey(taskFragment))) {
+            transition.collectExistenceChange(taskFragment);
+        }
 
         mLaunchTaskFragments.remove(taskFragment.getFragmentToken());
         taskFragment.remove(true /* withTransition */, "deleteTaskFragment");
+        if (isEmpty) {
+            // The removal of an empty TaskFragment doesn't affect lifecycle.
+            return 0;
+        }
         return TRANSACT_EFFECTS_LIFECYCLE;
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 80e4c30..26bc09f 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -465,17 +465,22 @@
         mCrashing = crashing;
     }
 
-    void handleAppCrash() {
+    boolean handleAppCrash() {
+        boolean hasVisibleActivity = false;
         ArrayList<ActivityRecord> activities = new ArrayList<>(mActivities);
         for (int i = activities.size() - 1; i >= 0; --i) {
             final ActivityRecord r = activities.get(i);
             Slog.w(TAG, "  Force finishing activity "
                     + r.mActivityComponent.flattenToShortString());
             r.detachFromProcess();
-            r.mDisplayContent.requestTransitionAndLegacyPrepare(TRANSIT_CLOSE,
-                    TRANSIT_FLAG_APP_CRASHED);
+            if (r.isVisibleRequested()) {
+                hasVisibleActivity = true;
+                r.mDisplayContent.requestTransitionAndLegacyPrepare(TRANSIT_CLOSE,
+                        TRANSIT_FLAG_APP_CRASHED);
+            }
             r.destroyIfPossible("handleAppCrashed");
         }
+        return hasVisibleActivity;
     }
 
     boolean isCrashing() {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index b43e334..3a2a1ea 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -5424,7 +5424,7 @@
             // change then delay the position update until it has redrawn to avoid any flickers.
             final boolean isLetterboxedAndRelaunching = activityRecord != null
                     && activityRecord.areBoundsLetterboxed()
-                    && activityRecord.mAppCompatController.getAppCompatOrientationOverrides()
+                    && activityRecord.mAppCompatController.getOrientationOverrides()
                         .getIsRelaunchingAfterRequestedOrientationChanged();
             if (surfaceResizedWithoutMoveAnimation || isLetterboxedAndRelaunching) {
                 applyWithNextDraw(mSetSurfacePositionConsumer);
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 813fec1..911c686 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 <dispatcher/Entry.h>
 #include <include/gestures.h>
 #include <input/Input.h>
 #include <input/PointerController.h>
@@ -66,6 +67,7 @@
 #include <atomic>
 #include <cinttypes>
 #include <map>
+#include <variant>
 #include <vector>
 
 #include "android_hardware_display_DisplayTopology.h"
@@ -341,10 +343,12 @@
     void setPointerDisplayId(ui::LogicalDisplayId displayId);
     int32_t getMousePointerSpeed();
     void setPointerSpeed(int32_t speed);
-    void setMousePointerAccelerationEnabled(ui::LogicalDisplayId displayId, bool enabled);
+    void setMouseScalingEnabled(ui::LogicalDisplayId displayId, bool enabled);
     void setMouseReverseVerticalScrollingEnabled(bool enabled);
     void setMouseScrollingAccelerationEnabled(bool enabled);
+    void setMouseScrollingSpeed(int32_t speed);
     void setMouseSwapPrimaryButtonEnabled(bool enabled);
+    void setMouseAccelerationEnabled(bool enabled);
     void setTouchpadPointerSpeed(int32_t speed);
     void setTouchpadNaturalScrollingEnabled(bool enabled);
     void setTouchpadTapToClickEnabled(bool enabled);
@@ -415,8 +419,9 @@
     void interceptMotionBeforeQueueing(ui::LogicalDisplayId displayId, uint32_t source,
                                        int32_t action, nsecs_t when,
                                        uint32_t& policyFlags) override;
-    nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>& token, const KeyEvent& keyEvent,
-                                          uint32_t policyFlags) override;
+    std::variant<nsecs_t, inputdispatcher::KeyEntry::InterceptKeyResult>
+    interceptKeyBeforeDispatching(const sp<IBinder>& token, const KeyEvent& keyEvent,
+                                  uint32_t policyFlags) override;
     std::optional<KeyEvent> dispatchUnhandledKey(const sp<IBinder>& token, const KeyEvent& keyEvent,
                                                  uint32_t policyFlags) override;
     void pokeUserActivity(nsecs_t eventTime, int32_t eventType,
@@ -469,8 +474,8 @@
         // Pointer speed.
         int32_t pointerSpeed{0};
 
-        // Displays on which its associated mice will have pointer acceleration disabled.
-        std::set<ui::LogicalDisplayId> displaysWithMousePointerAccelerationDisabled{};
+        // Displays on which its associated mice will have all scaling disabled.
+        std::set<ui::LogicalDisplayId> displaysWithMouseScalingDisabled{};
 
         // True if pointer gestures are enabled.
         bool pointerGesturesEnabled{true};
@@ -496,12 +501,18 @@
         // True if mouse scrolling acceleration is enabled.
         bool mouseScrollingAccelerationEnabled{true};
 
+        // The mouse scrolling speed, as a number from -7 (slowest) to 7 (fastest).
+        int32_t mouseScrollingSpeed{0};
+
         // True if mouse vertical scrolling is reversed.
         bool mouseReverseVerticalScrollingEnabled{false};
 
         // True if the mouse primary button is swapped (left/right buttons).
         bool mouseSwapPrimaryButtonEnabled{false};
 
+        // True if the mouse cursor will accelerate as the mouse moves faster.
+        bool mousePointerAccelerationEnabled{true};
+
         // The touchpad pointer speed, as a number from -7 (slowest) to 7 (fastest).
         int32_t touchpadPointerSpeed{0};
 
@@ -592,9 +603,8 @@
         dump += StringPrintf(INDENT "System UI Lights Out: %s\n",
                              toString(mLocked.systemUiLightsOut));
         dump += StringPrintf(INDENT "Pointer Speed: %" PRId32 "\n", mLocked.pointerSpeed);
-        dump += StringPrintf(INDENT "Display with Mouse Pointer Acceleration Disabled: %s\n",
-                             dumpSet(mLocked.displaysWithMousePointerAccelerationDisabled,
-                                     streamableToString)
+        dump += StringPrintf(INDENT "Display with Mouse Scaling Disabled: %s\n",
+                             dumpSet(mLocked.displaysWithMouseScalingDisabled, streamableToString)
                                      .c_str());
         dump += StringPrintf(INDENT "Pointer Gestures Enabled: %s\n",
                              toString(mLocked.pointerGesturesEnabled));
@@ -823,19 +833,20 @@
         std::scoped_lock _l(mLock);
 
         outConfig->mousePointerSpeed = mLocked.pointerSpeed;
-        outConfig->displaysWithMousePointerAccelerationDisabled =
-                mLocked.displaysWithMousePointerAccelerationDisabled;
+        outConfig->displaysWithMouseScalingDisabled = mLocked.displaysWithMouseScalingDisabled;
         outConfig->pointerVelocityControlParameters.scale =
                 exp2f(mLocked.pointerSpeed * POINTER_SPEED_EXPONENT);
         outConfig->pointerVelocityControlParameters.acceleration =
-                mLocked.displaysWithMousePointerAccelerationDisabled.count(
-                        mLocked.pointerDisplayId) == 0
+                mLocked.displaysWithMouseScalingDisabled.count(mLocked.pointerDisplayId) == 0
                 ? android::os::IInputConstants::DEFAULT_POINTER_ACCELERATION
                 : 1;
         outConfig->wheelVelocityControlParameters.acceleration =
                 mLocked.mouseScrollingAccelerationEnabled
                 ? android::os::IInputConstants::DEFAULT_MOUSE_WHEEL_ACCELERATION
                 : 1;
+        outConfig->wheelVelocityControlParameters.scale = mLocked.mouseScrollingAccelerationEnabled
+                ? 1
+                : exp2f(mLocked.mouseScrollingSpeed * POINTER_SPEED_EXPONENT);
         outConfig->pointerGesturesEnabled = mLocked.pointerGesturesEnabled;
 
         outConfig->pointerCaptureRequest = mLocked.pointerCaptureRequest;
@@ -847,6 +858,7 @@
         outConfig->mouseReverseVerticalScrollingEnabled =
                 mLocked.mouseReverseVerticalScrollingEnabled;
         outConfig->mouseSwapPrimaryButtonEnabled = mLocked.mouseSwapPrimaryButtonEnabled;
+        outConfig->mousePointerAccelerationEnabled = mLocked.mousePointerAccelerationEnabled;
 
         outConfig->touchpadPointerSpeed = mLocked.touchpadPointerSpeed;
         outConfig->touchpadNaturalScrollingEnabled = mLocked.touchpadNaturalScrollingEnabled;
@@ -1443,6 +1455,21 @@
             InputReaderConfiguration::Change::POINTER_SPEED);
 }
 
+void NativeInputManager::setMouseScrollingSpeed(int32_t speed) {
+    { // acquire lock
+        std::scoped_lock _l(mLock);
+
+        if (mLocked.mouseScrollingSpeed == speed) {
+            return;
+        }
+
+        mLocked.mouseScrollingSpeed = speed;
+    } // release lock
+
+    mInputManager->getReader().requestRefreshConfiguration(
+            InputReaderConfiguration::Change::POINTER_SPEED);
+}
+
 void NativeInputManager::setMouseSwapPrimaryButtonEnabled(bool enabled) {
     { // acquire lock
         std::scoped_lock _l(mLock);
@@ -1458,6 +1485,21 @@
             InputReaderConfiguration::Change::MOUSE_SETTINGS);
 }
 
+void NativeInputManager::setMouseAccelerationEnabled(bool enabled) {
+    { // acquire lock
+        std::scoped_lock _l(mLock);
+
+        if (mLocked.mousePointerAccelerationEnabled == enabled) {
+            return;
+        }
+
+        mLocked.mousePointerAccelerationEnabled = enabled;
+    } // release lock
+
+    mInputManager->getReader().requestRefreshConfiguration(
+            InputReaderConfiguration::Change::POINTER_SPEED);
+}
+
 void NativeInputManager::setPointerSpeed(int32_t speed) {
     { // acquire lock
         std::scoped_lock _l(mLock);
@@ -1474,23 +1516,21 @@
             InputReaderConfiguration::Change::POINTER_SPEED);
 }
 
-void NativeInputManager::setMousePointerAccelerationEnabled(ui::LogicalDisplayId displayId,
-                                                            bool enabled) {
+void NativeInputManager::setMouseScalingEnabled(ui::LogicalDisplayId displayId, bool enabled) {
     { // acquire lock
         std::scoped_lock _l(mLock);
 
-        const bool oldEnabled =
-                mLocked.displaysWithMousePointerAccelerationDisabled.count(displayId) == 0;
+        const bool oldEnabled = mLocked.displaysWithMouseScalingDisabled.count(displayId) == 0;
         if (oldEnabled == enabled) {
             return;
         }
 
-        ALOGI("Setting mouse pointer acceleration to %s on display %s", toString(enabled),
+        ALOGI("Setting mouse pointer scaling to %s on display %s", toString(enabled),
               displayId.toString().c_str());
         if (enabled) {
-            mLocked.displaysWithMousePointerAccelerationDisabled.erase(displayId);
+            mLocked.displaysWithMouseScalingDisabled.erase(displayId);
         } else {
-            mLocked.displaysWithMousePointerAccelerationDisabled.emplace(displayId);
+            mLocked.displaysWithMouseScalingDisabled.emplace(displayId);
         }
     } // release lock
 
@@ -1885,9 +1925,9 @@
     return true;
 }
 
-nsecs_t NativeInputManager::interceptKeyBeforeDispatching(const sp<IBinder>& token,
-                                                          const KeyEvent& keyEvent,
-                                                          uint32_t policyFlags) {
+std::variant<nsecs_t, inputdispatcher::KeyEntry::InterceptKeyResult>
+NativeInputManager::interceptKeyBeforeDispatching(const sp<IBinder>& token,
+                                                  const KeyEvent& keyEvent, uint32_t policyFlags) {
     ATRACE_CALL();
     // Policy:
     // - Ignore untrusted events and pass them along.
@@ -1915,7 +1955,19 @@
     if (checkAndClearExceptionFromCallback(env, "interceptKeyBeforeDispatching")) {
         return 0;
     }
-    return delayMillis < 0 ? -1 : milliseconds_to_nanoseconds(delayMillis);
+
+    // Negative delay represent states from intercepting the key.
+    // 0 : Continue event.
+    if (delayMillis == 0) {
+        return inputdispatcher::KeyEntry::InterceptKeyResult::CONTINUE;
+    }
+
+    // -1 : Drop and skip the key event.
+    if (delayMillis == -1) {
+        return inputdispatcher::KeyEntry::InterceptKeyResult::SKIP;
+    }
+
+    return milliseconds_to_nanoseconds(delayMillis);
 }
 
 std::optional<KeyEvent> NativeInputManager::dispatchUnhandledKey(const sp<IBinder>& token,
@@ -2532,11 +2584,11 @@
     im->setPointerSpeed(speed);
 }
 
-static void nativeSetMousePointerAccelerationEnabled(JNIEnv* env, jobject nativeImplObj,
-                                                     jint displayId, jboolean enabled) {
+static void nativeSetMouseScalingEnabled(JNIEnv* env, jobject nativeImplObj, jint displayId,
+                                         jboolean enabled) {
     NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
 
-    im->setMousePointerAccelerationEnabled(ui::LogicalDisplayId{displayId}, enabled);
+    im->setMouseScalingEnabled(ui::LogicalDisplayId{displayId}, enabled);
 }
 
 static void nativeSetTouchpadPointerSpeed(JNIEnv* env, jobject nativeImplObj, jint speed) {
@@ -3208,6 +3260,11 @@
     im->setMouseScrollingAccelerationEnabled(enabled);
 }
 
+static void nativeSetMouseScrollingSpeed(JNIEnv* env, jobject nativeImplObj, jint speed) {
+    NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
+    im->setMouseScrollingSpeed(speed);
+}
+
 static void nativeSetMouseReverseVerticalScrollingEnabled(JNIEnv* env, jobject nativeImplObj,
                                                           bool enabled) {
     NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
@@ -3220,6 +3277,11 @@
     im->setMouseSwapPrimaryButtonEnabled(enabled);
 }
 
+static void nativeSetMouseAccelerationEnabled(JNIEnv* env, jobject nativeImplObj, bool enabled) {
+    NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
+    im->setMouseAccelerationEnabled(enabled);
+}
+
 static jboolean nativeSetKernelWakeEnabled(JNIEnv* env, jobject nativeImplObj, jint deviceId,
                                       jboolean enabled) {
     NativeInputManager* im = getNativeInputManager(env, nativeImplObj);
@@ -3273,13 +3335,14 @@
         {"transferTouch", "(Landroid/os/IBinder;I)Z", (void*)nativeTransferTouchOnDisplay},
         {"getMousePointerSpeed", "()I", (void*)nativeGetMousePointerSpeed},
         {"setPointerSpeed", "(I)V", (void*)nativeSetPointerSpeed},
-        {"setMousePointerAccelerationEnabled", "(IZ)V",
-         (void*)nativeSetMousePointerAccelerationEnabled},
+        {"setMouseScalingEnabled", "(IZ)V", (void*)nativeSetMouseScalingEnabled},
         {"setMouseReverseVerticalScrollingEnabled", "(Z)V",
          (void*)nativeSetMouseReverseVerticalScrollingEnabled},
         {"setMouseScrollingAccelerationEnabled", "(Z)V",
          (void*)nativeSetMouseScrollingAccelerationEnabled},
+        {"setMouseScrollingSpeed", "(I)V", (void*)nativeSetMouseScrollingSpeed},
         {"setMouseSwapPrimaryButtonEnabled", "(Z)V", (void*)nativeSetMouseSwapPrimaryButtonEnabled},
+        {"setMouseAccelerationEnabled", "(Z)V", (void*)nativeSetMouseAccelerationEnabled},
         {"setTouchpadPointerSpeed", "(I)V", (void*)nativeSetTouchpadPointerSpeed},
         {"setTouchpadNaturalScrollingEnabled", "(Z)V",
          (void*)nativeSetTouchpadNaturalScrollingEnabled},
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index e9eafd3..b577710 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -21911,7 +21911,7 @@
                     accountToMigrate,
                     sourceUser,
                     targetUser,
-                    /* callback= */ null, /* handler= */ null)
+                    /* handler= */ null, /* callback= */ null)
                     .getResult(60 * 3, TimeUnit.SECONDS);
             if (copySucceeded) {
                 logCopyAccountStatus(COPY_ACCOUNT_SUCCEEDED, callerPackage);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 9ab9a8f..c5d42ad 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1787,9 +1787,11 @@
             SignedConfigService.registerUpdateReceiver(mSystemContext);
             t.traceEnd();
 
-            t.traceBegin("AppIntegrityService");
-            mSystemServiceManager.startService(AppIntegrityManagerService.class);
-            t.traceEnd();
+            if (!android.server.Flags.removeAppIntegrityManagerService()) {
+                t.traceBegin("AppIntegrityService");
+                mSystemServiceManager.startService(AppIntegrityManagerService.class);
+                t.traceEnd();
+            }
 
             t.traceBegin("StartLogcatManager");
             mSystemServiceManager.startService(LogcatManagerService.class);
diff --git a/services/java/com/android/server/flags.aconfig b/services/java/com/android/server/flags.aconfig
index 0d222fb..4d021ec 100644
--- a/services/java/com/android/server/flags.aconfig
+++ b/services/java/com/android/server/flags.aconfig
@@ -51,4 +51,11 @@
      description: "Remove GameManagerService from Wear"
      bug: "340929737"
      is_fixed_read_only: true
+}
+
+flag {
+     name: "remove_app_integrity_manager_service"
+     namespace: "package_manager_service"
+     description: "Remove AppIntegrityManagerService"
+     bug: "364200023"
 }
\ No newline at end of file
diff --git a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
index 018cf20..c62cd6e 100644
--- a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt
@@ -469,8 +469,9 @@
         permissionName: String,
         deviceId: String
     ): Int {
+        val pid = Binder.getCallingPid()
         val uid = Binder.getCallingUid()
-        val result = context.checkPermission(permissionName, Binder.getCallingPid(), uid)
+        val result = context.checkPermission(permissionName, pid, uid)
         if (result == PackageManager.PERMISSION_GRANTED) {
             return Context.PERMISSION_REQUEST_STATE_GRANTED
         }
@@ -478,17 +479,15 @@
         val appId = UserHandle.getAppId(uid)
         val userId = UserHandle.getUserId(uid)
         val packageState =
-                packageManagerLocal.withFilteredSnapshot(uid, userId).use {
-                    it.getPackageState(packageName)
-                } ?: return Context.PERMISSION_REQUEST_STATE_UNREQUESTABLE
-        val androidPackage = packageState.androidPackage
-                ?: return Context.PERMISSION_REQUEST_STATE_UNREQUESTABLE
+            packageManagerLocal.withFilteredSnapshot(uid, userId).use {
+                it.getPackageState(packageName)
+            } ?: return Context.PERMISSION_REQUEST_STATE_UNREQUESTABLE
+        val androidPackage =
+            packageState.androidPackage ?: return Context.PERMISSION_REQUEST_STATE_UNREQUESTABLE
         if (appId != packageState.appId) {
             return Context.PERMISSION_REQUEST_STATE_UNREQUESTABLE
         }
-        val permission = service.getState {
-            with(policy) { getPermissions()[permissionName] }
-        }
+        val permission = service.getState { with(policy) { getPermissions()[permissionName] } }
         if (permission == null || !permission.isRuntime) {
             return Context.PERMISSION_REQUEST_STATE_UNREQUESTABLE
         }
@@ -496,10 +495,37 @@
             return Context.PERMISSION_REQUEST_STATE_UNREQUESTABLE
         }
 
-        val permissionFlags = service.getState {
-            getPermissionFlagsWithPolicy(appId, userId, permissionName, deviceId)
+        val permissionFlags =
+            service.getState {
+                getPermissionFlagsWithPolicy(appId, userId, permissionName, deviceId)
+            }
+        val isUnreqestable = permissionFlags.hasAnyBit(UNREQUESTABLE_MASK)
+        // Special case for READ_MEDIA_IMAGES due to photo picker
+        if ((permissionName == Manifest.permission.READ_MEDIA_IMAGES ||
+                permissionName == Manifest.permission.READ_MEDIA_VIDEO) && isUnreqestable) {
+            val isUserSelectedGranted =
+                context.checkPermission(
+                    Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED,
+                    pid,
+                    uid,
+                ) == PackageManager.PERMISSION_GRANTED
+            val userSelectedPermissionFlags =
+                service.getState {
+                    getPermissionFlagsWithPolicy(
+                        appId,
+                        userId,
+                        Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED,
+                        deviceId,
+                    )
+                }
+            if (
+                isUserSelectedGranted &&
+                    userSelectedPermissionFlags.hasBits(PermissionFlags.USER_FIXED)
+            ) {
+                return Context.PERMISSION_REQUEST_STATE_REQUESTABLE
+            }
         }
-        return if (permissionFlags.hasAnyBit(UNREQUESTABLE_MASK)) {
+        return if (isUnreqestable) {
             Context.PERMISSION_REQUEST_STATE_UNREQUESTABLE
         } else {
             Context.PERMISSION_REQUEST_STATE_REQUESTABLE
diff --git a/services/print/Android.bp b/services/print/Android.bp
index 0dfceaa..b77cf16 100644
--- a/services/print/Android.bp
+++ b/services/print/Android.bp
@@ -18,8 +18,21 @@
     name: "services.print",
     defaults: ["platform_service_defaults"],
     srcs: [":services.print-sources"],
+    static_libs: ["print_flags_lib"],
     libs: ["services.core"],
     lint: {
         baseline_filename: "lint-baseline.xml",
     },
 }
+
+aconfig_declarations {
+    name: "print_flags",
+    package: "com.android.server.print",
+    container: "system",
+    srcs: ["**/flags.aconfig"],
+}
+
+java_aconfig_library {
+    name: "print_flags_lib",
+    aconfig_declarations: "print_flags",
+}
diff --git a/services/print/java/com/android/server/print/RemotePrintService.java b/services/print/java/com/android/server/print/RemotePrintService.java
index 502cd2c..b856715 100644
--- a/services/print/java/com/android/server/print/RemotePrintService.java
+++ b/services/print/java/com/android/server/print/RemotePrintService.java
@@ -572,7 +572,8 @@
 
         boolean wasBound = mContext.bindServiceAsUser(mIntent, mServiceConnection,
                 Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE
-                        | Context.BIND_INCLUDE_CAPABILITIES | Context.BIND_ALLOW_INSTANT,
+                        | (Flags.doNotIncludeCapabilities() ? 0 : Context.BIND_INCLUDE_CAPABILITIES)
+                        | Context.BIND_ALLOW_INSTANT,
                 new UserHandle(mUserId));
 
         if (!wasBound) {
diff --git a/services/print/java/com/android/server/print/flags.aconfig b/services/print/java/com/android/server/print/flags.aconfig
new file mode 100644
index 0000000..0210791
--- /dev/null
+++ b/services/print/java/com/android/server/print/flags.aconfig
@@ -0,0 +1,9 @@
+package: "com.android.server.print"
+container: "system"
+
+flag {
+    name: "do_not_include_capabilities"
+    namespace: "print"
+    description: "Do not use the flag Context.BIND_INCLUDE_CAPABILITIES when binding to the service"
+    bug: "291281543"
+}
diff --git a/services/robotests/Android.bp b/services/robotests/Android.bp
index 6c4158e..8e0eb6b 100644
--- a/services/robotests/Android.bp
+++ b/services/robotests/Android.bp
@@ -63,7 +63,6 @@
 
     instrumentation_for: "FrameworksServicesLib",
 
-    upstream: true,
 
     strict_mode: false,
 }
diff --git a/services/robotests/backup/Android.bp b/services/robotests/backup/Android.bp
index 3ace3fb..95b38e5 100644
--- a/services/robotests/backup/Android.bp
+++ b/services/robotests/backup/Android.bp
@@ -66,7 +66,6 @@
 
     instrumentation_for: "BackupFrameworksServicesLib",
 
-    upstream: true,
 
     strict_mode: false,
 
diff --git a/services/tests/InputMethodSystemServerTests/Android.bp b/services/tests/InputMethodSystemServerTests/Android.bp
index e6ff506..da58aa1 100644
--- a/services/tests/InputMethodSystemServerTests/Android.bp
+++ b/services/tests/InputMethodSystemServerTests/Android.bp
@@ -86,6 +86,7 @@
         "src/com/android/server/inputmethod/**/ClientControllerTest.java",
     ],
     auto_gen_config: true,
+    team: "trendy_team_ravenwood",
 }
 
 android_test {
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/AppsFilterImplTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/AppsFilterImplTest.java
index 7277fd7..66aaa562 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/AppsFilterImplTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/AppsFilterImplTest.java
@@ -45,6 +45,7 @@
 import android.platform.test.annotations.Presubmit;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.Pair;
 import android.util.SparseArray;
 
 import androidx.annotation.NonNull;
@@ -78,10 +79,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.List;
-import java.util.Map;
-import java.util.Set;
 
 @Presubmit
 @RunWith(JUnit4.class)
@@ -885,18 +883,15 @@
                         return null;
                     }
 
-                    @NonNull
+                    @Nullable
                     @Override
-                    public Map<String, Set<String>> getTargetToOverlayables(
+                    public Pair<String, String> getTargetToOverlayables(
                             @NonNull AndroidPackage pkg) {
                         if (overlay.getPackageName().equals(pkg.getPackageName())) {
-                            Map<String, Set<String>> map = new ArrayMap<>();
-                            Set<String> set = new ArraySet<>();
-                            set.add(overlay.getOverlayTargetOverlayableName());
-                            map.put(overlay.getOverlayTarget(), set);
-                            return map;
+                            return Pair.create(overlay.getOverlayTarget(),
+                                    overlay.getOverlayTargetOverlayableName());
                         }
-                        return Collections.emptyMap();
+                        return null;
                     }
                 },
                 mMockHandler);
@@ -977,18 +972,15 @@
                         return null;
                     }
 
-                    @NonNull
+                    @Nullable
                     @Override
-                    public Map<String, Set<String>> getTargetToOverlayables(
+                    public Pair<String, String> getTargetToOverlayables(
                             @NonNull AndroidPackage pkg) {
                         if (overlay.getPackageName().equals(pkg.getPackageName())) {
-                            Map<String, Set<String>> map = new ArrayMap<>();
-                            Set<String> set = new ArraySet<>();
-                            set.add(overlay.getOverlayTargetOverlayableName());
-                            map.put(overlay.getOverlayTarget(), set);
-                            return map;
+                            return Pair.create(overlay.getOverlayTarget(),
+                                    overlay.getOverlayTargetOverlayableName());
                         }
-                        return Collections.emptyMap();
+                        return null;
                     }
                 },
                 mMockHandler);
diff --git a/services/tests/appfunctions/src/com/android/server/appfunctions/AppFunctionManagerServiceImplTest.kt b/services/tests/appfunctions/src/com/android/server/appfunctions/AppFunctionManagerServiceImplTest.kt
index a69e902..9aaf9ce 100644
--- a/services/tests/appfunctions/src/com/android/server/appfunctions/AppFunctionManagerServiceImplTest.kt
+++ b/services/tests/appfunctions/src/com/android/server/appfunctions/AppFunctionManagerServiceImplTest.kt
@@ -18,28 +18,35 @@
 
 import android.app.appfunctions.flags.Flags
 import android.content.Context
+import android.content.pm.PackageManagerInternal
 import android.platform.test.annotations.RequiresFlagsEnabled
 import android.platform.test.flag.junit.CheckFlagsRule
 import android.platform.test.flag.junit.DeviceFlagsValueProvider
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.modules.utils.testing.ExtendedMockitoRule
+import com.android.server.LocalServices
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.test.runTest
 import org.junit.Ignore
 import org.junit.Rule
 import org.junit.Test
 import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
 
 @RunWith(AndroidJUnit4::class)
 @RequiresFlagsEnabled(Flags.FLAG_ENABLE_APP_FUNCTION_MANAGER)
 class AppFunctionManagerServiceImplTest {
+    @get:Rule val checkFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
+
     @get:Rule
-    val checkFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
+    val extendedMockitoRule =
+        ExtendedMockitoRule.Builder(this).mockStatic(LocalServices::class.java).build()
 
     private val context: Context
         get() = ApplicationProvider.getApplicationContext()
 
-    private val serviceImpl = AppFunctionManagerServiceImpl(context)
+    private val serviceImpl = AppFunctionManagerServiceImpl(context, mock<PackageManagerInternal>())
 
     @Test
     fun testGetLockForPackage_samePackage() {
diff --git a/services/tests/appfunctions/src/com/android/server/appfunctions/AppFunctionsLoggingTest.kt b/services/tests/appfunctions/src/com/android/server/appfunctions/AppFunctionsLoggingTest.kt
index 896d2a21d..687acf56 100644
--- a/services/tests/appfunctions/src/com/android/server/appfunctions/AppFunctionsLoggingTest.kt
+++ b/services/tests/appfunctions/src/com/android/server/appfunctions/AppFunctionsLoggingTest.kt
@@ -25,11 +25,13 @@
 import android.app.appsearch.GenericDocument
 import android.content.Context
 import android.content.pm.PackageManager
+import android.content.pm.PackageManagerInternal
 import android.os.UserHandle
 import androidx.test.core.app.ApplicationProvider
 import androidx.test.ext.junit.runners.AndroidJUnit4
 import com.android.dx.mockito.inline.extended.ExtendedMockito
 import com.android.modules.utils.testing.ExtendedMockitoRule
+import com.android.server.LocalServices
 import com.google.common.util.concurrent.MoreExecutors
 import org.junit.Before
 import org.junit.Rule
@@ -40,24 +42,25 @@
 import org.mockito.kotlin.mock
 import org.mockito.kotlin.whenever
 
-
-/**
- * Tests that AppFunctionsStatsLog logs AppFunctionsRequestReported with the expected values.
- */
+/** Tests that AppFunctionsStatsLog logs AppFunctionsRequestReported with the expected values. */
 @RunWith(AndroidJUnit4::class)
 class AppFunctionsLoggingTest {
     @get:Rule
     val mExtendedMockitoRule: ExtendedMockitoRule =
         ExtendedMockitoRule.Builder(this)
             .mockStatic(AppFunctionsStatsLog::class.java)
+            .mockStatic(LocalServices::class.java)
             .build()
-    private val mContext: Context get() = ApplicationProvider.getApplicationContext()
+    private val mContext: Context
+        get() = ApplicationProvider.getApplicationContext()
+
     private val mMockPackageManager = mock<PackageManager>()
     private val mAppFunctionsLoggerWrapper =
         AppFunctionsLoggerWrapper(
             mMockPackageManager,
             MoreExecutors.directExecutor(),
-            { TEST_CURRENT_TIME_MILLIS })
+            { TEST_CURRENT_TIME_MILLIS },
+        )
     private lateinit var mSafeCallback: SafeOneTimeExecuteAppFunctionCallback
 
     private val mServiceImpl =
@@ -67,25 +70,40 @@
             mock<CallerValidator>(),
             mock<ServiceHelper>(),
             ServiceConfigImpl(),
-            mAppFunctionsLoggerWrapper)
+            mAppFunctionsLoggerWrapper,
+            mock<PackageManagerInternal>(),
+        )
 
-    private val mRequestInternal = ExecuteAppFunctionAidlRequest(
-        ExecuteAppFunctionRequest.Builder(TEST_TARGET_PACKAGE, TEST_FUNCTION_ID).build(),
-        UserHandle.CURRENT, TEST_CALLING_PKG, TEST_INITIAL_REQUEST_TIME_MILLIS
-    )
+    private val mRequestInternal =
+        ExecuteAppFunctionAidlRequest(
+            ExecuteAppFunctionRequest.Builder(TEST_TARGET_PACKAGE, TEST_FUNCTION_ID).build(),
+            UserHandle.CURRENT,
+            TEST_CALLING_PKG,
+            TEST_INITIAL_REQUEST_TIME_MILLIS,
+        )
 
     @Before
     fun setup() {
-        whenever(mMockPackageManager.getPackageUid(eq(TEST_TARGET_PACKAGE), any<Int>())).thenReturn(TEST_TARGET_UID)
-        mSafeCallback = mServiceImpl.initializeSafeExecuteAppFunctionCallback(mRequestInternal, mock<IExecuteAppFunctionCallback>(), TEST_CALLING_UID)
+        whenever(mMockPackageManager.getPackageUid(eq(TEST_TARGET_PACKAGE), any<Int>()))
+            .thenReturn(TEST_TARGET_UID)
+        mSafeCallback =
+            mServiceImpl.initializeSafeExecuteAppFunctionCallback(
+                mRequestInternal,
+                mock<IExecuteAppFunctionCallback>(),
+                TEST_CALLING_UID,
+            )
         mSafeCallback.setExecutionStartTimeAfterBindMillis(TEST_EXECUTION_TIME_AFTER_BIND_MILLIS)
     }
 
     @Test
     fun testOnSuccess_logsSuccessResponse() {
         val response =
-            ExecuteAppFunctionResponse(GenericDocument.Builder<GenericDocument.Builder<*>>("", "", "")
-                .setPropertyLong("longProperty", 42L).setPropertyString("stringProperty", "text").build())
+            ExecuteAppFunctionResponse(
+                GenericDocument.Builder<GenericDocument.Builder<*>>("", "", "")
+                    .setPropertyLong("longProperty", 42L)
+                    .setPropertyString("stringProperty", "text")
+                    .build()
+            )
 
         mSafeCallback.onResult(response)
 
@@ -98,14 +116,16 @@
                 /* requestSizeBytes= */ eq<Int>(mRequestInternal.clientRequest.requestDataSize),
                 /* responseSizeBytes= */ eq<Int>(response.responseDataSize),
                 /* requestDurationMs= */ eq<Long>(TEST_EXPECTED_E2E_DURATION_MILLIS),
-                /* requestOverheadMs= */ eq<Long>(TEST_EXPECTED_OVERHEAD_DURATION_MILLIS)
+                /* requestOverheadMs= */ eq<Long>(TEST_EXPECTED_OVERHEAD_DURATION_MILLIS),
             )
         }
     }
 
     @Test
     fun testOnError_logsFailureResponse() {
-        mSafeCallback.onError(AppFunctionException(AppFunctionException.ERROR_DENIED, "Error: permission denied"))
+        mSafeCallback.onError(
+            AppFunctionException(AppFunctionException.ERROR_DENIED, "Error: permission denied")
+        )
 
         ExtendedMockito.verify {
             AppFunctionsStatsLog.write(
@@ -116,7 +136,7 @@
                 /* requestSizeBytes= */ eq<Int>(mRequestInternal.clientRequest.requestDataSize),
                 /* responseSizeBytes= */ eq<Int>(0),
                 /* requestDurationMs= */ eq<Long>(TEST_EXPECTED_E2E_DURATION_MILLIS),
-                /* requestOverheadMs= */ eq<Long>(TEST_EXPECTED_OVERHEAD_DURATION_MILLIS)
+                /* requestOverheadMs= */ eq<Long>(TEST_EXPECTED_OVERHEAD_DURATION_MILLIS),
             )
         }
     }
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 0e9dfed..7d25acd 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
@@ -52,6 +52,7 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import com.android.server.display.brightness.clamper.BrightnessClamperController;
 import com.android.server.display.config.HysteresisLevels;
 import com.android.server.display.feature.DisplayManagerFlags;
 import com.android.server.testutils.OffsettableClock;
@@ -102,7 +103,8 @@
     @Mock BrightnessRangeController mBrightnessRangeController;
     @Mock
     DisplayManagerFlags mDisplayManagerFlags;
-    @Mock BrightnessThrottler mBrightnessThrottler;
+    @Mock
+    BrightnessClamperController mBrightnessClamperController;
 
     @Before
     public void setUp() throws Exception {
@@ -175,7 +177,7 @@
                 RESET_AMBIENT_LUX_AFTER_WARMUP_CONFIG,
                 mAmbientBrightnessThresholds, mScreenBrightnessThresholds,
                 mAmbientBrightnessThresholdsIdle, mScreenBrightnessThresholdsIdle,
-                mContext, mBrightnessRangeController, mBrightnessThrottler,
+                mContext, mBrightnessRangeController, mBrightnessClamperController,
                 useHorizon ? AMBIENT_LIGHT_HORIZON_SHORT : 1,
                 useHorizon ? AMBIENT_LIGHT_HORIZON_LONG : 10000, userLux, userNits,
                 mDisplayManagerFlags
@@ -186,8 +188,8 @@
         when(mBrightnessRangeController.getCurrentBrightnessMin()).thenReturn(
                 BRIGHTNESS_MIN_FLOAT);
         // Disable brightness throttling by default. Individual tests can enable it as needed.
-        when(mBrightnessThrottler.getBrightnessCap()).thenReturn(BRIGHTNESS_MAX_FLOAT);
-        when(mBrightnessThrottler.isThrottled()).thenReturn(false);
+        when(mBrightnessClamperController.getMaxBrightness()).thenReturn(BRIGHTNESS_MAX_FLOAT);
+        when(mBrightnessClamperController.isThrottled()).thenReturn(false);
 
         // Configure the brightness controller and grab an instance of the sensor listener,
         // through which we can deliver fake (for test) sensor values.
@@ -754,8 +756,8 @@
 
         // Apply throttling and notify ABC (simulates DisplayPowerController#updatePowerState())
         final float throttledBrightness = 0.123f;
-        when(mBrightnessThrottler.getBrightnessCap()).thenReturn(throttledBrightness);
-        when(mBrightnessThrottler.isThrottled()).thenReturn(true);
+        when(mBrightnessClamperController.getMaxBrightness()).thenReturn(throttledBrightness);
+        when(mBrightnessClamperController.isThrottled()).thenReturn(true);
         mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration= */,
                 BRIGHTNESS_MAX_FLOAT /* brightness= */, false /* userChangedBrightness= */,
                 0 /* adjustment= */, false /* userChanged= */, DisplayPowerRequest.POLICY_BRIGHT,
@@ -766,8 +768,8 @@
         assertEquals(BRIGHTNESS_MAX_FLOAT, mController.getRawAutomaticScreenBrightness(), 0.0f);
 
         // Remove throttling and notify ABC again
-        when(mBrightnessThrottler.getBrightnessCap()).thenReturn(BRIGHTNESS_MAX_FLOAT);
-        when(mBrightnessThrottler.isThrottled()).thenReturn(false);
+        when(mBrightnessClamperController.getMaxBrightness()).thenReturn(BRIGHTNESS_MAX_FLOAT);
+        when(mBrightnessClamperController.isThrottled()).thenReturn(false);
         mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration= */,
                 BRIGHTNESS_MAX_FLOAT /* brightness= */, false /* userChangedBrightness= */,
                 0 /* adjustment= */, false /* userChanged= */, DisplayPowerRequest.POLICY_BRIGHT,
@@ -1098,7 +1100,7 @@
         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);
+        when(mBrightnessClamperController.getMaxBrightness()).thenReturn(BRIGHTNESS_MAX_FLOAT);
 
         // Set policy to DOZE
         mController.configure(AUTO_BRIGHTNESS_ENABLED, /* configuration= */ null,
@@ -1135,7 +1137,7 @@
         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);
+        when(mBrightnessClamperController.getMaxBrightness()).thenReturn(BRIGHTNESS_MAX_FLOAT);
 
         // Set policy to DOZE
         mController.configure(AUTO_BRIGHTNESS_ENABLED, /* configuration= */ null,
@@ -1172,7 +1174,7 @@
         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);
+        when(mBrightnessClamperController.getMaxBrightness()).thenReturn(BRIGHTNESS_MAX_FLOAT);
 
         // Set policy to DOZE
         mController.configure(AUTO_BRIGHTNESS_ENABLED, /* configuration= */ null,
@@ -1204,7 +1206,7 @@
         when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux)).thenReturn(lux);
         when(mDozeBrightnessMappingStrategy.getBrightness(eq(lux), /* packageName= */ eq(null),
                 /* category= */ anyInt())).thenReturn(normalizedBrightness);
-        when(mBrightnessThrottler.getBrightnessCap()).thenReturn(BRIGHTNESS_MAX_FLOAT);
+        when(mBrightnessClamperController.getMaxBrightness()).thenReturn(BRIGHTNESS_MAX_FLOAT);
 
         // Switch mode to DOZE
         mController.switchMode(AUTO_BRIGHTNESS_MODE_DOZE, /* sendUpdate= */ false);
@@ -1239,7 +1241,7 @@
         when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux)).thenReturn(lux);
         when(mDozeBrightnessMappingStrategy.getBrightness(eq(lux), /* packageName= */ eq(null),
                 /* category= */ anyInt())).thenReturn(normalizedBrightness);
-        when(mBrightnessThrottler.getBrightnessCap()).thenReturn(BRIGHTNESS_MAX_FLOAT);
+        when(mBrightnessClamperController.getMaxBrightness()).thenReturn(BRIGHTNESS_MAX_FLOAT);
 
         // Send a new sensor value
         listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, (int) lux));
@@ -1267,7 +1269,7 @@
         when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux)).thenReturn(lux);
         when(mDozeBrightnessMappingStrategy.getBrightness(eq(lux), /* packageName= */ eq(null),
                 /* category= */ anyInt())).thenReturn(normalizedBrightness);
-        when(mBrightnessThrottler.getBrightnessCap()).thenReturn(BRIGHTNESS_MAX_FLOAT);
+        when(mBrightnessClamperController.getMaxBrightness()).thenReturn(BRIGHTNESS_MAX_FLOAT);
 
         // Send a new sensor value
         listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, (int) lux));
diff --git a/services/tests/displayservicetests/src/com/android/server/display/BrightnessThrottlerTest.java b/services/tests/displayservicetests/src/com/android/server/display/BrightnessThrottlerTest.java
deleted file mode 100644
index 36baacc..0000000
--- a/services/tests/displayservicetests/src/com/android/server/display/BrightnessThrottlerTest.java
+++ /dev/null
@@ -1,556 +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 static com.android.server.display.config.DisplayDeviceConfigTestUtilsKt.createSensorData;
-
-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.mockito.Mockito.eq;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.hardware.display.BrightnessInfo;
-import android.os.Handler;
-import android.os.IThermalEventListener;
-import android.os.IThermalService;
-import android.os.Message;
-import android.os.PowerManager;
-import android.os.Temperature;
-import android.os.Temperature.ThrottlingStatus;
-import android.os.test.TestLooper;
-
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.internal.os.BackgroundThread;
-import com.android.server.display.BrightnessThrottler.Injector;
-import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData;
-import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData.ThrottlingLevel;
-import com.android.server.display.config.SensorData;
-import com.android.server.display.mode.DisplayModeDirectorTest;
-
-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.HashMap;
-import java.util.List;
-import java.util.Map;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class BrightnessThrottlerTest {
-    private static final float EPSILON = 0.000001f;
-
-    private Handler mHandler;
-    private TestLooper mTestLooper;
-
-    @Mock IThermalService mThermalServiceMock;
-    @Mock Injector mInjectorMock;
-
-    DisplayModeDirectorTest.FakeDeviceConfig mDeviceConfigFake;
-
-    @Captor ArgumentCaptor<IThermalEventListener> mThermalEventListenerCaptor;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        when(mInjectorMock.getThermalService()).thenReturn(mThermalServiceMock);
-        mTestLooper = new TestLooper();
-        mHandler = new Handler(mTestLooper.getLooper(), new Handler.Callback() {
-            @Override
-            public boolean handleMessage(Message msg) {
-                return true;
-            }
-        });
-        mDeviceConfigFake = new DisplayModeDirectorTest.FakeDeviceConfig();
-        when(mInjectorMock.getDeviceConfig()).thenReturn(mDeviceConfigFake);
-
-    }
-
-    /////////////////
-    // Test Methods
-    /////////////////
-
-    @Test
-    public void testThermalBrightnessThrottlingData() {
-        List<ThrottlingLevel> singleLevel = new ArrayList<>();
-        singleLevel.add(new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.25f));
-
-        List<ThrottlingLevel> validLevels = new ArrayList<>();
-        validLevels.add(new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE, 0.62f));
-        validLevels.add(new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.25f));
-
-        List<ThrottlingLevel> unsortedThermalLevels = new ArrayList<>();
-        unsortedThermalLevels.add(new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.62f));
-        unsortedThermalLevels.add(new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE, 0.25f));
-
-        List<ThrottlingLevel> unsortedBrightnessLevels = new ArrayList<>();
-        unsortedBrightnessLevels.add(
-                new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE, 0.25f));
-        unsortedBrightnessLevels.add(
-                new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.62f));
-
-        List<ThrottlingLevel> unsortedLevels = new ArrayList<>();
-        unsortedLevels.add(new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.25f));
-        unsortedLevels.add(new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE, 0.62f));
-
-        List<ThrottlingLevel> invalidLevel = new ArrayList<>();
-        invalidLevel.add(new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL,
-                PowerManager.BRIGHTNESS_MAX + EPSILON));
-
-        // Test invalid data
-        ThermalBrightnessThrottlingData data;
-        data = ThermalBrightnessThrottlingData.create((List<ThrottlingLevel>) null);
-        assertEquals(data, null);
-        data = ThermalBrightnessThrottlingData.create(new ArrayList<ThrottlingLevel>());
-        assertEquals(data, null);
-        data = ThermalBrightnessThrottlingData.create(unsortedThermalLevels);
-        assertEquals(data, null);
-        data = ThermalBrightnessThrottlingData.create(unsortedBrightnessLevels);
-        assertEquals(data, null);
-        data = ThermalBrightnessThrottlingData.create(unsortedLevels);
-        assertEquals(data, null);
-        data = ThermalBrightnessThrottlingData.create(invalidLevel);
-        assertEquals(data, null);
-
-        // Test valid data
-        data = ThermalBrightnessThrottlingData.create(singleLevel);
-        assertNotEquals(data, null);
-        assertThrottlingLevelsEquals(singleLevel, data.throttlingLevels);
-
-        data = ThermalBrightnessThrottlingData.create(validLevels);
-        assertNotEquals(data, null);
-        assertThrottlingLevelsEquals(validLevels, data.throttlingLevels);
-    }
-
-    @Test
-    public void testThermalThrottlingUnsupported() {
-        final BrightnessThrottler throttler = createThrottlerUnsupported();
-        assertFalse(throttler.deviceSupportsThrottling());
-
-        // Thermal listener shouldn't be registered if throttling is unsupported
-        verify(mInjectorMock, never()).getThermalService();
-
-        // Ensure that brightness is uncapped when the device doesn't support throttling
-        assertEquals(PowerManager.BRIGHTNESS_MAX, throttler.getBrightnessCap(), 0f);
-    }
-
-    @Test
-    public void testThermalThrottlingSingleLevel() throws Exception {
-        final ThrottlingLevel level = new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL,
-                0.25f);
-
-        List<ThrottlingLevel> levels = new ArrayList<>();
-        levels.add(level);
-        final ThermalBrightnessThrottlingData data = ThermalBrightnessThrottlingData.create(levels);
-        final BrightnessThrottler throttler = createThrottlerSupported(data);
-        assertTrue(throttler.deviceSupportsThrottling());
-
-        verify(mThermalServiceMock).registerThermalEventListenerWithType(
-                mThermalEventListenerCaptor.capture(), eq(Temperature.TYPE_SKIN));
-        final IThermalEventListener listener = mThermalEventListenerCaptor.getValue();
-
-        // Set status too low to trigger throttling
-        listener.notifyThrottling(getSkinTemp(level.thermalStatus - 1));
-        mTestLooper.dispatchAll();
-        assertEquals(PowerManager.BRIGHTNESS_MAX, throttler.getBrightnessCap(), 0f);
-        assertFalse(throttler.isThrottled());
-        assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE, throttler.getBrightnessMaxReason());
-
-        // Set status just high enough to trigger throttling
-        listener.notifyThrottling(getSkinTemp(level.thermalStatus));
-        mTestLooper.dispatchAll();
-        assertEquals(level.brightness, throttler.getBrightnessCap(), 0f);
-        assertTrue(throttler.isThrottled());
-        assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL,
-                throttler.getBrightnessMaxReason());
-
-        // Set status more than high enough to trigger throttling
-        listener.notifyThrottling(getSkinTemp(level.thermalStatus + 1));
-        mTestLooper.dispatchAll();
-        assertEquals(level.brightness, throttler.getBrightnessCap(), 0f);
-        assertTrue(throttler.isThrottled());
-        assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL,
-                throttler.getBrightnessMaxReason());
-
-        // Return to the lower throttling level
-        listener.notifyThrottling(getSkinTemp(level.thermalStatus));
-        mTestLooper.dispatchAll();
-        assertEquals(level.brightness, throttler.getBrightnessCap(), 0f);
-        assertTrue(throttler.isThrottled());
-        assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL,
-                throttler.getBrightnessMaxReason());
-
-        // Cool down
-        listener.notifyThrottling(getSkinTemp(level.thermalStatus - 1));
-        mTestLooper.dispatchAll();
-        assertEquals(PowerManager.BRIGHTNESS_MAX, throttler.getBrightnessCap(), 0f);
-        assertFalse(throttler.isThrottled());
-        assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE,
-                throttler.getBrightnessMaxReason());
-    }
-
-    @Test
-    public void testThermalThrottlingMultiLevel() throws Exception {
-        final ThrottlingLevel levelLo = new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE,
-                0.62f);
-        final ThrottlingLevel levelHi = new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL,
-                0.25f);
-
-        List<ThrottlingLevel> levels = new ArrayList<>();
-        levels.add(levelLo);
-        levels.add(levelHi);
-        final ThermalBrightnessThrottlingData data = ThermalBrightnessThrottlingData.create(levels);
-        final BrightnessThrottler throttler = createThrottlerSupported(data);
-        assertTrue(throttler.deviceSupportsThrottling());
-
-        verify(mThermalServiceMock).registerThermalEventListenerWithType(
-                mThermalEventListenerCaptor.capture(), eq(Temperature.TYPE_SKIN));
-        final IThermalEventListener listener = mThermalEventListenerCaptor.getValue();
-
-        // Set status too low to trigger throttling
-        listener.notifyThrottling(getSkinTemp(levelLo.thermalStatus - 1));
-        mTestLooper.dispatchAll();
-        assertEquals(PowerManager.BRIGHTNESS_MAX, throttler.getBrightnessCap(), 0f);
-        assertFalse(throttler.isThrottled());
-        assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE, throttler.getBrightnessMaxReason());
-
-        // Set status just high enough to trigger throttling
-        listener.notifyThrottling(getSkinTemp(levelLo.thermalStatus));
-        mTestLooper.dispatchAll();
-        assertEquals(levelLo.brightness, throttler.getBrightnessCap(), 0f);
-        assertTrue(throttler.isThrottled());
-        assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL,
-                throttler.getBrightnessMaxReason());
-
-        // Set status to an intermediate throttling level
-        listener.notifyThrottling(getSkinTemp(levelLo.thermalStatus + 1));
-        mTestLooper.dispatchAll();
-        assertEquals(levelLo.brightness, throttler.getBrightnessCap(), 0f);
-        assertTrue(throttler.isThrottled());
-        assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL,
-                throttler.getBrightnessMaxReason());
-
-        // Set status to the highest configured throttling level
-        listener.notifyThrottling(getSkinTemp(levelHi.thermalStatus));
-        mTestLooper.dispatchAll();
-        assertEquals(levelHi.brightness, throttler.getBrightnessCap(), 0f);
-        assertTrue(throttler.isThrottled());
-        assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL,
-                throttler.getBrightnessMaxReason());
-
-        // Set status to exceed the highest configured throttling level
-        listener.notifyThrottling(getSkinTemp(levelHi.thermalStatus + 1));
-        mTestLooper.dispatchAll();
-        assertEquals(levelHi.brightness, throttler.getBrightnessCap(), 0f);
-        assertTrue(throttler.isThrottled());
-        assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL,
-                throttler.getBrightnessMaxReason());
-
-        // Return to an intermediate throttling level
-        listener.notifyThrottling(getSkinTemp(levelLo.thermalStatus + 1));
-        mTestLooper.dispatchAll();
-        assertEquals(levelLo.brightness, throttler.getBrightnessCap(), 0f);
-        assertTrue(throttler.isThrottled());
-        assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL,
-                throttler.getBrightnessMaxReason());
-
-        // Return to the lowest configured throttling level
-        listener.notifyThrottling(getSkinTemp(levelLo.thermalStatus));
-        mTestLooper.dispatchAll();
-        assertEquals(levelLo.brightness, throttler.getBrightnessCap(), 0f);
-        assertTrue(throttler.isThrottled());
-        assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL,
-                throttler.getBrightnessMaxReason());
-
-        // Cool down
-        listener.notifyThrottling(getSkinTemp(levelLo.thermalStatus - 1));
-        mTestLooper.dispatchAll();
-        assertEquals(PowerManager.BRIGHTNESS_MAX, throttler.getBrightnessCap(), 0f);
-        assertFalse(throttler.isThrottled());
-        assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE, throttler.getBrightnessMaxReason());
-    }
-
-
-    @Test
-    public void testThermalThrottlingWithDisplaySensor() throws Exception {
-        final ThrottlingLevel level =
-                    new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.25f);
-        List<ThrottlingLevel> levels = new ArrayList<>(List.of(level));
-        final ThermalBrightnessThrottlingData data = ThermalBrightnessThrottlingData.create(levels);
-        final SensorData tempSensor = createSensorData("DISPLAY", "VIRTUAL-SKIN-DISPLAY");
-        final BrightnessThrottler throttler =
-                    createThrottlerSupportedWithTempSensor(data, tempSensor);
-        assertTrue(throttler.deviceSupportsThrottling());
-
-        verify(mThermalServiceMock)
-                    .registerThermalEventListenerWithType(
-                        mThermalEventListenerCaptor.capture(), eq(Temperature.TYPE_DISPLAY));
-        final IThermalEventListener listener = mThermalEventListenerCaptor.getValue();
-
-        // Set VIRTUAL-SKIN-DISPLAY tatus too low to verify no throttling.
-        listener.notifyThrottling(getDisplayTempWithName(tempSensor.name, level.thermalStatus - 1));
-        mTestLooper.dispatchAll();
-        assertEquals(PowerManager.BRIGHTNESS_MAX, throttler.getBrightnessCap(), 0f);
-        assertFalse(throttler.isThrottled());
-        assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE, throttler.getBrightnessMaxReason());
-
-        // Verify when skin sensor throttled, no brightness throttling triggered.
-        listener.notifyThrottling(getSkinTemp(level.thermalStatus + 1));
-        mTestLooper.dispatchAll();
-        assertEquals(PowerManager.BRIGHTNESS_MAX, throttler.getBrightnessCap(), 0f);
-        assertFalse(throttler.isThrottled());
-        assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE, throttler.getBrightnessMaxReason());
-
-        // Verify when display sensor of another name throttled, no brightness throttling triggered.
-        listener.notifyThrottling(getDisplayTempWithName("ANOTHER-NAME", level.thermalStatus + 1));
-        mTestLooper.dispatchAll();
-        assertEquals(PowerManager.BRIGHTNESS_MAX, throttler.getBrightnessCap(), 0f);
-        assertFalse(throttler.isThrottled());
-        assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE, throttler.getBrightnessMaxReason());
-
-        // Verify when display sensor of current name throttled, brightness throttling triggered.
-        listener.notifyThrottling(getDisplayTempWithName(tempSensor.name, level.thermalStatus + 1));
-        mTestLooper.dispatchAll();
-        assertEquals(level.brightness, throttler.getBrightnessCap(), 0f);
-        assertTrue(throttler.isThrottled());
-        assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL,
-                throttler.getBrightnessMaxReason());
-    }
-
-    @Test public void testUpdateThermalThrottlingData() throws Exception {
-        // Initialise brightness throttling levels
-        // Ensure that they are overridden by setting the data through device config.
-        final ThrottlingLevel level = new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL,
-                0.25f);
-        List<ThrottlingLevel> levels = new ArrayList<>();
-        levels.add(level);
-        final ThermalBrightnessThrottlingData data = ThermalBrightnessThrottlingData.create(levels);
-        mDeviceConfigFake.setThermalBrightnessThrottlingData("123,1,critical,0.4");
-        final BrightnessThrottler throttler = createThrottlerSupported(data);
-
-        verify(mThermalServiceMock).registerThermalEventListenerWithType(
-                mThermalEventListenerCaptor.capture(), eq(Temperature.TYPE_SKIN));
-        final IThermalEventListener listener = mThermalEventListenerCaptor.getValue();
-        testThermalThrottling(throttler, listener, PowerManager.BRIGHTNESS_MAX, 0.4f);
-
-        // Set new (valid) data from device config
-        mDeviceConfigFake.setThermalBrightnessThrottlingData("123,1,critical,0.8");
-        testThermalThrottling(throttler, listener, PowerManager.BRIGHTNESS_MAX, 0.8f);
-
-        mDeviceConfigFake.setThermalBrightnessThrottlingData(
-                "123,1,critical,0.75;123,1,critical,0.99,id_2");
-        testThermalThrottling(throttler, listener, PowerManager.BRIGHTNESS_MAX, 0.75f);
-        mDeviceConfigFake.setThermalBrightnessThrottlingData(
-                "123,1,critical,0.8,default;123,1,critical,0.99,id_2");
-        testThermalThrottling(throttler, listener, PowerManager.BRIGHTNESS_MAX, 0.8f);
-    }
-
-    @Test public void testInvalidThrottlingStrings() throws Exception {
-        // Initialise brightness throttling levels
-        // Ensure that they are not overridden by invalid data through device config.
-        final ThrottlingLevel level = new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL,
-                0.25f);
-        List<ThrottlingLevel> levels = new ArrayList<>();
-        levels.add(level);
-        final ThermalBrightnessThrottlingData data = ThermalBrightnessThrottlingData.create(levels);
-        final BrightnessThrottler throttler = createThrottlerSupported(data);
-        verify(mThermalServiceMock).registerThermalEventListenerWithType(
-                mThermalEventListenerCaptor.capture(), eq(Temperature.TYPE_SKIN));
-        final IThermalEventListener listener = mThermalEventListenerCaptor.getValue();
-
-        // None of these are valid so shouldn't override the original data
-
-        // Not the current id
-        mDeviceConfigFake.setThermalBrightnessThrottlingData("321,1,critical,0.4");
-        testThermalThrottling(throttler, listener, PowerManager.BRIGHTNESS_MAX, 0.25f);
-        // Incorrect number
-        mDeviceConfigFake.setThermalBrightnessThrottlingData("123,0,critical,0.4");
-        testThermalThrottling(throttler, listener, PowerManager.BRIGHTNESS_MAX, 0.25f);
-        // Incorrect number
-        mDeviceConfigFake.setThermalBrightnessThrottlingData("123,2,critical,0.4");
-        testThermalThrottling(throttler, listener, PowerManager.BRIGHTNESS_MAX, 0.25f);
-        // Invalid level
-        mDeviceConfigFake.setThermalBrightnessThrottlingData("123,1,invalid,0.4");
-        testThermalThrottling(throttler, listener, PowerManager.BRIGHTNESS_MAX, 0.25f);
-        // Invalid brightness
-        mDeviceConfigFake.setThermalBrightnessThrottlingData("123,1,critical,none");
-        testThermalThrottling(throttler, listener, PowerManager.BRIGHTNESS_MAX, 0.25f);
-        // Invalid brightness
-        mDeviceConfigFake.setThermalBrightnessThrottlingData("123,1,critical,-3");
-        testThermalThrottling(throttler, listener, PowerManager.BRIGHTNESS_MAX, 0.25f);
-        // Invalid format
-        mDeviceConfigFake.setThermalBrightnessThrottlingData("invalid string");
-        testThermalThrottling(throttler, listener, PowerManager.BRIGHTNESS_MAX, 0.25f);
-        // Invalid format
-        mDeviceConfigFake.setThermalBrightnessThrottlingData("");
-        testThermalThrottling(throttler, listener, PowerManager.BRIGHTNESS_MAX, 0.25f);
-        // Invalid string format
-        mDeviceConfigFake.setThermalBrightnessThrottlingData(
-                "123,default,1,critical,0.75,1,critical,0.99");
-        testThermalThrottling(throttler, listener, PowerManager.BRIGHTNESS_MAX, 0.25f);
-        // Invalid level string and number string
-        mDeviceConfigFake.setThermalBrightnessThrottlingData(
-                "123,1,1,critical,0.75,id_2,1,critical,0.99");
-        testThermalThrottling(throttler, listener, PowerManager.BRIGHTNESS_MAX, 0.25f);
-        // Invalid format - (two default ids for same display)
-        mDeviceConfigFake.setThermalBrightnessThrottlingData(
-                "123,1,critical,0.75,default;123,1,critical,0.99");
-        testThermalThrottling(throttler, listener, PowerManager.BRIGHTNESS_MAX, 0.25f);
-    }
-
-    private void testThermalThrottling(BrightnessThrottler throttler,
-            IThermalEventListener listener, float tooLowCap, float tooHighCap) throws Exception {
-        final ThrottlingLevel level = new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL,
-                tooHighCap);
-
-        // Set status too low to trigger throttling
-        listener.notifyThrottling(getSkinTemp(level.thermalStatus - 1));
-        mTestLooper.dispatchAll();
-        assertEquals(tooLowCap, throttler.getBrightnessCap(), 0f);
-        assertFalse(throttler.isThrottled());
-
-        // Set status high enough to trigger throttling
-        listener.notifyThrottling(getSkinTemp(level.thermalStatus));
-        mTestLooper.dispatchAll();
-        assertEquals(tooHighCap, throttler.getBrightnessCap(), 0f);
-        assertTrue(throttler.isThrottled());
-    }
-
-    @Test public void testMultipleConfigPoints() throws Exception {
-        // Initialise brightness throttling levels
-        final ThrottlingLevel level = new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL,
-                0.25f);
-        List<ThrottlingLevel> levels = new ArrayList<>();
-        levels.add(level);
-        final ThermalBrightnessThrottlingData data = ThermalBrightnessThrottlingData.create(levels);
-
-        // These are identical to the string set below
-        final ThrottlingLevel levelSevere = new ThrottlingLevel(PowerManager.THERMAL_STATUS_SEVERE,
-                0.9f);
-        final ThrottlingLevel levelCritical = new ThrottlingLevel(
-                PowerManager.THERMAL_STATUS_CRITICAL, 0.5f);
-        final ThrottlingLevel levelEmergency = new ThrottlingLevel(
-                PowerManager.THERMAL_STATUS_EMERGENCY, 0.1f);
-
-        mDeviceConfigFake.setThermalBrightnessThrottlingData(
-                "123,3,severe,0.9,critical,0.5,emergency,0.1");
-        final BrightnessThrottler throttler = createThrottlerSupported(data);
-
-        verify(mThermalServiceMock).registerThermalEventListenerWithType(
-                mThermalEventListenerCaptor.capture(), eq(Temperature.TYPE_SKIN));
-        final IThermalEventListener listener = mThermalEventListenerCaptor.getValue();
-
-        // Ensure that the multiple levels set via the string through the device config correctly
-        // override the original display device config ones.
-
-        // levelSevere
-        // Set status too low to trigger throttling
-        listener.notifyThrottling(getSkinTemp(levelSevere.thermalStatus - 1));
-        mTestLooper.dispatchAll();
-        assertEquals(PowerManager.BRIGHTNESS_MAX, throttler.getBrightnessCap(), 0f);
-        assertFalse(throttler.isThrottled());
-
-        // Set status high enough to trigger throttling
-        listener.notifyThrottling(getSkinTemp(levelSevere.thermalStatus));
-        mTestLooper.dispatchAll();
-        assertEquals(0.9f, throttler.getBrightnessCap(), 0f);
-        assertTrue(throttler.isThrottled());
-
-        // levelCritical
-        // Set status too low to trigger throttling
-        listener.notifyThrottling(getSkinTemp(levelCritical.thermalStatus - 1));
-        mTestLooper.dispatchAll();
-        assertEquals(0.9f, throttler.getBrightnessCap(), 0f);
-        assertTrue(throttler.isThrottled());
-
-        // Set status high enough to trigger throttling
-        listener.notifyThrottling(getSkinTemp(levelCritical.thermalStatus));
-        mTestLooper.dispatchAll();
-        assertEquals(0.5f, throttler.getBrightnessCap(), 0f);
-        assertTrue(throttler.isThrottled());
-
-        //levelEmergency
-        // Set status too low to trigger throttling
-        listener.notifyThrottling(getSkinTemp(levelEmergency.thermalStatus - 1));
-        mTestLooper.dispatchAll();
-        assertEquals(0.5f, throttler.getBrightnessCap(), 0f);
-        assertTrue(throttler.isThrottled());
-
-        // Set status high enough to trigger throttling
-        listener.notifyThrottling(getSkinTemp(levelEmergency.thermalStatus));
-        mTestLooper.dispatchAll();
-        assertEquals(0.1f, throttler.getBrightnessCap(), 0f);
-        assertTrue(throttler.isThrottled());
-    }
-
-    private void assertThrottlingLevelsEquals(
-            List<ThrottlingLevel> expected,
-            List<ThrottlingLevel> actual) {
-        assertEquals(expected.size(), actual.size());
-
-        for (int i = 0; i < expected.size(); i++) {
-            ThrottlingLevel expectedLevel = expected.get(i);
-            ThrottlingLevel actualLevel = actual.get(i);
-
-            assertEquals(expectedLevel.thermalStatus, actualLevel.thermalStatus);
-            assertEquals(expectedLevel.brightness, actualLevel.brightness, 0.0f);
-        }
-    }
-
-    private BrightnessThrottler createThrottlerUnsupported() {
-        return new BrightnessThrottler(mInjectorMock, mHandler, mHandler,
-                /* throttlingChangeCallback= */ () -> {}, /* uniqueDisplayId= */ null,
-                /* thermalThrottlingDataId= */ null,
-                /* thermalThrottlingDataMap= */ new HashMap<>(1),
-                /* tempSensor= */ null);
-    }
-
-    private BrightnessThrottler createThrottlerSupported(ThermalBrightnessThrottlingData data) {
-        SensorData tempSensor = SensorData.loadTempSensorUnspecifiedConfig();
-        return createThrottlerSupportedWithTempSensor(data, tempSensor);
-    }
-    private BrightnessThrottler createThrottlerSupportedWithTempSensor(
-                ThermalBrightnessThrottlingData data, SensorData tempSensor) {
-        assertNotNull(data);
-        Map<String, ThermalBrightnessThrottlingData> throttlingDataMap = new HashMap<>(1);
-        throttlingDataMap.put("default", data);
-        return new BrightnessThrottler(mInjectorMock, mHandler, BackgroundThread.getHandler(),
-                    () -> {}, "123", "default", throttlingDataMap, tempSensor);
-    }
-
-    private Temperature getSkinTemp(@ThrottlingStatus int status) {
-        return new Temperature(30.0f, Temperature.TYPE_SKIN, "test_skin_temp", status);
-    }
-
-    private Temperature getDisplayTempWithName(
-                String sensorName, @ThrottlingStatus int status) {
-        assertNotNull(sensorName);
-        return new Temperature(30.0f, Temperature.TYPE_DISPLAY, sensorName, status);
-    }
-}
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 a9ad435..02e5470 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -415,7 +415,6 @@
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
-        when(mMockFlags.isConnectedDisplayManagementEnabled()).thenReturn(false);
 
         mLocalServiceKeeperRule.overrideLocalService(
                 InputManagerInternal.class, mMockInputManagerInternal);
@@ -2797,30 +2796,7 @@
     }
 
     @Test
-    public void testConnectExternalDisplay_withoutDisplayManagement_shouldAddDisplay() {
-        when(mMockFlags.isConnectedDisplayManagementEnabled()).thenReturn(false);
-        manageDisplaysPermission(/* granted= */ true);
-        DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
-        DisplayManagerService.BinderService bs = displayManager.new BinderService();
-        LogicalDisplayMapper logicalDisplayMapper = displayManager.getLogicalDisplayMapper();
-        FakeDisplayManagerCallback callback = new FakeDisplayManagerCallback();
-        bs.registerCallbackWithEventMask(callback, STANDARD_AND_CONNECTION_DISPLAY_EVENTS);
-        callback.expectsEvent(EVENT_DISPLAY_ADDED);
-
-        FakeDisplayDevice displayDevice =
-                createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_EXTERNAL);
-        callback.waitForExpectedEvent();
-
-        LogicalDisplay display =
-                logicalDisplayMapper.getDisplayLocked(displayDevice, /* includeDisabled= */ true);
-        assertThat(display.isEnabledLocked()).isTrue();
-        assertThat(callback.receivedEvents()).containsExactly(EVENT_DISPLAY_ADDED);
-
-    }
-
-    @Test
-    public void testConnectExternalDisplay_withDisplayManagement_shouldDisableDisplay() {
-        when(mMockFlags.isConnectedDisplayManagementEnabled()).thenReturn(true);
+    public void testConnectExternalDisplay_shouldDisableDisplay() {
         manageDisplaysPermission(/* granted= */ true);
         DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
         displayManager.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
@@ -2849,9 +2825,8 @@
     }
 
     @Test
-    public void testConnectExternalDisplay_withDisplayManagementAndSysprop_shouldEnableDisplay() {
+    public void testConnectExternalDisplay_withSysprop_shouldEnableDisplay() {
         Assume.assumeTrue(Build.IS_ENG || Build.IS_USERDEBUG);
-        when(mMockFlags.isConnectedDisplayManagementEnabled()).thenReturn(true);
         doAnswer((Answer<Boolean>) invocationOnMock -> true)
                 .when(() -> SystemProperties.getBoolean(ENABLE_ON_CONNECT, false));
         manageDisplaysPermission(/* granted= */ true);
@@ -2883,8 +2858,7 @@
     }
 
     @Test
-    public void testConnectExternalDisplay_withDisplayManagement_allowsEnableAndDisableDisplay() {
-        when(mMockFlags.isConnectedDisplayManagementEnabled()).thenReturn(true);
+    public void testConnectExternalDisplay_allowsEnableAndDisableDisplay() {
         when(mMockFlags.isApplyDisplayChangedDuringDisplayAddedEnabled()).thenReturn(true);
         manageDisplaysPermission(/* granted= */ true);
         LocalServices.addService(WindowManagerPolicy.class, mMockedWindowManagerPolicy);
@@ -2955,8 +2929,7 @@
     }
 
     @Test
-    public void testConnectInternalDisplay_withDisplayManagement_shouldConnectAndAddDisplay() {
-        when(mMockFlags.isConnectedDisplayManagementEnabled()).thenReturn(true);
+    public void testConnectInternalDisplay_shouldConnectAndAddDisplay() {
         manageDisplaysPermission(/* granted= */ true);
         DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
         DisplayManagerService.BinderService bs = displayManager.new BinderService();
@@ -3011,7 +2984,7 @@
         DisplayManagerService.BinderService bs = displayManager.new BinderService();
         LogicalDisplayMapper logicalDisplayMapper = displayManager.getLogicalDisplayMapper();
         FakeDisplayManagerCallback callback = new FakeDisplayManagerCallback();
-        bs.registerCallbackWithEventMask(callback, STANDARD_AND_CONNECTION_DISPLAY_EVENTS);
+        bs.registerCallbackWithEventMask(callback, STANDARD_DISPLAY_EVENTS);
 
         callback.expectsEvent(EVENT_DISPLAY_ADDED);
         FakeDisplayDevice displayDevice =
@@ -3032,8 +3005,7 @@
     }
 
     @Test
-    public void testEnableExternalDisplay_withDisplayManagement_shouldSignalDisplayAdded() {
-        when(mMockFlags.isConnectedDisplayManagementEnabled()).thenReturn(true);
+    public void testEnableExternalDisplay_shouldSignalDisplayAdded() {
         manageDisplaysPermission(/* granted= */ true);
         DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
         displayManager.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
@@ -3062,8 +3034,7 @@
     }
 
     @Test
-    public void testEnableExternalDisplay_withoutPermission_shouldThrowException() {
-        when(mMockFlags.isConnectedDisplayManagementEnabled()).thenReturn(true);
+    public void testEnableExternalDisplay_shouldThrowException() {
         DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
         DisplayManagerService.BinderService bs = displayManager.new BinderService();
         LogicalDisplayMapper logicalDisplayMapper = displayManager.getLogicalDisplayMapper();
@@ -3087,8 +3058,7 @@
     }
 
     @Test
-    public void testEnableInternalDisplay_withManageDisplays_shouldSignalAdded() {
-        when(mMockFlags.isConnectedDisplayManagementEnabled()).thenReturn(true);
+    public void testEnableInternalDisplay_shouldSignalAdded() {
         DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
         DisplayManagerService.BinderService bs = displayManager.new BinderService();
         LogicalDisplayMapper logicalDisplayMapper = displayManager.getLogicalDisplayMapper();
@@ -3115,8 +3085,7 @@
     }
 
     @Test
-    public void testDisableInternalDisplay_withDisplayManagement_shouldSignalRemove() {
-        when(mMockFlags.isConnectedDisplayManagementEnabled()).thenReturn(true);
+    public void testDisableInternalDisplay_shouldSignalRemove() {
         DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
         DisplayManagerService.BinderService bs = displayManager.new BinderService();
         LogicalDisplayMapper logicalDisplayMapper = displayManager.getLogicalDisplayMapper();
@@ -3140,7 +3109,6 @@
 
     @Test
     public void testDisableExternalDisplay_shouldSignalDisplayRemoved() {
-        when(mMockFlags.isConnectedDisplayManagementEnabled()).thenReturn(true);
         DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
         DisplayManagerService.BinderService bs = displayManager.new BinderService();
         LogicalDisplayMapper logicalDisplayMapper = displayManager.getLogicalDisplayMapper();
@@ -3181,7 +3149,6 @@
 
     @Test
     public void testDisableExternalDisplay_withoutPermission_shouldThrowException() {
-        when(mMockFlags.isConnectedDisplayManagementEnabled()).thenReturn(true);
         DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
         DisplayManagerService.BinderService bs = displayManager.new BinderService();
         LogicalDisplayMapper logicalDisplayMapper = displayManager.getLogicalDisplayMapper();
@@ -3207,7 +3174,6 @@
 
     @Test
     public void testRemoveExternalDisplay_whenDisabled_shouldSignalDisconnected() {
-        when(mMockFlags.isConnectedDisplayManagementEnabled()).thenReturn(true);
         manageDisplaysPermission(/* granted= */ true);
         DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
         displayManager.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
@@ -3244,7 +3210,6 @@
 
     @Test
     public void testRegisterCallback_withoutPermission_shouldThrow() {
-        when(mMockFlags.isConnectedDisplayManagementEnabled()).thenReturn(true);
         DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
         DisplayManagerService.BinderService bs = displayManager.new BinderService();
         FakeDisplayManagerCallback callback = new FakeDisplayManagerCallback();
@@ -3255,7 +3220,6 @@
 
     @Test
     public void testRemoveExternalDisplay_whenEnabled_shouldSignalRemovedAndDisconnected() {
-        when(mMockFlags.isConnectedDisplayManagementEnabled()).thenReturn(true);
         manageDisplaysPermission(/* granted= */ true);
         DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
         displayManager.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
@@ -3288,7 +3252,6 @@
 
     @Test
     public void testRemoveInternalDisplay_whenEnabled_shouldSignalRemovedAndDisconnected() {
-        when(mMockFlags.isConnectedDisplayManagementEnabled()).thenReturn(true);
         manageDisplaysPermission(/* granted= */ true);
         DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector);
         DisplayManagerService.BinderService bs = displayManager.new BinderService();
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 7f12e9c..aed1f98 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -85,6 +85,7 @@
 import com.android.server.display.brightness.BrightnessEvent;
 import com.android.server.display.brightness.BrightnessReason;
 import com.android.server.display.brightness.clamper.BrightnessClamperController;
+import com.android.server.display.brightness.clamper.BrightnessClamperController.DisplayDeviceData;
 import com.android.server.display.brightness.clamper.HdrClamper;
 import com.android.server.display.color.ColorDisplayService;
 import com.android.server.display.config.HighBrightnessModeData;
@@ -1288,7 +1289,7 @@
                 any(HysteresisLevels.class),
                 eq(mContext),
                 any(BrightnessRangeController.class),
-                any(BrightnessThrottler.class),
+                any(BrightnessClamperController.class),
                 /* ambientLightHorizonShort= */ anyInt(),
                 /* ambientLightHorizonLong= */ anyInt(),
                 eq(lux),
@@ -1299,8 +1300,9 @@
 
     @Test
     public void testUpdateBrightnessThrottlingDataId() {
+        String throttlingDataId = "throttling-data-id";
         mHolder.display.getDisplayInfoLocked().thermalBrightnessThrottlingDataId =
-                "throttling-data-id";
+                throttlingDataId;
         clearInvocations(mHolder.display.getPrimaryDisplayDeviceLocked().getDisplayDeviceConfig());
 
         mHolder.dpc.onDisplayChanged(mHolder.hbmMetadata, Layout.NO_LEAD_DISPLAY);
@@ -1308,8 +1310,10 @@
         mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
         advanceTime(1); // Run updatePowerState
 
-        verify(mHolder.display.getPrimaryDisplayDeviceLocked().getDisplayDeviceConfig())
-                .getThermalBrightnessThrottlingDataMapByThrottlingId();
+        ArgumentCaptor<DisplayDeviceData> argumentCaptor = ArgumentCaptor.forClass(
+                DisplayDeviceData.class);
+        verify(mHolder.clamperController).onDisplayChanged(argumentCaptor.capture());
+        assertEquals(throttlingDataId, argumentCaptor.getValue().getThermalThrottlingDataId());
     }
 
     @Test
@@ -2798,7 +2802,7 @@
                 HysteresisLevels ambientBrightnessThresholdsIdle,
                 HysteresisLevels screenBrightnessThresholdsIdle, Context context,
                 BrightnessRangeController brightnessRangeController,
-                BrightnessThrottler brightnessThrottler, int ambientLightHorizonShort,
+                BrightnessClamperController clamperController, int ambientLightHorizonShort,
                 int ambientLightHorizonLong, float userLux, float userNits,
                 DisplayManagerFlags displayManagerFlags) {
             return mAutomaticBrightnessController;
@@ -2842,7 +2846,7 @@
         @Override
         BrightnessClamperController getBrightnessClamperController(Handler handler,
                 BrightnessClamperController.ClamperChangeListener clamperChangeListener,
-                BrightnessClamperController.DisplayDeviceData data, Context context,
+                DisplayDeviceData data, Context context,
                 DisplayManagerFlags flags, SensorManager sensorManager, float currentBrightness) {
             return mClamperController;
         }
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt b/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt
index c65024f8..b09947a 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayTopologyCoordinatorTest.kt
@@ -17,7 +17,7 @@
 package com.android.server.display
 
 import android.hardware.display.DisplayTopology
-import android.util.DisplayMetrics
+import android.hardware.display.DisplayTopology.pxToDp
 import android.view.Display
 import android.view.DisplayInfo
 import com.google.common.truth.Truth.assertThat
@@ -62,10 +62,8 @@
     fun addDisplay() {
         coordinator.onDisplayAdded(displayInfo)
 
-        val widthDp = displayInfo.logicalWidth * (DisplayMetrics.DENSITY_DEFAULT.toFloat()
-                / displayInfo.logicalDensityDpi)
-        val heightDp = displayInfo.logicalHeight * (DisplayMetrics.DENSITY_DEFAULT.toFloat()
-                / displayInfo.logicalDensityDpi)
+        val widthDp = pxToDp(displayInfo.logicalWidth.toFloat(), displayInfo.logicalDensityDpi)
+        val heightDp = pxToDp(displayInfo.logicalHeight.toFloat(), displayInfo.logicalDensityDpi)
         verify(mockTopology).addDisplay(displayInfo.displayId, widthDp, heightDp)
         verify(mockTopologyChangedCallback).invoke(mockTopologyCopy)
     }
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 782262d..a48a88c 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/ExternalDisplayPolicyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/ExternalDisplayPolicyTest.java
@@ -22,7 +22,6 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import static org.junit.Assume.assumeFalse;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -47,7 +46,6 @@
 import com.android.server.display.notifications.DisplayNotificationManager;
 import com.android.server.testutils.TestHandler;
 
-import com.google.testing.junit.testparameterinjector.TestParameter;
 import com.google.testing.junit.testparameterinjector.TestParameterInjector;
 
 import org.junit.Before;
@@ -124,7 +122,6 @@
     public void setup() throws Exception {
         MockitoAnnotations.initMocks(this);
         mHandler = new TestHandler(/*callback=*/ null);
-        when(mMockedFlags.isConnectedDisplayManagementEnabled()).thenReturn(true);
         when(mMockedFlags.isConnectedDisplayErrorHandlingEnabled()).thenReturn(true);
         when(mMockedInjector.getFlags()).thenReturn(mMockedFlags);
         when(mMockedInjector.getLogicalDisplayMapper()).thenReturn(mMockedLogicalDisplayMapper);
@@ -173,16 +170,6 @@
     }
 
     @Test
-    public void testTryEnableExternalDisplay_featureDisabled(@TestParameter final boolean enable) {
-        when(mMockedFlags.isConnectedDisplayManagementEnabled()).thenReturn(false);
-        mExternalDisplayPolicy.setExternalDisplayEnabledLocked(mMockedLogicalDisplay, enable);
-        mHandler.flush();
-        verify(mMockedLogicalDisplayMapper, never()).setDisplayEnabledLocked(any(), anyBoolean());
-        verify(mMockedDisplayNotificationManager, never())
-                .onHighTemperatureExternalDisplayNotAllowed();
-    }
-
-    @Test
     public void testTryDisableExternalDisplay_criticalThermalCondition() throws RemoteException {
         // Disallow external displays due to thermals.
         setTemperature(registerThermalListener(), List.of(CRITICAL_TEMPERATURE));
@@ -278,21 +265,6 @@
     }
 
     @Test
-    public void testNoThermalListenerRegistered_featureDisabled(
-            @TestParameter final boolean isConnectedDisplayManagementEnabled,
-            @TestParameter final boolean isErrorHandlingEnabled) throws RemoteException {
-        assumeFalse(isConnectedDisplayManagementEnabled && isErrorHandlingEnabled);
-        when(mMockedFlags.isConnectedDisplayManagementEnabled()).thenReturn(
-                isConnectedDisplayManagementEnabled);
-        when(mMockedFlags.isConnectedDisplayErrorHandlingEnabled()).thenReturn(
-                isErrorHandlingEnabled);
-
-        mExternalDisplayPolicy.onBootCompleted();
-        verify(mMockedThermalService, never()).registerThermalEventListenerWithType(
-                any(), anyInt());
-    }
-
-    @Test
     public void testOnCriticalTemperature_disallowAndAllowExternalDisplay() throws RemoteException {
         final var thermalListener = registerThermalListener();
 
diff --git a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java
index 0dbb6ba..7d3cd8a 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java
@@ -222,7 +222,6 @@
         when(mSyntheticModeManagerMock.createAppSupportedModes(any(), any(), anyBoolean()))
                 .thenAnswer(AdditionalAnswers.returnsSecondArg());
 
-        when(mFlagsMock.isConnectedDisplayManagementEnabled()).thenReturn(false);
         mLooper = new TestLooper();
         mHandler = new Handler(mLooper.getLooper());
         mLogicalDisplayMapper = new LogicalDisplayMapper(mContextMock, mFoldSettingProviderMock,
@@ -351,8 +350,7 @@
     }
 
     @Test
-    public void testDisplayDeviceAddAndRemove_withDisplayManagement() {
-        when(mFlagsMock.isConnectedDisplayManagementEnabled()).thenReturn(true);
+    public void testDisplayDeviceAddAndRemove() {
         DisplayDevice device = createDisplayDevice(TYPE_INTERNAL, 600, 800,
                 FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY);
 
@@ -390,8 +388,7 @@
     }
 
     @Test
-    public void testDisplayDisableEnable_withDisplayManagement() {
-        when(mFlagsMock.isConnectedDisplayManagementEnabled()).thenReturn(true);
+    public void testDisplayDisableEnable() {
         DisplayDevice device = createDisplayDevice(TYPE_INTERNAL, 600, 800,
                 FLAG_ALLOWED_TO_BE_DEFAULT_DISPLAY);
         LogicalDisplay displayAdded = add(device);
@@ -1350,9 +1347,14 @@
         ArgumentCaptor<LogicalDisplay> displayCaptor =
                 ArgumentCaptor.forClass(LogicalDisplay.class);
         verify(mListenerMock).onLogicalDisplayEventLocked(
-                displayCaptor.capture(), eq(LOGICAL_DISPLAY_EVENT_ADDED));
+                displayCaptor.capture(), eq(LOGICAL_DISPLAY_EVENT_CONNECTED));
+        LogicalDisplay display = displayCaptor.getValue();
+        if (display.isEnabledLocked()) {
+            verify(mListenerMock).onLogicalDisplayEventLocked(
+                    eq(display), eq(LOGICAL_DISPLAY_EVENT_ADDED));
+        }
         clearInvocations(mListenerMock);
-        return displayCaptor.getValue();
+        return display;
     }
 
     private void testDisplayDeviceAddAndRemove_NonInternal(int type) {
diff --git a/services/tests/mockingservicestests/Android.bp b/services/tests/mockingservicestests/Android.bp
index 0d25426..f37ca7c 100644
--- a/services/tests/mockingservicestests/Android.bp
+++ b/services/tests/mockingservicestests/Android.bp
@@ -238,7 +238,7 @@
     include_filters: [
         "com.android.server.am.BroadcastQueueTest",
         "com.android.server.am.BroadcastRecordTest",
-        "com.android.server.am.BroadcastQueueModernImplTest",
+        "com.android.server.am.BroadcastQueueImplTest",
     ],
 }
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueImplTest.java
similarity index 99%
rename from services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
rename to services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueImplTest.java
index 82237bc..1e665c2 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueImplTest.java
@@ -96,8 +96,8 @@
 import java.util.Objects;
 
 @SmallTest
-public final class BroadcastQueueModernImplTest extends BaseBroadcastQueueTest {
-    private static final String TAG = "BroadcastQueueModernImplTest";
+public final class BroadcastQueueImplTest extends BaseBroadcastQueueTest {
+    private static final String TAG = "BroadcastQueueImplTest";
 
     private static final int TEST_UID = android.os.Process.FIRST_APPLICATION_UID;
     private static final int TEST_UID2 = android.os.Process.FIRST_APPLICATION_UID + 1;
@@ -109,7 +109,7 @@
     @Mock BroadcastProcessQueue mQueue3;
     @Mock BroadcastProcessQueue mQueue4;
 
-    BroadcastQueueModernImpl mImpl;
+    BroadcastQueueImpl mImpl;
 
     BroadcastProcessQueue mHead;
 
@@ -121,7 +121,7 @@
         mConstants.DELAY_NORMAL_MILLIS = 10_000;
         mConstants.DELAY_CACHED_MILLIS = 120_000;
 
-        mImpl = new BroadcastQueueModernImpl(mAms, mHandlerThread.getThreadHandler(),
+        mImpl = new BroadcastQueueImpl(mAms, mHandlerThread.getThreadHandler(),
                 mConstants, mConstants, mSkipPolicy, mEmptyHistory);
         mAms.setBroadcastQueueForTest(mImpl);
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
index ea80f28..ad35b25 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
@@ -233,7 +233,7 @@
         }).when(mAms).registerUidObserver(any(), anyInt(),
                 eq(ActivityManager.PROCESS_STATE_TOP), any());
 
-        mQueue = new BroadcastQueueModernImpl(mAms, mHandlerThread.getThreadHandler(),
+        mQueue = new BroadcastQueueImpl(mAms, mHandlerThread.getThreadHandler(),
                 mConstants, mConstants, mSkipPolicy, mEmptyHistory);
         mAms.setBroadcastQueueForTest(mQueue);
         mQueue.start(mContext.getContentResolver());
@@ -454,7 +454,7 @@
 
     private void assertHealth() {
         // If this fails, it'll throw a clear reason message
-        ((BroadcastQueueModernImpl) mQueue).assertHealthLocked();
+        ((BroadcastQueueImpl) mQueue).assertHealthLocked();
     }
 
     private static Map<String, Object> asMap(Bundle bundle) {
@@ -602,7 +602,7 @@
         mQueue.dumpDebug(new ProtoOutputStream(),
                 ActivityManagerServiceDumpBroadcastsProto.BROADCAST_QUEUE);
         mQueue.dumpLocked(FileDescriptor.err, new PrintWriter(Writer.nullWriter()),
-                null, 0, true, true, true, null, false);
+                null, 0, true, true, true, null, null, false);
         mQueue.dumpToDropBoxLocked(TAG);
 
         BroadcastQueue.logv(TAG);
@@ -1019,7 +1019,7 @@
             mQueue.dumpDebug(new ProtoOutputStream(),
                     ActivityManagerServiceDumpBroadcastsProto.BROADCAST_QUEUE);
             mQueue.dumpLocked(FileDescriptor.err, new PrintWriter(Writer.nullWriter()),
-                    null, 0, true, true, true, null, false);
+                    null, 0, true, true, true, null, null, false);
         }
 
         waitForIdle();
@@ -1953,7 +1953,7 @@
                 withPriority(receiverGreenA, 5))));
 
         waitForIdle();
-        // In the modern queue, we don't end up replacing the old broadcast to
+        // In the broadcast queue, we don't end up replacing the old broadcast to
         // avoid creating priority inversion and so the process will receive
         // both the old and new broadcasts.
         verifyScheduleRegisteredReceiver(times(3), receiverGreenApp, airplane);
@@ -2235,7 +2235,7 @@
         }
         waitForIdle();
 
-        // Modern stack requests once each time we promote a process to
+        // The broadcast queue requests once each time we promote a process to
         // running; we promote "green" twice, and "blue" and "yellow" once
         final int expectedTimes = 4;
         verify(mAms, times(expectedTimes))
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 9e96800..4a09802 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -258,7 +258,6 @@
         mService.mOomAdjuster = mService.mProcessStateController.getOomAdjuster();
         mService.mOomAdjuster.mAdjSeq = 10000;
         mService.mWakefulness = new AtomicInteger(PowerManagerInternal.WAKEFULNESS_AWAKE);
-        mSetFlagsRule.enableFlags(Flags.FLAG_NEW_FGS_RESTRICTION_LOGIC);
 
         mUiTierSize = mService.mConstants.TIERED_CACHED_ADJ_UI_TIER_SIZE;
         mFirstNonUiCachedAdj = sFirstUiCachedAdj + mUiTierSize;
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/PendingIntentControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/am/PendingIntentControllerTest.java
index 89b48ba..27eada0 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/PendingIntentControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/PendingIntentControllerTest.java
@@ -16,6 +16,8 @@
 
 package com.android.server.am;
 
+import static android.os.PowerWhitelistManager.REASON_NOTIFICATION_SERVICE;
+import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED;
 import static android.os.Process.INVALID_UID;
 
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -27,9 +29,11 @@
 import static com.android.server.am.PendingIntentRecord.CANCEL_REASON_OWNER_FORCE_STOPPED;
 import static com.android.server.am.PendingIntentRecord.CANCEL_REASON_SUPERSEDED;
 import static com.android.server.am.PendingIntentRecord.CANCEL_REASON_USER_STOPPED;
+import static com.android.server.am.PendingIntentRecord.FLAG_ACTIVITY_SENDER;
 import static com.android.server.am.PendingIntentRecord.cancelReasonToString;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.eq;
@@ -39,9 +43,11 @@
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
 import android.app.AppGlobals;
+import android.app.BackgroundStartPrivileges;
 import android.app.PendingIntent;
 import android.content.Intent;
 import android.content.pm.IPackageManager;
+import android.os.Binder;
 import android.os.Looper;
 import android.os.UserHandle;
 
@@ -179,6 +185,34 @@
         }
     }
 
+    @Test
+    public void testClearAllowBgActivityStartsClearsToken() {
+        final PendingIntentRecord pir = createPendingIntentRecord(0);
+        Binder token = new Binder();
+        pir.setAllowBgActivityStarts(token, FLAG_ACTIVITY_SENDER);
+        assertEquals(BackgroundStartPrivileges.allowBackgroundActivityStarts(token),
+                pir.getBackgroundStartPrivilegesForActivitySender(token));
+        pir.clearAllowBgActivityStarts(token);
+        assertEquals(BackgroundStartPrivileges.NONE,
+                pir.getBackgroundStartPrivilegesForActivitySender(token));
+    }
+
+    @Test
+    public void testClearAllowBgActivityStartsClearsDuration() {
+        final PendingIntentRecord pir = createPendingIntentRecord(0);
+        Binder token = new Binder();
+        pir.setAllowlistDurationLocked(token, 1000,
+                TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED, REASON_NOTIFICATION_SERVICE,
+                "NotificationManagerService");
+        PendingIntentRecord.TempAllowListDuration allowlistDurationLocked =
+                pir.getAllowlistDurationLocked(token);
+        assertEquals(1000, allowlistDurationLocked.duration);
+        pir.clearAllowBgActivityStarts(token);
+        PendingIntentRecord.TempAllowListDuration allowlistDurationLockedAfterClear =
+                pir.getAllowlistDurationLocked(token);
+        assertNull(allowlistDurationLockedAfterClear);
+    }
+
     private void assertCancelReason(int expectedReason, int actualReason) {
         final String errMsg = "Expected: " + cancelReasonToString(expectedReason)
                 + "; Actual: " + cancelReasonToString(actualReason);
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/fudger/LocationFudgerCacheTest.java b/services/tests/mockingservicestests/src/com/android/server/location/fudger/LocationFudgerCacheTest.java
index c89048a..c3706c3 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/fudger/LocationFudgerCacheTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/fudger/LocationFudgerCacheTest.java
@@ -281,7 +281,7 @@
     }
 
     @Test
-    public void fetchDefaultCoarseningLevelIfNeeded_withDefaultValue_doesNotQueryProvider()
+    public void onDefaultCoarseningLevelNotSet_withDefaultValue_doesNotQueryProvider()
             throws RemoteException {
         // Arrange.
         ProxyPopulationDensityProvider provider = mock(ProxyPopulationDensityProvider.class);
@@ -297,14 +297,14 @@
         assertThat(cache.hasDefaultValue()).isTrue();
 
         // Act.
-        cache.fetchDefaultCoarseningLevelIfNeeded();
+        cache.onDefaultCoarseningLevelNotSet();
 
         // Assert. The method is not called again.
         verify(provider, times(1)).getDefaultCoarseningLevel(any());
     }
 
     @Test
-    public void fetchDefaultCoarseningLevelIfNeeded_withoutDefaultValue_doesQueryProvider()
+    public void onDefaultCoarseningLevelNotSet_withoutDefaultValue_doesQueryProvider()
             throws RemoteException {
         // Arrange.
         ProxyPopulationDensityProvider provider = mock(ProxyPopulationDensityProvider.class);
@@ -320,7 +320,7 @@
         assertThat(cache.hasDefaultValue()).isFalse();
 
         // Act.
-        cache.fetchDefaultCoarseningLevelIfNeeded();
+        cache.onDefaultCoarseningLevelNotSet();
 
         // Assert. The method is called again.
         verify(provider, times(2)).getDefaultCoarseningLevel(any());
@@ -383,4 +383,56 @@
         assertThat(cache.getCoarseningLevel(latlngs[size - 1][0], latlngs[size - 1][1]))
                 .isEqualTo(0);
     }
+
+    @Test
+    public void logDensityBasedLocsUsed_rateLimitsTheSecondCall() {
+        // To avoid having to mock the logger, logDensityBasedLocsUsed returns a boolean indicating
+        // if the log was successful or rate-limited.
+
+        ProxyPopulationDensityProvider provider = mock(ProxyPopulationDensityProvider.class);
+        LocationFudgerCache cache = new LocationFudgerCache(provider);
+        boolean skippedNoDefault = false;
+        boolean isCacheHit = false;
+        int defaultCoarseningLevel = 3;
+        long time1 = 0;
+        // 7 min later. Can be any value < time1 + LOG_DENSITY_BASED_LOCS_USED_RATE_LIMIT_MS
+        long time2 = time1 + 7 * 60 * 1000;
+
+        boolean success1 = cache.logDensityBasedLocsUsed(time1, skippedNoDefault, isCacheHit,
+                defaultCoarseningLevel);
+        boolean success2 = cache.logDensityBasedLocsUsed(time2, skippedNoDefault, isCacheHit,
+                defaultCoarseningLevel);
+
+        assertThat(success1).isTrue();  // log OK
+        assertThat(success2).isFalse();  // dropped
+    }
+
+    @Test
+    public void logDensityBasedLocsUsed_rateLimitOf3rdCall_isNotAffectedByDropped2ndCall() {
+        // To avoid having to mock the logger, logDensityBasedLocsUsed returns a boolean indicating
+        // if the log was successful or rate-limited.
+
+        ProxyPopulationDensityProvider provider = mock(ProxyPopulationDensityProvider.class);
+        LocationFudgerCache cache = new LocationFudgerCache(provider);
+        boolean skippedNoDefault = false;
+        boolean isCacheHit = false;
+        int defaultCoarseningLevel = 3;
+        long time1 = 0;
+        // 7 min later. Can be any value < time1 + LOG_DENSITY_BASED_LOCS_USED_RATE_LIMIT_MS
+        long time2 = time1 + 7 * 60 * 1000;
+        // 11 min later. Can be any value >= time1 + LOG_DENSITY_BASED_LOCS_USED_RATE_LIMIT_MS
+        long time3 = time1 + 11 * 60 * 1000;
+
+        boolean success1 = cache.logDensityBasedLocsUsed(time1, skippedNoDefault, isCacheHit,
+                defaultCoarseningLevel);
+        boolean success2 = cache.logDensityBasedLocsUsed(time2, skippedNoDefault, isCacheHit,
+                defaultCoarseningLevel);
+        boolean success3 = cache.logDensityBasedLocsUsed(time3, skippedNoDefault, isCacheHit,
+                defaultCoarseningLevel);
+
+        assertThat(success1).isTrue();  // log OK
+        assertThat(success2).isFalse();  // dropped
+        assertThat(success3).isTrue();  // log OK
+    }
+
 }
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/fudger/LocationFudgerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/fudger/LocationFudgerTest.java
index 2e4652e..f442eb6 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/fudger/LocationFudgerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/fudger/LocationFudgerTest.java
@@ -232,7 +232,7 @@
 
         mFudger.createCoarse(createLocation("test", mRandom));
 
-        verify(cache).fetchDefaultCoarseningLevelIfNeeded();
+        verify(cache).onDefaultCoarseningLevelNotSet();
     }
 
     @Test
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 bd15bd0..cd94c0f 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
@@ -1411,7 +1411,6 @@
         halParams3.tids = tids;
         halParams3.calculationType = CpuHeadroomParams.CalculationType.AVERAGE;
 
-        // this params should not be cached as the window is not default
         CpuHeadroomParamsInternal params4 = new CpuHeadroomParamsInternal();
         params4.calculationWindowMillis = 123;
         CpuHeadroomParams halParams4 = new CpuHeadroomParams();
@@ -1450,11 +1449,7 @@
         assertEquals(halRet2, service.getBinderServiceInstance().getCpuHeadroom(params2));
         assertEquals(halRet3, service.getBinderServiceInstance().getCpuHeadroom(params3));
         assertEquals(halRet4, service.getBinderServiceInstance().getCpuHeadroom(params4));
-        verify(mIPowerMock, times(1)).getCpuHeadroom(any());
-        verify(mIPowerMock, times(0)).getCpuHeadroom(eq(halParams1));
-        verify(mIPowerMock, times(0)).getCpuHeadroom(eq(halParams2));
-        verify(mIPowerMock, times(0)).getCpuHeadroom(eq(halParams3));
-        verify(mIPowerMock, times(1)).getCpuHeadroom(eq(halParams4));
+        verify(mIPowerMock, times(0)).getCpuHeadroom(any());
 
         // after 500ms more it should be served with cache
         Thread.sleep(500);
@@ -1463,11 +1458,7 @@
         assertEquals(halRet2, service.getBinderServiceInstance().getCpuHeadroom(params2));
         assertEquals(halRet3, service.getBinderServiceInstance().getCpuHeadroom(params3));
         assertEquals(halRet4, service.getBinderServiceInstance().getCpuHeadroom(params4));
-        verify(mIPowerMock, times(1)).getCpuHeadroom(any());
-        verify(mIPowerMock, times(0)).getCpuHeadroom(eq(halParams1));
-        verify(mIPowerMock, times(0)).getCpuHeadroom(eq(halParams2));
-        verify(mIPowerMock, times(0)).getCpuHeadroom(eq(halParams3));
-        verify(mIPowerMock, times(1)).getCpuHeadroom(eq(halParams4));
+        verify(mIPowerMock, times(0)).getCpuHeadroom(any());
 
         // after 1+ seconds it should be served from HAL as it exceeds 1000 millis interval
         Thread.sleep(600);
@@ -1574,18 +1565,14 @@
         clearInvocations(mIPowerMock);
         assertEquals(halRet1, service.getBinderServiceInstance().getGpuHeadroom(params1));
         assertEquals(halRet2, service.getBinderServiceInstance().getGpuHeadroom(params2));
-        verify(mIPowerMock, times(1)).getGpuHeadroom(any());
-        verify(mIPowerMock, times(0)).getGpuHeadroom(eq(halParams1));
-        verify(mIPowerMock, times(1)).getGpuHeadroom(eq(halParams2));
+        verify(mIPowerMock, times(0)).getGpuHeadroom(any());
 
         // after 500ms it should be served with cache
         Thread.sleep(500);
         clearInvocations(mIPowerMock);
         assertEquals(halRet1, service.getBinderServiceInstance().getGpuHeadroom(params1));
         assertEquals(halRet2, service.getBinderServiceInstance().getGpuHeadroom(params2));
-        verify(mIPowerMock, times(1)).getGpuHeadroom(any());
-        verify(mIPowerMock, times(0)).getGpuHeadroom(eq(halParams1));
-        verify(mIPowerMock, times(1)).getGpuHeadroom(eq(halParams2));
+        verify(mIPowerMock, times(0)).getGpuHeadroom(any());
 
         // after 1+ seconds it should be served from HAL as it exceeds 1000 millis interval
         Thread.sleep(600);
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java
index bc81feb..164eec6 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java
@@ -34,6 +34,8 @@
 import android.os.PersistableBundle;
 import android.os.Process;
 import android.os.UserHandle;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.telephony.NetworkRegistrationInfo;
 import android.util.AtomicFile;
 import android.util.Log;
@@ -46,6 +48,7 @@
 import com.android.internal.os.PowerStats;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.InOrder;
@@ -61,14 +64,22 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.TimeZone;
 
 /**
  * Test BatteryStatsHistory.
  */
 @RunWith(AndroidJUnit4.class)
+@EnableFlags({com.android.server.power.optimization.Flags
+        .FLAG_EXTENDED_BATTERY_HISTORY_CONTINUOUS_COLLECTION_ENABLED})
 public class BatteryStatsHistoryTest {
     private static final String TAG = "BatteryStatsHistoryTest";
+
     private static final int MAX_HISTORY_BUFFER_SIZE = 1024;
+
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
     private final Parcel mHistoryBuffer = Parcel.obtain();
     private File mSystemDir;
     private File mHistoryDir;
@@ -98,15 +109,18 @@
         mHistoryDir.delete();
 
         mClock.realtime = 123;
+        mClock.currentTime = 1743645660000L;    //  2025-04-03, 2:01:00 AM
 
         mHistory = new BatteryStatsHistory(mHistoryBuffer, mSystemDir, 32768,
                 MAX_HISTORY_BUFFER_SIZE, mStepDetailsCalculator, mClock, mMonotonicClock, mTracer,
                 mEventLogger);
+        mHistory.forceRecordAllHistory();
+        mHistory.startRecordingHistory(mClock.realtime, mClock.uptime, false);
 
         when(mStepDetailsCalculator.getHistoryStepDetails())
                 .thenReturn(new BatteryStats.HistoryStepDetails());
 
-        mHistoryPrinter = new BatteryStats.HistoryPrinter();
+        mHistoryPrinter = new BatteryStats.HistoryPrinter(TimeZone.getTimeZone("GMT"));
     }
 
     @Test
@@ -145,8 +159,6 @@
 
     @Test
     public void testAtraceExcludedState() {
-        mHistory.forceRecordAllHistory();
-
         Mockito.when(mTracer.tracingEnabled()).thenReturn(true);
 
         mHistory.recordStateStartEvent(mClock.elapsedRealtime(),
@@ -354,8 +366,6 @@
     }
 
     private void prepareMultiFileHistory() {
-        mHistory.forceRecordAllHistory();
-
         mClock.realtime = 1000;
         mClock.uptime = 1000;
         mHistory.recordEvent(mClock.realtime, mClock.uptime,
@@ -428,7 +438,8 @@
         powerStats.uidStats.put(300, new long[]{400, 500});
         powerStats.uidStats.put(600, new long[]{700, 800});
 
-        mHistory.recordPowerStats(200, 200, powerStats);
+        mClock.advance(200);
+        mHistory.recordPowerStats(mClock.realtime, mClock.uptime, powerStats);
 
         BatteryStatsHistoryIterator iterator = mHistory.iterate(0, MonotonicClock.UNDEFINED);
         BatteryStats.HistoryItem item;
@@ -437,7 +448,7 @@
         assertThat(item = iterator.next()).isNotNull();
 
         String dump = toString(item, /* checkin */ false);
-        assertThat(dump).contains("+200ms");
+        assertThat(dump).contains("04-03 02:01:00.200");
         assertThat(dump).contains("duration=100");
         assertThat(dump).contains("foo=[200]");
         assertThat(dump).contains("300: [400, 500]");
@@ -446,49 +457,49 @@
 
     @Test
     public void testNrState_dump() {
-        mHistory.forceRecordAllHistory();
-        mHistory.startRecordingHistory(0, 0, /* reset */ true);
         mHistory.setBatteryState(true /* charging */, BatteryManager.BATTERY_STATUS_CHARGING, 80,
                 1234);
 
-        mHistory.recordNrStateChangeEvent(200, 200,
+        mClock.advance(200);
+        mHistory.recordNrStateChangeEvent(mClock.realtime, mClock.uptime,
                 NetworkRegistrationInfo.NR_STATE_RESTRICTED);
-        mHistory.recordNrStateChangeEvent(300, 300,
+        mClock.advance(100);
+        mHistory.recordNrStateChangeEvent(mClock.realtime, mClock.uptime,
                 NetworkRegistrationInfo.NR_STATE_NOT_RESTRICTED);
-        mHistory.recordNrStateChangeEvent(400, 400,
+        mClock.advance(100);
+        mHistory.recordNrStateChangeEvent(mClock.realtime, mClock.uptime,
                 NetworkRegistrationInfo.NR_STATE_CONNECTED);
-        mHistory.recordNrStateChangeEvent(500, 500,
+        mClock.advance(100);
+        mHistory.recordNrStateChangeEvent(mClock.realtime, mClock.uptime,
                 NetworkRegistrationInfo.NR_STATE_NONE);
 
         BatteryStatsHistoryIterator iterator = mHistory.iterate(0, MonotonicClock.UNDEFINED);
-        BatteryStats.HistoryItem item = new BatteryStats.HistoryItem();
+        BatteryStats.HistoryItem item;
         assertThat(item = iterator.next()).isNotNull(); // First item contains current time only
 
         assertThat(item = iterator.next()).isNotNull();
         String dump = toString(item, /* checkin */ false);
-        assertThat(dump).contains("+200ms");
+        assertThat(dump).contains("04-03 02:01:00.200");
         assertThat(dump).contains("nr_state=restricted");
 
         assertThat(item = iterator.next()).isNotNull();
         dump = toString(item, /* checkin */ false);
-        assertThat(dump).contains("+300ms");
+        assertThat(dump).contains("04-03 02:01:00.300");
         assertThat(dump).contains("nr_state=not_restricted");
 
         assertThat(item = iterator.next()).isNotNull();
         dump = toString(item, /* checkin */ false);
-        assertThat(dump).contains("+400ms");
+        assertThat(dump).contains("04-03 02:01:00.400");
         assertThat(dump).contains("nr_state=connected");
 
         assertThat(item = iterator.next()).isNotNull();
         dump = toString(item, /* checkin */ false);
-        assertThat(dump).contains("+500ms");
+        assertThat(dump).contains("04-03 02:01:00.500");
         assertThat(dump).contains("nr_state=none");
     }
 
     @Test
     public void testNrState_checkin() {
-        mHistory.forceRecordAllHistory();
-        mHistory.startRecordingHistory(0, 0, /* reset */ true);
         mHistory.setBatteryState(true /* charging */, BatteryManager.BATTERY_STATUS_CHARGING, 80,
                 1234);
 
@@ -502,7 +513,7 @@
                 NetworkRegistrationInfo.NR_STATE_NONE);
 
         BatteryStatsHistoryIterator iterator = mHistory.iterate(0, MonotonicClock.UNDEFINED);
-        BatteryStats.HistoryItem item = new BatteryStats.HistoryItem();
+        BatteryStats.HistoryItem item;
         assertThat(item = iterator.next()).isNotNull(); // First item contains current time only
 
         assertThat(item = iterator.next()).isNotNull();
@@ -633,10 +644,17 @@
 
     @Test
     public void recordProcStateChange() {
-        mHistory.recordProcessStateChange(200, 200, 42, BatteryConsumer.PROCESS_STATE_BACKGROUND);
-        mHistory.recordProcessStateChange(300, 300, 42, BatteryConsumer.PROCESS_STATE_FOREGROUND);
+        mClock.advance(200);
+        mHistory.recordProcessStateChange(mClock.realtime, mClock.uptime, 42,
+                BatteryConsumer.PROCESS_STATE_BACKGROUND);
+
+        mClock.advance(100);
+        mHistory.recordProcessStateChange(mClock.realtime, mClock.uptime, 42,
+                BatteryConsumer.PROCESS_STATE_FOREGROUND);
+
+        mClock.advance(100);
         // Large UID, > 0xFFFFFF
-        mHistory.recordProcessStateChange(400, 400,
+        mHistory.recordProcessStateChange(mClock.realtime, mClock.uptime,
                 UserHandle.getUid(777, Process.LAST_ISOLATED_UID),
                 BatteryConsumer.PROCESS_STATE_FOREGROUND_SERVICE);
 
@@ -647,17 +665,17 @@
         assertThat(item = iterator.next()).isNotNull();
 
         String dump = toString(item, /* checkin */ false);
-        assertThat(dump).contains("+200ms");
+        assertThat(dump).contains("04-03 02:01:00.200");
         assertThat(dump).contains("procstate: 42: bg");
 
         assertThat(item = iterator.next()).isNotNull();
         dump = toString(item, /* checkin */ false);
-        assertThat(dump).contains("+300ms");
+        assertThat(dump).contains("04-03 02:01:00.300");
         assertThat(dump).contains("procstate: 42: fg");
 
         assertThat(item = iterator.next()).isNotNull();
         dump = toString(item, /* checkin */ false);
-        assertThat(dump).contains("+400ms");
+        assertThat(dump).contains("04-03 02:01:00.400");
         assertThat(dump).contains("procstate: u777i999: fgs");
     }
 
@@ -672,7 +690,6 @@
     @Test
     public void getMonotonicHistorySize() {
         long lastHistorySize = mHistory.getMonotonicHistorySize();
-        mHistory.forceRecordAllHistory();
 
         mClock.realtime = 1000;
         mClock.uptime = 1000;
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 00b911b..cd3683b 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
@@ -158,6 +158,11 @@
         public LongSupplier getPhoneSignalScanDurationSupplier() {
             return mScanDurationSupplier;
         }
+
+        @Override
+        public NetworkStats networkStatsDelta(NetworkStats stats, NetworkStats oldStats) {
+            return NetworkStatsTestUtils.networkStatsDelta(stats, oldStats);
+        }
     };
 
     @Before
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java
index 4b6fcc3..8a081f8 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MockBatteryStatsImpl.java
@@ -283,6 +283,11 @@
     protected void updateBatteryPropertiesLocked() {
     }
 
+    @Override
+    protected NetworkStats networkStatsDelta(NetworkStats stats, NetworkStats oldStats) {
+        return NetworkStatsTestUtils.networkStatsDelta(stats, oldStats);
+    }
+
     public static class DummyExternalStatsSync implements ExternalStatsSync {
         public int flags = 0;
 
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/MockClock.java b/services/tests/powerstatstests/src/com/android/server/power/stats/MockClock.java
index 5e57cc3..215ac40 100644
--- a/services/tests/powerstatstests/src/com/android/server/power/stats/MockClock.java
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/MockClock.java
@@ -40,4 +40,13 @@
     public long currentTimeMillis() {
         return currentTime;
     }
+
+    /**
+     * Advances the clock by the given number of milliseconds.
+     */
+    public void advance(long milliseconds) {
+        realtime += milliseconds;
+        uptime += milliseconds;
+        currentTime += milliseconds;
+    }
 }
diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/NetworkStatsTestUtils.java b/services/tests/powerstatstests/src/com/android/server/power/stats/NetworkStatsTestUtils.java
new file mode 100644
index 0000000..21be654
--- /dev/null
+++ b/services/tests/powerstatstests/src/com/android/server/power/stats/NetworkStatsTestUtils.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.power.stats;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.NetworkStats;
+import android.platform.test.ravenwood.RavenwoodRule;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class NetworkStatsTestUtils {
+    /**
+     * Equivalent to NetworkStats.subtract, reimplementing the method for Ravenwood tests.
+     */
+    @NonNull
+    public static NetworkStats networkStatsDelta(@NonNull NetworkStats currentStats,
+            @Nullable NetworkStats lastStats) {
+        if (!RavenwoodRule.isOnRavenwood()) {
+            if (lastStats == null) {
+                return currentStats;
+            }
+            return currentStats.subtract(lastStats);
+        }
+
+        List<NetworkStats.Entry> entries = new ArrayList<>();
+        for (NetworkStats.Entry entry : currentStats) {
+            NetworkStats.Entry lastEntry = null;
+            int uid = entry.getUid();
+            if (lastStats != null) {
+                for (NetworkStats.Entry e : lastStats) {
+                    if (e.getUid() == uid && e.getSet() == entry.getSet()
+                            && e.getTag() == entry.getTag()
+                            && e.getMetered() == entry.getMetered()
+                            && e.getRoaming() == entry.getRoaming()
+                            && e.getDefaultNetwork() == entry.getDefaultNetwork()
+                        /*&& Objects.equals(e.getIface(), entry.getIface())*/) {
+                        lastEntry = e;
+                        break;
+                    }
+                }
+            }
+            long rxBytes, rxPackets, txBytes, txPackets;
+            if (lastEntry != null) {
+                rxBytes = Math.max(0, entry.getRxBytes() - lastEntry.getRxBytes());
+                rxPackets = Math.max(0, entry.getRxPackets() - lastEntry.getRxPackets());
+                txBytes = Math.max(0, entry.getTxBytes() - lastEntry.getTxBytes());
+                txPackets = Math.max(0, entry.getTxPackets() - lastEntry.getTxPackets());
+            } else {
+                rxBytes = entry.getRxBytes();
+                rxPackets = entry.getRxPackets();
+                txBytes = entry.getTxBytes();
+                txPackets = entry.getTxPackets();
+            }
+
+            NetworkStats.Entry uidEntry = mock(NetworkStats.Entry.class);
+            when(uidEntry.getUid()).thenReturn(uid);
+            when(uidEntry.getRxBytes()).thenReturn(rxBytes);
+            when(uidEntry.getRxPackets()).thenReturn(rxPackets);
+            when(uidEntry.getTxBytes()).thenReturn(txBytes);
+            when(uidEntry.getTxPackets()).thenReturn(txPackets);
+
+            entries.add(uidEntry);
+        }
+        NetworkStats delta = mock(NetworkStats.class);
+        when(delta.iterator()).thenAnswer(inv -> entries.iterator());
+        return delta;
+    }
+}
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 8b5e6ee..a26b2c9 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
@@ -168,6 +168,11 @@
         public WifiManager getWifiManager() {
             return mWifiManager;
         }
+
+        @Override
+        public NetworkStats networkStatsDelta(NetworkStats stats, NetworkStats oldStats) {
+            return NetworkStatsTestUtils.networkStatsDelta(stats, oldStats);
+        }
     };
 
     @Before
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
index 4ed44a0..6acd368 100644
--- 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
@@ -56,6 +56,7 @@
 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.NetworkStatsTestUtils;
 import com.android.server.power.stats.PowerStatsCollector;
 import com.android.server.power.stats.PowerStatsUidResolver;
 import com.android.server.power.stats.format.MobileRadioPowerStatsLayout;
@@ -152,6 +153,11 @@
                 public LongSupplier getPhoneSignalScanDurationSupplier() {
                     return mScanDurationSupplier;
                 }
+
+                @Override
+                public NetworkStats networkStatsDelta(NetworkStats stats, NetworkStats oldStats) {
+                    return NetworkStatsTestUtils.networkStatsDelta(stats, oldStats);
+                }
             };
 
     @Before
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
index 535f2da..a20274f 100644
--- 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
@@ -43,6 +43,7 @@
 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.NetworkStatsTestUtils;
 import com.android.server.power.stats.PowerStatsCollector;
 import com.android.server.power.stats.PowerStatsUidResolver;
 import com.android.server.power.stats.format.PowerStatsLayout;
@@ -135,6 +136,11 @@
                 public LongSupplier getPhoneSignalScanDurationSupplier() {
                     return mScanDurationSupplier;
                 }
+
+                @Override
+                public NetworkStats networkStatsDelta(NetworkStats stats, NetworkStats oldStats) {
+                    return NetworkStatsTestUtils.networkStatsDelta(stats, oldStats);
+                }
             };
 
     @Before
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
index 1e09769..bd92a84 100644
--- 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
@@ -56,6 +56,7 @@
 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.NetworkStatsTestUtils;
 import com.android.server.power.stats.PowerStatsCollector;
 import com.android.server.power.stats.PowerStatsUidResolver;
 import com.android.server.power.stats.WifiPowerStatsCollector;
@@ -178,6 +179,11 @@
                 public WifiStatsRetriever getWifiStatsRetriever() {
                     return mWifiStatsRetriever;
                 }
+
+                @Override
+                public NetworkStats networkStatsDelta(NetworkStats stats, NetworkStats oldStats) {
+                    return NetworkStatsTestUtils.networkStatsDelta(stats, oldStats);
+                }
             };
 
     @Before
diff --git a/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java b/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
index 45761b6..92c6db5 100644
--- a/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/GestureLauncherServiceTest.java
@@ -17,8 +17,13 @@
 package com.android.server;
 
 import static android.service.quickaccesswallet.Flags.FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP;
+import static android.service.quickaccesswallet.Flags.FLAG_LAUNCH_WALLET_VIA_SYSUI_CALLBACKS;
 import static android.service.quickaccesswallet.Flags.launchWalletOptionOnPowerDoubleTap;
+import static android.service.quickaccesswallet.Flags.launchWalletViaSysuiCallbacks;
 
+import static com.android.server.GestureLauncherService.DOUBLE_TAP_POWER_DISABLED_MODE;
+import static com.android.server.GestureLauncherService.DOUBLE_TAP_POWER_LAUNCH_CAMERA_MODE;
+import static com.android.server.GestureLauncherService.DOUBLE_TAP_POWER_MULTI_TARGET_MODE;
 import static com.android.server.GestureLauncherService.LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER;
 import static com.android.server.GestureLauncherService.LAUNCH_WALLET_ON_DOUBLE_TAP_POWER;
 import static com.android.server.GestureLauncherService.POWER_DOUBLE_TAP_MAX_TIME_MS;
@@ -45,6 +50,7 @@
 import android.os.Looper;
 import android.os.UserHandle;
 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;
@@ -160,7 +166,7 @@
                 new GestureLauncherService(
                         mContext, mMetricsLogger, mQuickAccessWalletClient, mUiEventLogger);
 
-        withDoubleTapPowerGestureEnableSettingValue(true);
+        withMultiTargetDoubleTapPowerGestureEnableSettingValue(true);
         withDefaultDoubleTapPowerGestureAction(LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER);
     }
 
@@ -212,68 +218,117 @@
     }
 
     @Test
-    public void testIsCameraDoubleTapPowerSettingEnabled_configFalseSettingDisabled() {
-        if (launchWalletOptionOnPowerDoubleTap()) {
-            withDoubleTapPowerEnabledConfigValue(false);
-            withDoubleTapPowerGestureEnableSettingValue(false);
-            withDefaultDoubleTapPowerGestureAction(LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER);
-        } else {
-            withCameraDoubleTapPowerEnableConfigValue(false);
-            withCameraDoubleTapPowerDisableSettingValue(1);
-        }
+    @RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
+    public void testIsCameraDoubleTapPowerSettingEnabled_flagEnabled_configFalseSettingDisabled() {
+        withDoubleTapPowerModeConfigValue(
+                DOUBLE_TAP_POWER_DISABLED_MODE);
+        withMultiTargetDoubleTapPowerGestureEnableSettingValue(false);
+        withDefaultDoubleTapPowerGestureAction(LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER);
+
         assertFalse(mGestureLauncherService.isCameraDoubleTapPowerSettingEnabled(
                 mContext, FAKE_USER_ID));
     }
 
     @Test
-    public void testIsCameraDoubleTapPowerSettingEnabled_configFalseSettingEnabled() {
-        if (launchWalletOptionOnPowerDoubleTap()) {
-            withDoubleTapPowerEnabledConfigValue(false);
-            withDoubleTapPowerGestureEnableSettingValue(true);
-            withDefaultDoubleTapPowerGestureAction(LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER);
-            assertTrue(mGestureLauncherService.isCameraDoubleTapPowerSettingEnabled(
-                    mContext, FAKE_USER_ID));
-        } else {
-            withCameraDoubleTapPowerEnableConfigValue(false);
-            withCameraDoubleTapPowerDisableSettingValue(0);
-            assertFalse(mGestureLauncherService.isCameraDoubleTapPowerSettingEnabled(
-                    mContext, FAKE_USER_ID));
-        }
-    }
+    @RequiresFlagsDisabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
+    public void testIsCameraDoubleTapPowerSettingEnabled_flagDisabled_configFalseSettingDisabled() {
+        withCameraDoubleTapPowerEnableConfigValue(false);
+        withCameraDoubleTapPowerDisableSettingValue(1);
 
-    @Test
-    public void testIsCameraDoubleTapPowerSettingEnabled_configTrueSettingDisabled() {
-        if (launchWalletOptionOnPowerDoubleTap()) {
-            withDoubleTapPowerEnabledConfigValue(true);
-            withDoubleTapPowerGestureEnableSettingValue(false);
-            withDefaultDoubleTapPowerGestureAction(LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER);
-        } else {
-            withCameraDoubleTapPowerEnableConfigValue(true);
-            withCameraDoubleTapPowerDisableSettingValue(1);
-        }
         assertFalse(mGestureLauncherService.isCameraDoubleTapPowerSettingEnabled(
                 mContext, FAKE_USER_ID));
     }
 
     @Test
-    public void testIsCameraDoubleTapPowerSettingEnabled_configTrueSettingEnabled() {
-        if (launchWalletOptionOnPowerDoubleTap()) {
-            withDoubleTapPowerEnabledConfigValue(true);
-            withDoubleTapPowerGestureEnableSettingValue(true);
-            withDefaultDoubleTapPowerGestureAction(LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER);
-        } else {
-            withCameraDoubleTapPowerEnableConfigValue(true);
-            withCameraDoubleTapPowerDisableSettingValue(0);
-        }
+    @RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
+    public void testIsCameraDoubleTapPowerSettingEnabled_flagEnabled_configFalseSettingEnabled() {
+        withDoubleTapPowerModeConfigValue(DOUBLE_TAP_POWER_DISABLED_MODE);
+        withMultiTargetDoubleTapPowerGestureEnableSettingValue(true);
+        withDefaultDoubleTapPowerGestureAction(LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER);
+
+        assertFalse(mGestureLauncherService.isCameraDoubleTapPowerSettingEnabled(
+                mContext, FAKE_USER_ID));
+    }
+
+    @Test
+    @RequiresFlagsDisabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
+    public void testIsCameraDoubleTapPowerSettingEnabled_flagDisabled_configFalseSettingEnabled() {
+        withCameraDoubleTapPowerEnableConfigValue(false);
+        withCameraDoubleTapPowerDisableSettingValue(0);
+
+        assertFalse(mGestureLauncherService.isCameraDoubleTapPowerSettingEnabled(
+                mContext, FAKE_USER_ID));
+    }
+
+    @Test
+    @RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
+    public void testIsCameraDoubleTapPowerSettingEnabled_flagEnabled_configTrueSettingDisabled() {
+        withDoubleTapPowerModeConfigValue(DOUBLE_TAP_POWER_MULTI_TARGET_MODE);
+        withMultiTargetDoubleTapPowerGestureEnableSettingValue(false);
+        withDefaultDoubleTapPowerGestureAction(LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER);
+
+        assertFalse(mGestureLauncherService.isCameraDoubleTapPowerSettingEnabled(
+                mContext, FAKE_USER_ID));
+    }
+
+    @Test
+    @RequiresFlagsDisabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
+    public void testIsCameraDoubleTapPowerSettingEnabled_flagDisabled_configTrueSettingDisabled() {
+        withCameraDoubleTapPowerEnableConfigValue(true);
+        withCameraDoubleTapPowerDisableSettingValue(1);
+
+        assertFalse(mGestureLauncherService.isCameraDoubleTapPowerSettingEnabled(
+                mContext, FAKE_USER_ID));
+    }
+
+    @Test
+    @RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
+    public void testIsCameraDoubleTapPowerSettingEnabled_flagEnabled_configTrueSettingEnabled() {
+        withDoubleTapPowerModeConfigValue(DOUBLE_TAP_POWER_MULTI_TARGET_MODE);
+        withMultiTargetDoubleTapPowerGestureEnableSettingValue(true);
+        withDefaultDoubleTapPowerGestureAction(LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER);
+
+        assertTrue(mGestureLauncherService.isCameraDoubleTapPowerSettingEnabled(
+                mContext, FAKE_USER_ID));
+    }
+
+    @Test
+    @RequiresFlagsDisabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
+    public void testIsCameraDoubleTapPowerSettingEnabled_flagDisabled_configTrueSettingEnabled() {
+        withCameraDoubleTapPowerEnableConfigValue(true);
+        withCameraDoubleTapPowerDisableSettingValue(0);
+
         assertTrue(mGestureLauncherService.isCameraDoubleTapPowerSettingEnabled(
                 mContext, FAKE_USER_ID));
     }
 
     @Test
     @RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
+    public void testIsCameraDoubleTapPowerSettingEnabled_launchCameraMode_settingEnabled() {
+        withDoubleTapPowerModeConfigValue(DOUBLE_TAP_POWER_LAUNCH_CAMERA_MODE);
+        withCameraDoubleTapPowerDisableSettingValue(0);
+
+        assertTrue(
+                mGestureLauncherService.isCameraDoubleTapPowerSettingEnabled(
+                        mContext, FAKE_USER_ID));
+    }
+
+    @Test
+    @RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
+    public void testIsCameraDoubleTapPowerSettingEnabled_launchCameraMode_settingDisabled() {
+        withDoubleTapPowerModeConfigValue(DOUBLE_TAP_POWER_LAUNCH_CAMERA_MODE);
+        withCameraDoubleTapPowerDisableSettingValue(1);
+
+        assertFalse(
+                mGestureLauncherService.isCameraDoubleTapPowerSettingEnabled(
+                        mContext, FAKE_USER_ID));
+    }
+
+    @Test
+    @RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
     public void testIsCameraDoubleTapPowerSettingEnabled_actionWallet() {
-        withDoubleTapPowerEnabledConfigValue(true);
-        withDoubleTapPowerGestureEnableSettingValue(true);
+        withDoubleTapPowerModeConfigValue(DOUBLE_TAP_POWER_MULTI_TARGET_MODE);
+        withMultiTargetDoubleTapPowerGestureEnableSettingValue(true);
         withDefaultDoubleTapPowerGestureAction(LAUNCH_WALLET_ON_DOUBLE_TAP_POWER);
 
         assertFalse(
@@ -284,8 +339,8 @@
     @Test
     @RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
     public void testIsWalletDoubleTapPowerSettingEnabled() {
-        withDoubleTapPowerEnabledConfigValue(true);
-        withDoubleTapPowerGestureEnableSettingValue(true);
+        withDoubleTapPowerModeConfigValue(DOUBLE_TAP_POWER_MULTI_TARGET_MODE);
+        withMultiTargetDoubleTapPowerGestureEnableSettingValue(true);
         withDefaultDoubleTapPowerGestureAction(LAUNCH_WALLET_ON_DOUBLE_TAP_POWER);
 
         assertTrue(
@@ -296,11 +351,11 @@
     @Test
     @RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
     public void testIsWalletDoubleTapPowerSettingEnabled_configDisabled() {
-        withDoubleTapPowerEnabledConfigValue(false);
-        withDoubleTapPowerGestureEnableSettingValue(true);
+        withDoubleTapPowerModeConfigValue(DOUBLE_TAP_POWER_DISABLED_MODE);
+        withMultiTargetDoubleTapPowerGestureEnableSettingValue(true);
         withDefaultDoubleTapPowerGestureAction(LAUNCH_WALLET_ON_DOUBLE_TAP_POWER);
 
-        assertTrue(
+        assertFalse(
                 mGestureLauncherService.isWalletDoubleTapPowerSettingEnabled(
                         mContext, FAKE_USER_ID));
     }
@@ -308,8 +363,8 @@
     @Test
     @RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
     public void testIsWalletDoubleTapPowerSettingEnabled_settingDisabled() {
-        withDoubleTapPowerEnabledConfigValue(true);
-        withDoubleTapPowerGestureEnableSettingValue(false);
+        withDoubleTapPowerModeConfigValue(DOUBLE_TAP_POWER_MULTI_TARGET_MODE);
+        withMultiTargetDoubleTapPowerGestureEnableSettingValue(false);
         withDefaultDoubleTapPowerGestureAction(LAUNCH_WALLET_ON_DOUBLE_TAP_POWER);
 
         assertFalse(
@@ -320,8 +375,8 @@
     @Test
     @RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
     public void testIsWalletDoubleTapPowerSettingEnabled_actionCamera() {
-        withDoubleTapPowerEnabledConfigValue(true);
-        withDoubleTapPowerGestureEnableSettingValue(true);
+        withDoubleTapPowerModeConfigValue(DOUBLE_TAP_POWER_MULTI_TARGET_MODE);
+        withMultiTargetDoubleTapPowerGestureEnableSettingValue(true);
         withDefaultDoubleTapPowerGestureAction(LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER);
 
         assertFalse(
@@ -446,13 +501,7 @@
 
     @Test
     public void testInterceptPowerKeyDown_intervalInBoundsCameraPowerGestureOffInteractive() {
-        if (launchWalletOptionOnPowerDoubleTap()) {
-            withDoubleTapPowerGestureEnableSettingValue(false);
-        } else {
-            withCameraDoubleTapPowerEnableConfigValue(false);
-            withCameraDoubleTapPowerDisableSettingValue(1);
-        }
-        mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
+        disableDoubleTapPowerGesture();
 
         long eventTime = INITIAL_EVENT_TIME_MILLIS;
         KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
@@ -495,13 +544,7 @@
 
     @Test
     public void testInterceptPowerKeyDown_intervalMidBoundsCameraPowerGestureOffInteractive() {
-        if (launchWalletOptionOnPowerDoubleTap()) {
-            withDoubleTapPowerGestureEnableSettingValue(false);
-        } else {
-            withCameraDoubleTapPowerEnableConfigValue(false);
-            withCameraDoubleTapPowerDisableSettingValue(1);
-        }
-        mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
+        disableDoubleTapPowerGesture();
 
         long eventTime = INITIAL_EVENT_TIME_MILLIS;
         KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
@@ -546,9 +589,7 @@
 
     @Test
     public void testInterceptPowerKeyDown_intervalOutOfBoundsCameraPowerGestureOffInteractive() {
-        withCameraDoubleTapPowerEnableConfigValue(false);
-        withCameraDoubleTapPowerDisableSettingValue(1);
-        mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
+        disableDoubleTapPowerGesture();
 
         long eventTime = INITIAL_EVENT_TIME_MILLIS;
         KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
@@ -653,7 +694,11 @@
         eventTime += interval;
         sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, true, true);
 
-        assertTrue(receiver.waitUntilShown());
+        if (launchWalletViaSysuiCallbacks()) {
+            verify(mStatusBarManagerInternal).onWalletLaunchGestureDetected();
+        } else {
+            assertTrue(receiver.waitUntilShown());
+        }
 
         // Presses 3 and 4 should not trigger any gesture
         for (int i = 0; i < 2; i++) {
@@ -683,11 +728,17 @@
         final long interval = POWER_DOUBLE_TAP_MAX_TIME_MS - 1;
         eventTime += interval;
         sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, true, true);
-        assertTrue(receiver.waitUntilShown());
+
+        if (launchWalletViaSysuiCallbacks()) {
+            verify(mStatusBarManagerInternal).onWalletLaunchGestureDetected();
+        } else {
+            assertTrue(receiver.waitUntilShown());
+        }
     }
 
     @Test
     @RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
+    @RequiresFlagsDisabled(FLAG_LAUNCH_WALLET_VIA_SYSUI_CALLBACKS)
     public void testInterceptPowerKeyDown_walletGestureOn_quickAccessWalletServiceUnavailable() {
         when(mQuickAccessWalletClient.isWalletServiceAvailable()).thenReturn(false);
         WalletLaunchedReceiver receiver = registerWalletLaunchedReceiver(LAUNCH_TEST_WALLET_ACTION);
@@ -720,11 +771,16 @@
         eventTime += interval;
         sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, false, false);
 
-        assertFalse(receiver.waitUntilShown());
+        if (launchWalletViaSysuiCallbacks()) {
+            verify(mStatusBarManagerInternal, never()).onWalletLaunchGestureDetected();
+        } else {
+            assertFalse(receiver.waitUntilShown());
+        }
     }
 
     @Test
     @RequiresFlagsEnabled(FLAG_LAUNCH_WALLET_OPTION_ON_POWER_DOUBLE_TAP)
+    @RequiresFlagsDisabled(FLAG_LAUNCH_WALLET_VIA_SYSUI_CALLBACKS)
     public void testInterceptPowerKeyDown_walletPowerGesture_nullPendingIntent() {
         WalletLaunchedReceiver gestureReceiver =
                 registerWalletLaunchedReceiver(LAUNCH_TEST_WALLET_ACTION);
@@ -767,8 +823,12 @@
         eventTime += interval;
         sendPowerKeyDownToGestureLauncherServiceAndAssertValues(eventTime, false, false);
 
-        assertFalse(gestureReceiver.waitUntilShown());
-        assertFalse(fallbackReceiver.waitUntilShown());
+        if (launchWalletViaSysuiCallbacks()) {
+            verify(mStatusBarManagerInternal, never()).onWalletLaunchGestureDetected();
+        } else {
+            assertFalse(gestureReceiver.waitUntilShown());
+            assertFalse(fallbackReceiver.waitUntilShown());
+        }
     }
 
     @Test
@@ -1009,9 +1069,7 @@
     public void
     testInterceptPowerKeyDown_triggerEmergency_cameraGestureEnabled_doubleTap_cooldownTriggered() {
         // Enable camera double tap gesture
-        withCameraDoubleTapPowerEnableConfigValue(true);
-        withCameraDoubleTapPowerDisableSettingValue(0);
-        mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
+        enableCameraGesture();
 
         // Enable power button cooldown
         withEmergencyGesturePowerButtonCooldownPeriodMsValue(3000);
@@ -1198,10 +1256,7 @@
 
     @Test
     public void testInterceptPowerKeyDown_longpress() {
-        withCameraDoubleTapPowerEnableConfigValue(true);
-        withCameraDoubleTapPowerDisableSettingValue(0);
-        mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
-        withUserSetupCompleteValue(true);
+        enableCameraGesture();
 
         long eventTime = INITIAL_EVENT_TIME_MILLIS;
         KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
@@ -1378,13 +1433,7 @@
 
     @Test
     public void testInterceptPowerKeyDown_intervalInBoundsCameraPowerGestureOffNotInteractive() {
-        if (launchWalletOptionOnPowerDoubleTap()) {
-            withDoubleTapPowerGestureEnableSettingValue(false);
-        } else {
-            withCameraDoubleTapPowerEnableConfigValue(false);
-            withCameraDoubleTapPowerDisableSettingValue(1);
-        }
-        mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
+        disableDoubleTapPowerGesture();
 
         long eventTime = INITIAL_EVENT_TIME_MILLIS;
         KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
@@ -1427,9 +1476,7 @@
 
     @Test
     public void testInterceptPowerKeyDown_intervalMidBoundsCameraPowerGestureOffNotInteractive() {
-        withCameraDoubleTapPowerEnableConfigValue(false);
-        withCameraDoubleTapPowerDisableSettingValue(1);
-        mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
+        disableDoubleTapPowerGesture();
 
         long eventTime = INITIAL_EVENT_TIME_MILLIS;
         KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
@@ -1473,9 +1520,7 @@
 
     @Test
     public void testInterceptPowerKeyDown_intervalOutOfBoundsCameraPowerGestureOffNotInteractive() {
-        withCameraDoubleTapPowerEnableConfigValue(false);
-        withCameraDoubleTapPowerDisableSettingValue(1);
-        mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
+        disableDoubleTapPowerGesture();
 
         long eventTime = INITIAL_EVENT_TIME_MILLIS;
         KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
@@ -1608,9 +1653,7 @@
 
     @Test
     public void testInterceptPowerKeyDown_intervalMidBoundsCameraPowerGestureOnNotInteractive() {
-        withCameraDoubleTapPowerEnableConfigValue(true);
-        withCameraDoubleTapPowerDisableSettingValue(0);
-        mGestureLauncherService.updateCameraDoubleTapPowerEnabled();
+        enableCameraGesture();
 
         long eventTime = INITIAL_EVENT_TIME_MILLIS;
         KeyEvent keyEvent = new KeyEvent(IGNORED_DOWN_TIME, eventTime, IGNORED_ACTION, IGNORED_CODE,
@@ -1801,12 +1844,13 @@
                 .thenReturn(enableConfigValue);
     }
 
-    private void withDoubleTapPowerEnabledConfigValue(boolean enable) {
-        when(mResources.getBoolean(com.android.internal.R.bool.config_doubleTapPowerGestureEnabled))
-                .thenReturn(enable);
+    private void withDoubleTapPowerModeConfigValue(
+            int modeConfigValue) {
+        when(mResources.getInteger(com.android.internal.R.integer.config_doubleTapPowerGestureMode))
+                .thenReturn(modeConfigValue);
     }
 
-    private void withDoubleTapPowerGestureEnableSettingValue(boolean enable) {
+    private void withMultiTargetDoubleTapPowerGestureEnableSettingValue(boolean enable) {
         Settings.Secure.putIntForUser(
                 mContentResolver,
                 Settings.Secure.DOUBLE_TAP_POWER_BUTTON_GESTURE_ENABLED,
@@ -1888,8 +1932,8 @@
 
     private void enableWalletGesture() {
         withDefaultDoubleTapPowerGestureAction(LAUNCH_WALLET_ON_DOUBLE_TAP_POWER);
-        withDoubleTapPowerGestureEnableSettingValue(true);
-        withDoubleTapPowerEnabledConfigValue(true);
+        withMultiTargetDoubleTapPowerGestureEnableSettingValue(true);
+        withDoubleTapPowerModeConfigValue(DOUBLE_TAP_POWER_MULTI_TARGET_MODE);
 
         mGestureLauncherService.updateWalletDoubleTapPowerEnabled();
         withUserSetupCompleteValue(true);
@@ -1904,8 +1948,9 @@
 
     private void enableCameraGesture() {
         if (launchWalletOptionOnPowerDoubleTap()) {
-            withDoubleTapPowerEnabledConfigValue(true);
-            withDoubleTapPowerGestureEnableSettingValue(true);
+            withDoubleTapPowerModeConfigValue(
+                    DOUBLE_TAP_POWER_MULTI_TARGET_MODE);
+            withMultiTargetDoubleTapPowerGestureEnableSettingValue(true);
             withDefaultDoubleTapPowerGestureAction(LAUNCH_CAMERA_ON_DOUBLE_TAP_POWER);
         } else {
             withCameraDoubleTapPowerEnableConfigValue(true);
@@ -1915,6 +1960,18 @@
         withUserSetupCompleteValue(true);
     }
 
+    private void disableDoubleTapPowerGesture() {
+        if (launchWalletOptionOnPowerDoubleTap()) {
+            withDoubleTapPowerModeConfigValue(DOUBLE_TAP_POWER_DISABLED_MODE);
+            withMultiTargetDoubleTapPowerGestureEnableSettingValue(false);
+        } else {
+            withCameraDoubleTapPowerEnableConfigValue(false);
+            withCameraDoubleTapPowerDisableSettingValue(1);
+        }
+        mGestureLauncherService.updateWalletDoubleTapPowerEnabled();
+        withUserSetupCompleteValue(true);
+    }
+
     private void sendPowerKeyDownToGestureLauncherServiceAndAssertValues(
             long eventTime, boolean expectedIntercept, boolean expectedOutLaunchedValue) {
         KeyEvent keyEvent =
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterInputTest.kt b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterInputTest.kt
index e6c94c5..acd8f3a 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterInputTest.kt
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityInputFilterInputTest.kt
@@ -43,6 +43,7 @@
 import com.android.cts.input.inputeventmatchers.withDeviceId
 import com.android.cts.input.inputeventmatchers.withMotionAction
 import com.android.cts.input.inputeventmatchers.withSource
+import com.android.cts.input.BlockingQueueEventVerifier
 import com.android.server.LocalServices
 import com.android.server.accessibility.magnification.MagnificationProcessor
 import com.android.server.wm.WindowManagerInternal
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 fa78dfc..dafe482 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java
@@ -220,6 +220,9 @@
     @Mock private ProxyManager mProxyManager;
     @Mock private StatusBarManagerInternal mStatusBarManagerInternal;
     @Mock private DevicePolicyManager mDevicePolicyManager;
+    @Mock
+    private HearingDevicePhoneCallNotificationController
+            mMockHearingDevicePhoneCallNotificationController;
     @Spy private IUserInitializationCompleteCallback mUserInitializationCompleteCallback;
     @Captor private ArgumentCaptor<Intent> mIntentArgumentCaptor;
     private IAccessibilityManager mA11yManagerServiceOnDevice;
@@ -289,7 +292,8 @@
                 mMockMagnificationController,
                 mInputFilter,
                 mProxyManager,
-                mFakePermissionEnforcer);
+                mFakePermissionEnforcer,
+                mMockHearingDevicePhoneCallNotificationController);
         mA11yms.switchUser(mTestableContext.getUserId());
         mTestableLooper.processAllMessages();
 
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/BlockingQueueEventVerifier.kt b/services/tests/servicestests/src/com/android/server/accessibility/BlockingQueueEventVerifier.kt
deleted file mode 100644
index b12f537..0000000
--- a/services/tests/servicestests/src/com/android/server/accessibility/BlockingQueueEventVerifier.kt
+++ /dev/null
@@ -1,57 +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.accessibility
-
-import android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS
-import android.view.InputEvent
-import android.view.MotionEvent
-import java.time.Duration
-import java.util.concurrent.BlockingQueue
-import java.util.concurrent.TimeUnit
-import org.junit.Assert.fail
-
-import org.hamcrest.Matcher
-import org.hamcrest.MatcherAssert.assertThat
-import org.junit.Assert.assertNull
-
-private fun <T> getEvent(queue: BlockingQueue<T>, timeout: Duration): T? {
-    return queue.poll(timeout.toMillis(), TimeUnit.MILLISECONDS)
-}
-
-class BlockingQueueEventVerifier(val queue: BlockingQueue<InputEvent>) {
-    fun assertReceivedMotion(matcher: Matcher<MotionEvent>) {
-        val event = getMotionEvent()
-        assertThat("MotionEvent checks", event, matcher)
-    }
-
-    fun assertNoEvents() {
-        val event = getEvent(queue, Duration.ofMillis(50))
-        assertNull(event)
-    }
-
-    private fun getMotionEvent(): MotionEvent {
-        val event = getEvent(queue, Duration.ofMillis(DEFAULT_DISPATCHING_TIMEOUT_MILLIS.toLong()))
-        if (event == null) {
-            fail("Did not get an event")
-        }
-        if (event is MotionEvent) {
-            return event
-        }
-        fail("Instead of motion, got $event")
-        throw RuntimeException("should not reach here")
-    }
-}
-
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/HearingDevicePhoneCallNotificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/HearingDevicePhoneCallNotificationControllerTest.java
new file mode 100644
index 0000000..efea214
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/accessibility/HearingDevicePhoneCallNotificationControllerTest.java
@@ -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.server.accessibility;
+
+import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
+
+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.doReturn;
+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.app.Application;
+import android.app.Instrumentation;
+import android.app.NotificationManager;
+import android.bluetooth.BluetoothAdapter;
+import android.content.Context;
+import android.media.AudioDeviceInfo;
+import android.media.AudioDevicePort;
+import android.media.AudioManager;
+import android.telephony.TelephonyCallback;
+import android.telephony.TelephonyManager;
+
+import androidx.annotation.NonNull;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.messages.nano.SystemMessageProto;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.Spy;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * Tests for the {@link HearingDevicePhoneCallNotificationController}.
+ */
+@RunWith(AndroidJUnit4.class)
+public class HearingDevicePhoneCallNotificationControllerTest {
+    @Rule
+    public MockitoRule mockito = MockitoJUnit.rule();
+
+    private static final String TEST_ADDRESS = "55:66:77:88:99:AA";
+
+    private final Application mApplication = ApplicationProvider.getApplicationContext();
+    @Spy
+    private final Context mContext = mApplication.getApplicationContext();
+    private final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
+
+    @Mock
+    private TelephonyManager mTelephonyManager;
+    @Mock
+    private NotificationManager mNotificationManager;
+    @Mock
+    private AudioManager mAudioManager;
+    private HearingDevicePhoneCallNotificationController mController;
+    private TestCallStateListener mTestCallStateListener;
+
+    @Before
+    public void setUp() {
+        mInstrumentation.getUiAutomation().adoptShellPermissionIdentity(BLUETOOTH_PRIVILEGED);
+        when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager);
+        when(mContext.getSystemService(NotificationManager.class)).thenReturn(mNotificationManager);
+        when(mContext.getSystemService(AudioManager.class)).thenReturn(mAudioManager);
+
+        mTestCallStateListener = new TestCallStateListener(mContext);
+        mController = new HearingDevicePhoneCallNotificationController(mContext,
+                mTestCallStateListener);
+        mController.startListenForCallState();
+    }
+
+    @Test
+    public void startListenForCallState_callbackNotNull() {
+        Mockito.reset(mTelephonyManager);
+        mController = new HearingDevicePhoneCallNotificationController(mContext);
+        ArgumentCaptor<TelephonyCallback> listenerCaptor = ArgumentCaptor.forClass(
+                TelephonyCallback.class);
+
+        mController.startListenForCallState();
+
+        verify(mTelephonyManager).registerTelephonyCallback(any(Executor.class),
+                listenerCaptor.capture());
+        TelephonyCallback callback = listenerCaptor.getValue();
+        assertThat(callback).isNotNull();
+    }
+
+    @Test
+    public void onCallStateChanged_stateOffHook_hapDevice_showNotification() {
+        AudioDeviceInfo hapDeviceInfo = createAudioDeviceInfo(TEST_ADDRESS,
+                AudioManager.DEVICE_OUT_BLE_HEADSET);
+        when(mAudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)).thenReturn(
+                new AudioDeviceInfo[]{hapDeviceInfo});
+        when(mAudioManager.getAvailableCommunicationDevices()).thenReturn(List.of(hapDeviceInfo));
+
+        mTestCallStateListener.onCallStateChanged(TelephonyManager.CALL_STATE_OFFHOOK);
+
+        verify(mNotificationManager).notify(
+                eq(SystemMessageProto.SystemMessage.NOTE_HEARING_DEVICE_INPUT_SWITCH), any());
+    }
+
+    @Test
+    public void onCallStateChanged_stateOffHook_a2dpDevice_noNotification() {
+        AudioDeviceInfo a2dpDeviceInfo = createAudioDeviceInfo(TEST_ADDRESS,
+                AudioManager.DEVICE_OUT_BLUETOOTH_A2DP);
+        when(mAudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)).thenReturn(
+                new AudioDeviceInfo[]{a2dpDeviceInfo});
+        when(mAudioManager.getAvailableCommunicationDevices()).thenReturn(List.of(a2dpDeviceInfo));
+
+        mTestCallStateListener.onCallStateChanged(TelephonyManager.CALL_STATE_OFFHOOK);
+
+        verify(mNotificationManager, never()).notify(
+                eq(SystemMessageProto.SystemMessage.NOTE_HEARING_DEVICE_INPUT_SWITCH), any());
+    }
+
+    @Test
+    public void onCallStateChanged_stateOffHookThenIdle_hapDeviceInfo_cancelNotification() {
+        AudioDeviceInfo hapDeviceInfo = createAudioDeviceInfo(TEST_ADDRESS,
+                AudioManager.DEVICE_OUT_BLE_HEADSET);
+        when(mAudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS)).thenReturn(
+                new AudioDeviceInfo[]{hapDeviceInfo});
+        when(mAudioManager.getAvailableCommunicationDevices()).thenReturn(List.of(hapDeviceInfo));
+
+        mTestCallStateListener.onCallStateChanged(TelephonyManager.CALL_STATE_OFFHOOK);
+        mTestCallStateListener.onCallStateChanged(TelephonyManager.CALL_STATE_IDLE);
+
+        verify(mNotificationManager).cancel(
+                eq(SystemMessageProto.SystemMessage.NOTE_HEARING_DEVICE_INPUT_SWITCH));
+    }
+
+    private AudioDeviceInfo createAudioDeviceInfo(String address, int type) {
+        AudioDevicePort audioDevicePort = mock(AudioDevicePort.class);
+        doReturn(type).when(audioDevicePort).type();
+        doReturn(address).when(audioDevicePort).address();
+        doReturn("testDevice").when(audioDevicePort).name();
+
+        return new AudioDeviceInfo(audioDevicePort);
+    }
+
+    /**
+     * For easier testing for CallStateListener, override methods that contain final object.
+     */
+    private static class TestCallStateListener extends
+            HearingDevicePhoneCallNotificationController.CallStateListener {
+
+        TestCallStateListener(@NonNull Context context) {
+            super(context);
+        }
+
+        @Override
+        boolean isHapClientSupported() {
+            return true;
+        }
+
+        @Override
+        boolean isHapClientDevice(BluetoothAdapter bluetoothAdapter, AudioDeviceInfo info) {
+            return TEST_ADDRESS.equals(info.getAddress());
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/OWNERS b/services/tests/servicestests/src/com/android/server/accessibility/OWNERS
index b74281e..c824c39 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/OWNERS
+++ b/services/tests/servicestests/src/com/android/server/accessibility/OWNERS
@@ -1 +1,3 @@
+# Bug component: 44215
+
 include /core/java/android/view/accessibility/OWNERS
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 2fe6918..6411463 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -94,6 +94,7 @@
 import android.os.Message;
 import android.os.PowerManagerInternal;
 import android.os.RemoteException;
+import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.storage.IStorageManager;
@@ -181,14 +182,12 @@
             Intent.ACTION_USER_STARTING);
 
     private static final Set<Integer> START_FOREGROUND_USER_MESSAGE_CODES = newHashSet(
-            0, // for startUserInternalOnHandler
             REPORT_USER_SWITCH_MSG,
             USER_SWITCH_TIMEOUT_MSG,
             USER_START_MSG,
             USER_CURRENT_MSG);
 
     private static final Set<Integer> START_BACKGROUND_USER_MESSAGE_CODES = newHashSet(
-            0, // for startUserInternalOnHandler
             USER_START_MSG,
             REPORT_LOCKED_BOOT_COMPLETE_MSG);
 
@@ -376,7 +375,7 @@
         // and the cascade effect goes on...). In fact, a better approach would to not assert the
         // binder calls, but their side effects (in this case, that the user is stopped right away)
         assertWithMessage("wrong binder message calls").that(mInjector.mHandler.getMessageCodes())
-                .containsExactly(/* for startUserInternalOnHandler */ 0, USER_START_MSG);
+                .containsExactly(USER_START_MSG);
     }
 
     private void startUserAssertions(
@@ -419,17 +418,12 @@
     @Test
     public void testDispatchUserSwitch() throws RemoteException {
         // Prepare mock observer and register it
-        IUserSwitchObserver observer = mock(IUserSwitchObserver.class);
-        when(observer.asBinder()).thenReturn(new Binder());
-        doAnswer(invocation -> {
-            IRemoteCallback callback = (IRemoteCallback) invocation.getArguments()[1];
-            callback.sendResult(null);
-            return null;
-        }).when(observer).onUserSwitching(anyInt(), any());
-        mUserController.registerUserSwitchObserver(observer, "mock");
+        IUserSwitchObserver observer = registerUserSwitchObserver(
+                /* replyToOnBeforeUserSwitchingCallback= */ true,
+                /* replyToOnUserSwitchingCallback= */ true);
         // Start user -- this will update state of mUserController
         mUserController.startUser(TEST_USER_ID, USER_START_MODE_FOREGROUND);
-        verify(observer, times(1)).onBeforeUserSwitching(eq(TEST_USER_ID));
+        verify(observer, times(1)).onBeforeUserSwitching(eq(TEST_USER_ID), any());
         Message reportMsg = mInjector.mHandler.getMessageForCode(REPORT_USER_SWITCH_MSG);
         assertNotNull(reportMsg);
         UserState userState = (UserState) reportMsg.obj;
@@ -454,13 +448,13 @@
 
     @Test
     public void testDispatchUserSwitchBadReceiver() throws RemoteException {
-        // Prepare mock observer which doesn't notify the callback and register it
-        IUserSwitchObserver observer = mock(IUserSwitchObserver.class);
-        when(observer.asBinder()).thenReturn(new Binder());
-        mUserController.registerUserSwitchObserver(observer, "mock");
+        // Prepare mock observer which doesn't notify the onUserSwitching callback and register it
+        IUserSwitchObserver observer = registerUserSwitchObserver(
+                /* replyToOnBeforeUserSwitchingCallback= */ true,
+                /* replyToOnUserSwitchingCallback= */ false);
         // Start user -- this will update state of mUserController
         mUserController.startUser(TEST_USER_ID, USER_START_MODE_FOREGROUND);
-        verify(observer, times(1)).onBeforeUserSwitching(eq(TEST_USER_ID));
+        verify(observer, times(1)).onBeforeUserSwitching(eq(TEST_USER_ID), any());
         Message reportMsg = mInjector.mHandler.getMessageForCode(REPORT_USER_SWITCH_MSG);
         assertNotNull(reportMsg);
         UserState userState = (UserState) reportMsg.obj;
@@ -551,7 +545,6 @@
         expectedCodes.add(REPORT_USER_SWITCH_COMPLETE_MSG);
         if (backgroundUserStopping) {
             expectedCodes.add(CLEAR_USER_JOURNEY_SESSION_MSG);
-            expectedCodes.add(0); // this is for directly posting in stopping.
         }
         if (expectScheduleBackgroundUserStopping) {
             expectedCodes.add(SCHEDULED_STOP_BACKGROUND_USER_MSG);
@@ -567,9 +560,9 @@
     @Test
     public void testDispatchUserSwitchComplete() throws RemoteException {
         // Prepare mock observer and register it
-        IUserSwitchObserver observer = mock(IUserSwitchObserver.class);
-        when(observer.asBinder()).thenReturn(new Binder());
-        mUserController.registerUserSwitchObserver(observer, "mock");
+        IUserSwitchObserver observer = registerUserSwitchObserver(
+                /* replyToOnBeforeUserSwitchingCallback= */ true,
+                /* replyToOnUserSwitchingCallback= */ true);
         // Start user -- this will update state of mUserController
         mUserController.startUser(TEST_USER_ID, USER_START_MODE_FOREGROUND);
         Message reportMsg = mInjector.mHandler.getMessageForCode(REPORT_USER_SWITCH_MSG);
@@ -1752,6 +1745,29 @@
         verify(mInjector, never()).onSystemUserVisibilityChanged(anyBoolean());
     }
 
+    private IUserSwitchObserver registerUserSwitchObserver(
+            boolean replyToOnBeforeUserSwitchingCallback, boolean replyToOnUserSwitchingCallback)
+            throws RemoteException {
+        IUserSwitchObserver observer = mock(IUserSwitchObserver.class);
+        when(observer.asBinder()).thenReturn(new Binder());
+        if (replyToOnBeforeUserSwitchingCallback) {
+            doAnswer(invocation -> {
+                IRemoteCallback callback = (IRemoteCallback) invocation.getArguments()[1];
+                callback.sendResult(null);
+                return null;
+            }).when(observer).onBeforeUserSwitching(anyInt(), any());
+        }
+        if (replyToOnUserSwitchingCallback) {
+            doAnswer(invocation -> {
+                IRemoteCallback callback = (IRemoteCallback) invocation.getArguments()[1];
+                callback.sendResult(null);
+                return null;
+            }).when(observer).onUserSwitching(anyInt(), any());
+        }
+        mUserController.registerUserSwitchObserver(observer, "mock");
+        return observer;
+    }
+
     // Should be public to allow mocking
     private static class TestInjector extends UserController.Injector {
         public final TestHandler mHandler;
@@ -1957,6 +1973,7 @@
          * fix this, but in the meantime, this is your warning.
          */
         private final List<Message> mMessages = new ArrayList<>();
+        private final List<Runnable> mPendingCallbacks = new ArrayList<>();
 
         TestHandler(Looper looper) {
             super(looper);
@@ -1989,14 +2006,24 @@
 
         @Override
         public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
-            Message copy = new Message();
-            copy.copyFrom(msg);
-            mMessages.add(copy);
-            if (msg.getCallback() != null) {
-                msg.getCallback().run();
+            if (msg.getCallback() == null) {
+                Message copy = new Message();
+                copy.copyFrom(msg);
+                mMessages.add(copy);
+            } else {
+                if (SystemClock.uptimeMillis() >= uptimeMillis) {
+                    msg.getCallback().run();
+                } else {
+                    mPendingCallbacks.add(msg.getCallback());
+                }
                 msg.setCallback(null);
             }
             return super.sendMessageAtTime(msg, uptimeMillis);
         }
+
+        private void runPendingCallbacks() {
+            mPendingCallbacks.forEach(Runnable::run);
+            mPendingCallbacks.clear();
+        }
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/appop/DiscreteAppOpSqlPersistenceTest.java b/services/tests/servicestests/src/com/android/server/appop/DiscreteAppOpSqlPersistenceTest.java
new file mode 100644
index 0000000..8471307
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/appop/DiscreteAppOpSqlPersistenceTest.java
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.appop;
+
+import static android.app.AppOpsManager.ATTRIBUTION_FLAG_ACCESSOR;
+import static android.app.AppOpsManager.ATTRIBUTION_FLAG_RECEIVER;
+import static android.app.AppOpsManager.ATTRIBUTION_FLAG_TRUSTED;
+import static android.app.AppOpsManager.UID_STATE_FOREGROUND;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.os.Process;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.LongSparseArray;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.server.appop.DiscreteOpsSqlRegistry.DiscreteOp;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.time.Duration;
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class DiscreteAppOpSqlPersistenceTest {
+    private static final String DATABASE_NAME = "test_app_ops.db";
+    private DiscreteOpsSqlRegistry mDiscreteRegistry;
+    private final Context mContext =
+            InstrumentationRegistry.getInstrumentation().getTargetContext();
+
+    @Before
+    public void setUp() {
+        mDiscreteRegistry = new DiscreteOpsSqlRegistry(mContext,
+                mContext.getDatabasePath(DATABASE_NAME));
+        mDiscreteRegistry.systemReady();
+    }
+
+    @After
+    public void cleanUp() {
+        mContext.deleteDatabase(DATABASE_NAME);
+    }
+
+    @Test
+    public void discreteOpEventIsRecorded() {
+        DiscreteOp opEvent = new DiscreteOpBuilder(mContext).build();
+        mDiscreteRegistry.recordDiscreteAccess(opEvent);
+        List<DiscreteOp> discreteOps = mDiscreteRegistry.getCachedDiscreteOps();
+        assertThat(discreteOps.size()).isEqualTo(1);
+        assertThat(discreteOps).contains(opEvent);
+    }
+
+    @Test
+    public void discreteOpEventIsPersistedToDisk() {
+        DiscreteOp opEvent = new DiscreteOpBuilder(mContext).build();
+        mDiscreteRegistry.recordDiscreteAccess(opEvent);
+        flushDiscreteOpsToDatabase();
+        assertThat(mDiscreteRegistry.getCachedDiscreteOps()).isEmpty();
+        List<DiscreteOp> discreteOps = mDiscreteRegistry.getAllDiscreteOps();
+        assertThat(discreteOps.size()).isEqualTo(1);
+        assertThat(discreteOps).contains(opEvent);
+    }
+
+    @Test
+    public void discreteOpEventInSameMinuteIsNotRecorded() {
+        long oneMinuteMillis = Duration.ofMinutes(1).toMillis();
+        // round timestamp at minute level and add 5 seconds
+        long accessTime = System.currentTimeMillis() / oneMinuteMillis * oneMinuteMillis + 5000;
+        DiscreteOp opEvent = new DiscreteOpBuilder(mContext).setAccessTime(accessTime).build();
+        mDiscreteRegistry.recordDiscreteAccess(opEvent);
+        // create duplicate event in same minute, with added 30 seconds
+        DiscreteOp opEvent2 =
+                new DiscreteOpBuilder(mContext).setAccessTime(accessTime + 30000).build();
+        mDiscreteRegistry.recordDiscreteAccess(opEvent2);
+        List<DiscreteOp> discreteOps = mDiscreteRegistry.getAllDiscreteOps();
+
+        assertThat(discreteOps.size()).isEqualTo(1);
+        assertThat(discreteOps).contains(opEvent);
+    }
+
+    @Test
+    public void multipleDiscreteOpEventAreRecorded() {
+        DiscreteOp opEvent = new DiscreteOpBuilder(mContext).build();
+        DiscreteOp opEvent2 = new DiscreteOpBuilder(mContext).setPackageName(
+                "test.package").build();
+        mDiscreteRegistry.recordDiscreteAccess(opEvent);
+        mDiscreteRegistry.recordDiscreteAccess(opEvent2);
+
+        List<DiscreteOp> discreteOps = mDiscreteRegistry.getAllDiscreteOps();
+        assertThat(discreteOps).contains(opEvent);
+        assertThat(discreteOps).contains(opEvent2);
+        assertThat(discreteOps.size()).isEqualTo(2);
+    }
+
+    @Test
+    public void clearDiscreteOps() {
+        DiscreteOp opEvent = new DiscreteOpBuilder(mContext).build();
+        mDiscreteRegistry.recordDiscreteAccess(opEvent);
+        flushDiscreteOpsToDatabase();
+        DiscreteOp opEvent2 = new DiscreteOpBuilder(mContext).setUid(12345).setPackageName(
+                "abc").build();
+        mDiscreteRegistry.recordDiscreteAccess(opEvent2);
+        mDiscreteRegistry.clearHistory();
+        assertThat(mDiscreteRegistry.getAllDiscreteOps()).isEmpty();
+    }
+
+    @Test
+    public void clearDiscreteOpsForPackage() {
+        DiscreteOp opEvent = new DiscreteOpBuilder(mContext).build();
+        mDiscreteRegistry.recordDiscreteAccess(opEvent);
+        flushDiscreteOpsToDatabase();
+        mDiscreteRegistry.recordDiscreteAccess(new DiscreteOpBuilder(mContext).build());
+        mDiscreteRegistry.clearHistory(Process.myUid(), mContext.getPackageName());
+
+        assertThat(mDiscreteRegistry.getAllDiscreteOps()).isEmpty();
+    }
+
+    @Test
+    public void offsetDiscreteOps() {
+        DiscreteOp opEvent = new DiscreteOpBuilder(mContext).build();
+        long event2AccessTime = System.currentTimeMillis() - 300000;
+        DiscreteOp opEvent2 = new DiscreteOpBuilder(mContext).setAccessTime(
+                event2AccessTime).build();
+        mDiscreteRegistry.recordDiscreteAccess(opEvent);
+        flushDiscreteOpsToDatabase();
+        mDiscreteRegistry.recordDiscreteAccess(opEvent2);
+        long offset = Duration.ofMinutes(2).toMillis();
+
+        mDiscreteRegistry.offsetHistory(offset);
+
+        // adjust input for assertion
+        DiscreteOp e1 = new DiscreteOpBuilder(opEvent)
+                .setAccessTime(opEvent.getAccessTime() - offset).build();
+        DiscreteOp e2 = new DiscreteOpBuilder(opEvent2)
+                .setAccessTime(event2AccessTime - offset).build();
+
+        List<DiscreteOp> results = mDiscreteRegistry.getAllDiscreteOps();
+        assertThat(results.size()).isEqualTo(2);
+        assertThat(results).contains(e1);
+        assertThat(results).contains(e2);
+    }
+
+    @Test
+    public void completeAttributionChain() {
+        long chainId = 100;
+        DiscreteOp event1 = new DiscreteOpBuilder(mContext)
+                .setChainId(chainId)
+                .setAttributionFlags(ATTRIBUTION_FLAG_RECEIVER | ATTRIBUTION_FLAG_TRUSTED)
+                .build();
+        DiscreteOp event2 = new DiscreteOpBuilder(mContext)
+                .setChainId(chainId)
+                .setAttributionFlags(ATTRIBUTION_FLAG_ACCESSOR | ATTRIBUTION_FLAG_TRUSTED)
+                .build();
+        List<DiscreteOp> events = new ArrayList<>();
+        events.add(event1);
+        events.add(event2);
+
+        LongSparseArray<DiscreteOpsSqlRegistry.AttributionChain> chains =
+                mDiscreteRegistry.createAttributionChains(events, new ArraySet<>());
+
+        assertThat(chains.size()).isGreaterThan(0);
+        DiscreteOpsSqlRegistry.AttributionChain chain = chains.get(chainId);
+        assertThat(chain).isNotNull();
+        assertThat(chain.isComplete()).isTrue();
+        assertThat(chain.getStart()).isEqualTo(event1);
+        assertThat(chain.getLastVisible()).isEqualTo(event2);
+    }
+
+    @Test
+    public void addToHistoricalOps() {
+        long beginTimeMillis = System.currentTimeMillis();
+        DiscreteOp event1 = new DiscreteOpBuilder(mContext)
+                .build();
+        DiscreteOp event2 = new DiscreteOpBuilder(mContext)
+                .setUid(123457)
+                .build();
+        mDiscreteRegistry.recordDiscreteAccess(event1);
+        flushDiscreteOpsToDatabase();
+        mDiscreteRegistry.recordDiscreteAccess(event2);
+
+        long endTimeMillis = System.currentTimeMillis() + 500;
+        AppOpsManager.HistoricalOps results = new AppOpsManager.HistoricalOps(beginTimeMillis,
+                endTimeMillis);
+
+        mDiscreteRegistry.addFilteredDiscreteOpsToHistoricalOps(results, beginTimeMillis,
+                endTimeMillis, 0, 0, null, null, null, 0, new ArraySet<>());
+        Log.i("Manjeet", "TEST read " + results);
+        assertWithMessage("results shouldn't be empty").that(results.isEmpty()).isFalse();
+    }
+
+    @Test
+    public void dump() {
+        DiscreteOp event1 = new DiscreteOpBuilder(mContext)
+                .setAccessTime(1732221340628L)
+                .setUid(12345)
+                .build();
+        DiscreteOp event2 = new DiscreteOpBuilder(mContext)
+                .setAccessTime(1732227340628L)
+                .setUid(123457)
+                .build();
+        mDiscreteRegistry.recordDiscreteAccess(event1);
+        flushDiscreteOpsToDatabase();
+        mDiscreteRegistry.recordDiscreteAccess(event2);
+    }
+
+    /** This clears in-memory cache and push records into the database. */
+    private void flushDiscreteOpsToDatabase() {
+        mDiscreteRegistry.writeAndClearOldAccessHistory();
+    }
+
+    /**
+     * Creates default op event for CAMERA app op with current time as access time
+     * and 1 minute duration
+     */
+    private static class DiscreteOpBuilder {
+        private int mUid;
+        private String mPackageName;
+        private String mAttributionTag;
+        private String mDeviceId;
+        private int mOpCode;
+        private int mOpFlags;
+        private int mAttributionFlags;
+        private int mUidState;
+        private long mChainId;
+        private long mAccessTime;
+        private long mDuration;
+
+        DiscreteOpBuilder(Context context) {
+            mUid = Process.myUid();
+            mPackageName = context.getPackageName();
+            mAttributionTag = null;
+            mDeviceId = String.valueOf(context.getDeviceId());
+            mOpCode = AppOpsManager.OP_CAMERA;
+            mOpFlags = AppOpsManager.OP_FLAG_SELF;
+            mAttributionFlags = ATTRIBUTION_FLAG_ACCESSOR;
+            mUidState = UID_STATE_FOREGROUND;
+            mChainId = AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE;
+            mAccessTime = System.currentTimeMillis();
+            mDuration = Duration.ofMinutes(1).toMillis();
+        }
+
+        DiscreteOpBuilder(DiscreteOp discreteOp) {
+            this.mUid = discreteOp.getUid();
+            this.mPackageName = discreteOp.getPackageName();
+            this.mAttributionTag = discreteOp.getAttributionTag();
+            this.mDeviceId = discreteOp.getDeviceId();
+            this.mOpCode = discreteOp.getOpCode();
+            this.mOpFlags = discreteOp.getOpFlags();
+            this.mAttributionFlags = discreteOp.getAttributionFlags();
+            this.mUidState = discreteOp.getUidState();
+            this.mChainId = discreteOp.getChainId();
+            this.mAccessTime = discreteOp.getAccessTime();
+            this.mDuration = discreteOp.getDuration();
+        }
+
+        public DiscreteOpBuilder setUid(int uid) {
+            this.mUid = uid;
+            return this;
+        }
+
+        public DiscreteOpBuilder setPackageName(String packageName) {
+            this.mPackageName = packageName;
+            return this;
+        }
+
+        public DiscreteOpBuilder setAttributionFlags(int attributionFlags) {
+            this.mAttributionFlags = attributionFlags;
+            return this;
+        }
+
+        public DiscreteOpBuilder setChainId(long chainId) {
+            this.mChainId = chainId;
+            return this;
+        }
+
+        public DiscreteOpBuilder setAccessTime(long accessTime) {
+            this.mAccessTime = accessTime;
+            return this;
+        }
+
+        public DiscreteOp build() {
+            return new DiscreteOp(mUid, mPackageName, mAttributionTag, mDeviceId, mOpCode, mOpFlags,
+                    mAttributionFlags, mUidState, mChainId, mAccessTime, mDuration);
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/appop/DiscreteAppOpPersistenceTest.java b/services/tests/servicestests/src/com/android/server/appop/DiscreteAppOpXmlPersistenceTest.java
similarity index 84%
rename from services/tests/servicestests/src/com/android/server/appop/DiscreteAppOpPersistenceTest.java
rename to services/tests/servicestests/src/com/android/server/appop/DiscreteAppOpXmlPersistenceTest.java
index 2ff0c62..ae973be 100644
--- a/services/tests/servicestests/src/com/android/server/appop/DiscreteAppOpPersistenceTest.java
+++ b/services/tests/servicestests/src/com/android/server/appop/DiscreteAppOpXmlPersistenceTest.java
@@ -47,9 +47,12 @@
 import java.io.File;
 import java.util.List;
 
+/**
+ * Test xml persistence implementation for discrete ops.
+ */
 @RunWith(AndroidJUnit4.class)
-public class DiscreteAppOpPersistenceTest {
-    private DiscreteRegistry mDiscreteRegistry;
+public class DiscreteAppOpXmlPersistenceTest {
+    private DiscreteOpsXmlRegistry mDiscreteRegistry;
     private final Object mLock = new Object();
     private File mMockDataDirectory;
     private final Context mContext =
@@ -61,13 +64,13 @@
     @Before
     public void setUp() {
         mMockDataDirectory = mContext.getDir("mock_data", Context.MODE_PRIVATE);
-        mDiscreteRegistry = new DiscreteRegistry(mLock, mMockDataDirectory);
+        mDiscreteRegistry = new DiscreteOpsXmlRegistry(mLock, mMockDataDirectory);
         mDiscreteRegistry.systemReady();
     }
 
     @After
     public void cleanUp() {
-        mDiscreteRegistry.writeAndClearAccessHistory();
+        mDiscreteRegistry.writeAndClearOldAccessHistory();
         FileUtils.deleteContents(mMockDataDirectory);
     }
 
@@ -87,14 +90,14 @@
 
         mDiscreteRegistry.recordDiscreteAccess(uid, packageName, deviceId, op, null, opFlags,
                 uidState, accessTime, duration, attributionFlags, attributionChainId,
-                DiscreteRegistry.ACCESS_TYPE_FINISH_OP);
+                DiscreteOpsXmlRegistry.ACCESS_TYPE_FINISH_OP);
 
         // Verify in-memory object is correct
         fetchDiscreteOpsAndValidate(uid, packageName, op, deviceId, null, accessTime,
                 duration, uidState, opFlags, attributionFlags, attributionChainId);
 
         // Write to disk and clear the in-memory object
-        mDiscreteRegistry.writeAndClearAccessHistory();
+        mDiscreteRegistry.writeAndClearOldAccessHistory();
 
         // Verify the storage file is created and then verify its content is correct
         File[] files = FileUtils.listFilesOrEmpty(mMockDataDirectory);
@@ -119,12 +122,12 @@
 
         mDiscreteRegistry.recordDiscreteAccess(uid, packageName, deviceId, op, null, opFlags,
                 uidState, accessTime, duration, attributionFlags, attributionChainId,
-                DiscreteRegistry.ACCESS_TYPE_START_OP);
+                DiscreteOpsXmlRegistry.ACCESS_TYPE_START_OP);
 
         fetchDiscreteOpsAndValidate(uid, packageName, op, deviceId, null, accessTime,
                 duration, uidState, opFlags, attributionFlags, attributionChainId);
 
-        mDiscreteRegistry.writeAndClearAccessHistory();
+        mDiscreteRegistry.writeAndClearOldAccessHistory();
 
         File[] files = FileUtils.listFilesOrEmpty(mMockDataDirectory);
         assertThat(files.length).isEqualTo(1);
@@ -136,30 +139,31 @@
             int expectedOp, String expectedDeviceId, String expectedAttrTag,
             long expectedAccessTime, long expectedAccessDuration, int expectedUidState,
             int expectedOpFlags, int expectedAttrFlags, int expectedAttrChainId) {
-        DiscreteRegistry.DiscreteOps discreteOps = mDiscreteRegistry.getAllDiscreteOps();
+        DiscreteOpsXmlRegistry.DiscreteOps discreteOps = mDiscreteRegistry.getAllDiscreteOps();
 
         assertThat(discreteOps.isEmpty()).isFalse();
         assertThat(discreteOps.mUids.size()).isEqualTo(1);
 
-        DiscreteRegistry.DiscreteUidOps discreteUidOps = discreteOps.mUids.get(expectedUid);
+        DiscreteOpsXmlRegistry.DiscreteUidOps discreteUidOps = discreteOps.mUids.get(expectedUid);
         assertThat(discreteUidOps.mPackages.size()).isEqualTo(1);
 
-        DiscreteRegistry.DiscretePackageOps discretePackageOps =
+        DiscreteOpsXmlRegistry.DiscretePackageOps discretePackageOps =
                 discreteUidOps.mPackages.get(expectedPackageName);
         assertThat(discretePackageOps.mPackageOps.size()).isEqualTo(1);
 
-        DiscreteRegistry.DiscreteOp discreteOp = discretePackageOps.mPackageOps.get(expectedOp);
+        DiscreteOpsXmlRegistry.DiscreteOp discreteOp =
+                discretePackageOps.mPackageOps.get(expectedOp);
         assertThat(discreteOp.mDeviceAttributedOps.size()).isEqualTo(1);
 
-        DiscreteRegistry.DiscreteDeviceOp discreteDeviceOp =
+        DiscreteOpsXmlRegistry.DiscreteDeviceOp discreteDeviceOp =
                 discreteOp.mDeviceAttributedOps.get(expectedDeviceId);
         assertThat(discreteDeviceOp.mAttributedOps.size()).isEqualTo(1);
 
-        List<DiscreteRegistry.DiscreteOpEvent> discreteOpEvents =
+        List<DiscreteOpsXmlRegistry.DiscreteOpEvent> discreteOpEvents =
                 discreteDeviceOp.mAttributedOps.get(expectedAttrTag);
         assertThat(discreteOpEvents.size()).isEqualTo(1);
 
-        DiscreteRegistry.DiscreteOpEvent discreteOpEvent = discreteOpEvents.get(0);
+        DiscreteOpsXmlRegistry.DiscreteOpEvent discreteOpEvent = discreteOpEvents.get(0);
         assertThat(discreteOpEvent.mNoteTime).isEqualTo(expectedAccessTime);
         assertThat(discreteOpEvent.mNoteDuration).isEqualTo(expectedAccessDuration);
         assertThat(discreteOpEvent.mUidState).isEqualTo(expectedUidState);
diff --git a/services/tests/servicestests/src/com/android/server/appop/DiscreteOpsMigrationAndRollbackTest.java b/services/tests/servicestests/src/com/android/server/appop/DiscreteOpsMigrationAndRollbackTest.java
new file mode 100644
index 0000000..21cc3ba
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/appop/DiscreteOpsMigrationAndRollbackTest.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS 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.appop;
+
+import static android.app.AppOpsManager.ATTRIBUTION_FLAG_ACCESSOR;
+import static android.app.AppOpsManager.UID_STATE_FOREGROUND;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.AppOpsManager;
+import android.companion.virtual.VirtualDeviceManager;
+import android.content.Context;
+import android.os.FileUtils;
+import android.os.Process;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.time.Duration;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class DiscreteOpsMigrationAndRollbackTest {
+    private final Context mContext =
+            InstrumentationRegistry.getInstrumentation().getTargetContext();
+    private static final String DATABASE_NAME = "test_app_ops.db";
+    private static final int RECORD_COUNT = 500;
+    private final File mMockDataDirectory = mContext.getDir("mock_data", Context.MODE_PRIVATE);
+    final Object mLock = new Object();
+
+    @After
+    @Before
+    public void clean() {
+        mContext.deleteDatabase(DATABASE_NAME);
+        FileUtils.deleteContents(mMockDataDirectory);
+    }
+
+    @Test
+    public void migrateFromXmlToSqlite() {
+        // write records to xml registry
+        DiscreteOpsXmlRegistry xmlRegistry = new DiscreteOpsXmlRegistry(mLock, mMockDataDirectory);
+        xmlRegistry.systemReady();
+        for (int i = 1; i <= RECORD_COUNT; i++) {
+            DiscreteOpsSqlRegistry.DiscreteOp opEvent =
+                    new DiscreteOpBuilder(mContext)
+                            .setChainId(i)
+                            .setUid(10000 + i) // make all records unique
+                            .build();
+            xmlRegistry.recordDiscreteAccess(opEvent.getUid(), opEvent.getPackageName(),
+                    opEvent.getDeviceId(), opEvent.getOpCode(), opEvent.getAttributionTag(),
+                    opEvent.getOpFlags(), opEvent.getUidState(), opEvent.getAccessTime(),
+                    opEvent.getDuration(), opEvent.getAttributionFlags(),
+                    (int) opEvent.getChainId(), DiscreteOpsRegistry.ACCESS_TYPE_NOTE_OP);
+        }
+        xmlRegistry.writeAndClearOldAccessHistory();
+        assertThat(xmlRegistry.readLargestChainIdFromDiskLocked()).isEqualTo(RECORD_COUNT);
+        assertThat(xmlRegistry.getAllDiscreteOps().mUids.size()).isEqualTo(RECORD_COUNT);
+
+        // migration to sql registry
+        DiscreteOpsSqlRegistry sqlRegistry = new DiscreteOpsSqlRegistry(mContext,
+                mContext.getDatabasePath(DATABASE_NAME));
+        sqlRegistry.systemReady();
+        DiscreteOpsMigrationHelper.migrateDiscreteOpsToSqlite(xmlRegistry, sqlRegistry);
+        List<DiscreteOpsSqlRegistry.DiscreteOp> sqlOps = sqlRegistry.getAllDiscreteOps();
+
+        assertThat(xmlRegistry.getAllDiscreteOps().mUids).isEmpty();
+        assertThat(sqlOps.size()).isEqualTo(RECORD_COUNT);
+        assertThat(sqlRegistry.getLargestAttributionChainId()).isEqualTo(RECORD_COUNT);
+    }
+
+    @Test
+    public void migrateFromSqliteToXml() {
+        // write to sql registry
+        DiscreteOpsSqlRegistry sqlRegistry = new DiscreteOpsSqlRegistry(mContext,
+                mContext.getDatabasePath(DATABASE_NAME));
+        sqlRegistry.systemReady();
+        for (int i = 1; i <= RECORD_COUNT; i++) {
+            DiscreteOpsSqlRegistry.DiscreteOp opEvent =
+                    new DiscreteOpBuilder(mContext)
+                            .setChainId(i)
+                            .setUid(RECORD_COUNT + i) // make all records unique
+                            .build();
+            sqlRegistry.recordDiscreteAccess(opEvent.getUid(), opEvent.getPackageName(),
+                    opEvent.getDeviceId(), opEvent.getOpCode(), opEvent.getAttributionTag(),
+                    opEvent.getOpFlags(), opEvent.getUidState(), opEvent.getAccessTime(),
+                    opEvent.getDuration(), opEvent.getAttributionFlags(),
+                    (int) opEvent.getChainId(), DiscreteOpsRegistry.ACCESS_TYPE_NOTE_OP);
+        }
+        sqlRegistry.writeAndClearOldAccessHistory();
+        assertThat(sqlRegistry.getAllDiscreteOps().size()).isEqualTo(RECORD_COUNT);
+        assertThat(sqlRegistry.getLargestAttributionChainId()).isEqualTo(RECORD_COUNT);
+
+        // migration to xml registry
+        DiscreteOpsXmlRegistry xmlRegistry = new DiscreteOpsXmlRegistry(mLock, mMockDataDirectory);
+        xmlRegistry.systemReady();
+        DiscreteOpsMigrationHelper.migrateDiscreteOpsToXml(sqlRegistry, xmlRegistry);
+        DiscreteOpsXmlRegistry.DiscreteOps xmlOps = xmlRegistry.getAllDiscreteOps();
+
+        assertThat(sqlRegistry.getAllDiscreteOps()).isEmpty();
+        assertThat(xmlOps.mLargestChainId).isEqualTo(RECORD_COUNT);
+        assertThat(xmlOps.mUids.size()).isEqualTo(RECORD_COUNT);
+    }
+
+    private static class DiscreteOpBuilder {
+        private int mUid;
+        private String mPackageName;
+        private String mAttributionTag;
+        private String mDeviceId;
+        private int mOpCode;
+        private int mOpFlags;
+        private int mAttributionFlags;
+        private int mUidState;
+        private int mChainId;
+        private long mAccessTime;
+        private long mDuration;
+
+        DiscreteOpBuilder(Context context) {
+            mUid = Process.myUid();
+            mPackageName = context.getPackageName();
+            mAttributionTag = null;
+            mDeviceId = VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT;
+            mOpCode = AppOpsManager.OP_CAMERA;
+            mOpFlags = AppOpsManager.OP_FLAG_SELF;
+            mAttributionFlags = ATTRIBUTION_FLAG_ACCESSOR;
+            mUidState = UID_STATE_FOREGROUND;
+            mChainId = AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE;
+            mAccessTime = System.currentTimeMillis();
+            mDuration = Duration.ofMinutes(1).toMillis();
+        }
+
+        public DiscreteOpBuilder setUid(int uid) {
+            this.mUid = uid;
+            return this;
+        }
+
+        public DiscreteOpBuilder setChainId(int chainId) {
+            this.mChainId = chainId;
+            return this;
+        }
+
+        public DiscreteOpsSqlRegistry.DiscreteOp build() {
+            return new DiscreteOpsSqlRegistry.DiscreteOp(mUid, mPackageName, mAttributionTag,
+                    mDeviceId,
+                    mOpCode, mOpFlags, mAttributionFlags, mUidState, mChainId, mAccessTime,
+                    mDuration);
+        }
+    }
+}
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 32578a7..bdbb495 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
@@ -340,8 +340,7 @@
         LocalServices.removeServiceForTest(DisplayManagerInternal.class);
         LocalServices.addService(DisplayManagerInternal.class, mDisplayManagerInternalMock);
 
-        doNothing().when(mInputManagerInternalMock)
-                .setMousePointerAccelerationEnabled(anyBoolean(), anyInt());
+        doNothing().when(mInputManagerInternalMock).setMouseScalingEnabled(anyBoolean(), anyInt());
         doNothing().when(mInputManagerInternalMock).setPointerIconVisible(anyBoolean(), anyInt());
         LocalServices.removeServiceForTest(InputManagerInternal.class);
         LocalServices.addService(InputManagerInternal.class, mInputManagerInternalMock);
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
index 0244164..4f55111 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystemTest.java
@@ -887,6 +887,21 @@
                 systemAudioModeRequest_fromAudioSystem);
     }
 
+    @Test
+    public void addAndStartAction_remove() throws Exception {
+        // utilize callback test to test if addAndStartAction(action, remove)
+        TestCallback callback = new TestCallback();
+
+        mHdmiCecLocalDeviceAudioSystem.setArcStatus(true);
+        mHdmiCecLocalDeviceAudioSystem.addAndStartAction(
+                new ArcTerminationActionFromAvr(mHdmiCecLocalDeviceAudioSystem, callback),
+                true);
+
+        mTestLooper.dispatchAll();
+        assertThat(mHdmiCecLocalDeviceAudioSystem.getActions(
+                ArcTerminationActionFromAvr.class).size()).isEqualTo(1);
+    }
+
     private static class TestCallback extends IHdmiControlCallback.Stub {
         private final ArrayList<Integer> mCallbackResult = new ArrayList<Integer>();
 
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayActorEnforcerTests.kt b/services/tests/servicestests/src/com/android/server/om/OverlayActorEnforcerTests.kt
index 72fa949..085ef53b 100644
--- a/services/tests/servicestests/src/com/android/server/om/OverlayActorEnforcerTests.kt
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayActorEnforcerTests.kt
@@ -196,6 +196,8 @@
                 },
                 ActorState.INVALID_OVERLAYABLE_ACTOR_NAME withCases {
                     fun TestState.mockActor(actorUri: String) {
+                        namedActorsMap = mapOf(VALID_NAMESPACE to
+                                mapOf(VALID_ACTOR_NAME to VALID_ACTOR_PKG))
                         targetOverlayableInfo = OverlayableInfo(OVERLAYABLE_NAME, actorUri)
                     }
                     failure("wrongScheme") {
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayReferenceMapperTests.kt b/services/tests/servicestests/src/com/android/server/om/OverlayReferenceMapperTests.kt
index 1352ade..ad6e467 100644
--- a/services/tests/servicestests/src/com/android/server/om/OverlayReferenceMapperTests.kt
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayReferenceMapperTests.kt
@@ -76,12 +76,10 @@
         val overlay1 = mockOverlay(1)
         mapper = mapper(
                 overlayToTargetToOverlayables = mapOf(
-                        overlay0.packageName to mapOf(
-                                target.packageName to target.overlayables.keys
-                        ),
-                        overlay1.packageName to mapOf(
-                                target.packageName to target.overlayables.keys
-                        )
+                        overlay0.packageName to android.util.Pair(target.packageName,
+                            target.overlayables.keys.first()),
+                        overlay1.packageName to android.util.Pair(target.packageName,
+                            target.overlayables.keys.first())
                 )
         )
         val existing = mapper.addInOrder(overlay0, overlay1) {
@@ -134,33 +132,6 @@
     }
 
     @Test
-    fun overlayWithMultipleTargets() {
-        val target0 = mockTarget(0)
-        val target1 = mockTarget(1)
-        val overlay = mockOverlay()
-        mapper = mapper(
-                overlayToTargetToOverlayables = mapOf(
-                        overlay.packageName to mapOf(
-                                target0.packageName to target0.overlayables.keys,
-                                target1.packageName to target1.overlayables.keys
-                        )
-                )
-        )
-        mapper.addInOrder(target0, target1, overlay) {
-            assertThat(it).containsExactly(ACTOR_PACKAGE_NAME)
-        }
-        assertMapping(ACTOR_PACKAGE_NAME to setOf(target0, target1, overlay))
-        mapper.remove(target0) {
-            assertThat(it).containsExactly(ACTOR_PACKAGE_NAME)
-        }
-        assertMapping(ACTOR_PACKAGE_NAME to setOf(target1, overlay))
-        mapper.remove(target1) {
-            assertThat(it).containsExactly(ACTOR_PACKAGE_NAME)
-        }
-        assertEmpty()
-    }
-
-    @Test
     fun overlayWithoutTarget() {
         val overlay = mockOverlay()
         mapper.addInOrder(overlay) {
@@ -174,6 +145,29 @@
         assertEmpty()
     }
 
+    @Test
+    fun targetWithNullOverlayable() {
+        val target = mockTarget()
+        val overlay = mockOverlay()
+        mapper = mapper(
+            overlayToTargetToOverlayables = mapOf(
+                overlay.packageName to android.util.Pair(target.packageName, null)
+            )
+        )
+        val existing = mapper.addInOrder(overlay) {
+            assertThat(it).isEmpty()
+        }
+        assertEmpty()
+        mapper.addInOrder(target, existing = existing) {
+            assertThat(it).containsExactly(ACTOR_PACKAGE_NAME)
+        }
+        assertMapping(ACTOR_PACKAGE_NAME to setOf(target))
+        mapper.remove(target) {
+            assertThat(it).containsExactly(ACTOR_PACKAGE_NAME)
+        }
+        assertEmpty()
+    }
+
     private fun OverlayReferenceMapper.addInOrder(
         vararg pkgs: AndroidPackage,
         existing: MutableMap<String, AndroidPackage> = mutableMapOf(),
@@ -219,17 +213,15 @@
         namedActors: Map<String, Map<String, String>> = Uri.parse(ACTOR_NAME).run {
             mapOf(authority!! to mapOf(pathSegments.first() to ACTOR_PACKAGE_NAME))
         },
-        overlayToTargetToOverlayables: Map<String, Map<String, Set<String>>> = mapOf(
-                mockOverlay().packageName to mapOf(
-                        mockTarget().run { packageName to overlayables.keys }
-                )
-        )
+        overlayToTargetToOverlayables: Map<String, android.util.Pair<String, String>> = mapOf(
+                mockOverlay().packageName to mockTarget().run { android.util.Pair(packageName!!,
+                    overlayables.keys.first()) })
     ) = OverlayReferenceMapper(deferRebuild, object : OverlayReferenceMapper.Provider {
         override fun getActorPkg(actor: String) =
                 OverlayActorEnforcer.getPackageNameForActor(actor, namedActors).first
 
         override fun getTargetToOverlayables(pkg: AndroidPackage) =
-                overlayToTargetToOverlayables[pkg.packageName] ?: emptyMap()
+                overlayToTargetToOverlayables[pkg.packageName]
     })
 
     private fun mockTarget(increment: Int = 0) = mockThrowOnUnmocked<AndroidPackage> {
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index 4e030d4..3ef360a 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -112,6 +112,7 @@
 import java.io.BufferedReader;
 import java.io.ByteArrayOutputStream;
 import java.io.File;
+import java.io.FileOutputStream;
 import java.io.FileReader;
 import java.io.IOException;
 import java.io.InputStreamReader;
@@ -556,6 +557,12 @@
         }
 
         @Override
+        void injectFinishWrite(@NonNull ResilientAtomicFile file,
+                @NonNull FileOutputStream os) throws IOException {
+            file.finishWrite(os, false /* doFsVerity */);
+        }
+
+        @Override
         void wtf(String message, Throwable th) {
             // During tests, WTF is fatal.
             fail(message + "  exception: " + th + "\n" + Log.getStackTraceString(th));
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
index c01283a..60a4b9a 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java
@@ -159,7 +159,7 @@
     /**
      * Test for the first launch path, no settings file available.
      */
-    public void FirstInitialize() {
+    public void testFirstInitialize() {
         assertResetTimes(START_TIME, START_TIME + INTERVAL);
     }
 
@@ -167,7 +167,7 @@
      * Test for {@link ShortcutService#getLastResetTimeLocked()} and
      * {@link ShortcutService#getNextResetTimeLocked()}.
      */
-    public void UpdateAndGetNextResetTimeLocked() {
+    public void testUpdateAndGetNextResetTimeLocked() {
         assertResetTimes(START_TIME, START_TIME + INTERVAL);
 
         // Advance clock.
@@ -196,7 +196,7 @@
     /**
      * Test for the restoration from saved file.
      */
-    public void InitializeFromSavedFile() {
+    public void testInitializeFromSavedFile() {
 
         mInjectedCurrentTimeMillis = START_TIME + 4 * INTERVAL + 50;
         assertResetTimes(START_TIME + 4 * INTERVAL, START_TIME + 5 * INTERVAL);
@@ -220,7 +220,7 @@
         // TODO Add various broken cases.
     }
 
-    public void LoadConfig() {
+    public void testLoadConfig() {
         mService.updateConfigurationLocked(
                 ConfigConstants.KEY_RESET_INTERVAL_SEC + "=123,"
                         + ConfigConstants.KEY_MAX_SHORTCUTS + "=4,"
@@ -261,22 +261,22 @@
     // === Test for app side APIs ===
 
     /** Test for {@link android.content.pm.ShortcutManager#getMaxShortcutCountForActivity()} */
-    public void GetMaxDynamicShortcutCount() {
+    public void testGetMaxDynamicShortcutCount() {
         assertEquals(MAX_SHORTCUTS, mManager.getMaxShortcutCountForActivity());
     }
 
     /** Test for {@link android.content.pm.ShortcutManager#getRemainingCallCount()} */
-    public void GetRemainingCallCount() {
+    public void testGetRemainingCallCount() {
         assertEquals(MAX_UPDATES_PER_INTERVAL, mManager.getRemainingCallCount());
     }
 
-    public void GetIconMaxDimensions() {
+    public void testGetIconMaxDimensions() {
         assertEquals(MAX_ICON_DIMENSION, mManager.getIconMaxWidth());
         assertEquals(MAX_ICON_DIMENSION, mManager.getIconMaxHeight());
     }
 
     /** Test for {@link android.content.pm.ShortcutManager#getRateLimitResetTime()} */
-    public void GetRateLimitResetTime() {
+    public void testGetRateLimitResetTime() {
         assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
 
         mInjectedCurrentTimeMillis = START_TIME + 4 * INTERVAL + 50;
@@ -284,7 +284,7 @@
         assertEquals(START_TIME + 5 * INTERVAL, mManager.getRateLimitResetTime());
     }
 
-    public void SetDynamicShortcuts() {
+    public void testSetDynamicShortcuts() {
         setCaller(CALLING_PACKAGE_1, USER_10);
 
         final Icon icon1 = Icon.createWithResource(getTestContext(), R.drawable.icon1);
@@ -354,7 +354,7 @@
         });
     }
 
-    public void AddDynamicShortcuts() {
+    public void testAddDynamicShortcuts() {
         setCaller(CALLING_PACKAGE_1, USER_10);
 
         final ShortcutInfo si1 = makeShortcut("shortcut1");
@@ -402,7 +402,8 @@
         });
     }
 
-    public void PushDynamicShortcut() {
+    /**
+    public void testPushDynamicShortcut() {
         // Change the max number of shortcuts.
         mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=5,"
                 + ShortcutService.ConfigConstants.KEY_SAVE_DELAY_MILLIS + "=1");
@@ -543,8 +544,9 @@
         verify(mMockUsageStatsManagerInternal, times(1)).reportShortcutUsage(
                 eq(CALLING_PACKAGE_1), eq("s9"), eq(USER_10));
     }
+    */
 
-    public void PushDynamicShortcut_CallsToUsageStatsManagerAreThrottled()
+    public void testPushDynamicShortcut_CallsToUsageStatsManagerAreThrottled()
             throws InterruptedException {
         mService.updateConfigurationLocked(
                 ShortcutService.ConfigConstants.KEY_SAVE_DELAY_MILLIS + "=500");
@@ -576,6 +578,7 @@
         Mockito.reset(mMockUsageStatsManagerInternal);
         for (int i = 2; i <= 10; i++) {
             final ShortcutInfo si = makeShortcut("s" + i);
+            setCaller(CALLING_PACKAGE_2, USER_10);
             mManager.pushDynamicShortcut(si);
         }
         verify(mMockUsageStatsManagerInternal, times(0)).reportShortcutUsage(
@@ -595,7 +598,7 @@
                 eq(CALLING_PACKAGE_2), any(), eq(USER_10));
     }
 
-    public void UnlimitedCalls() {
+    public void testUnlimitedCalls() {
         setCaller(CALLING_PACKAGE_1, USER_10);
 
         final ShortcutInfo si1 = makeShortcut("shortcut1");
@@ -626,7 +629,7 @@
         assertEquals(3, mManager.getRemainingCallCount());
     }
 
-    public void PublishWithNoActivity() {
+    public void testPublishWithNoActivity() {
         // If activity is not explicitly set, use the default one.
 
         mRunningUsers.put(USER_11, true);
@@ -732,7 +735,7 @@
         });
     }
 
-    public void PublishWithNoActivity_noMainActivityInPackage() {
+    public void testPublishWithNoActivity_noMainActivityInPackage() {
         mRunningUsers.put(USER_11, true);
 
         runWithCaller(CALLING_PACKAGE_2, USER_11, () -> {
@@ -751,7 +754,7 @@
         });
     }
 
-    public void DeleteDynamicShortcuts() {
+    public void testDeleteDynamicShortcuts() {
         final ShortcutInfo si1 = makeShortcut("shortcut1");
         final ShortcutInfo si2 = makeShortcut("shortcut2");
         final ShortcutInfo si3 = makeShortcut("shortcut3");
@@ -792,7 +795,7 @@
         assertEquals(2, mManager.getRemainingCallCount());
     }
 
-    public void DeleteAllDynamicShortcuts() {
+    public void testDeleteAllDynamicShortcuts() {
         final ShortcutInfo si1 = makeShortcut("shortcut1");
         final ShortcutInfo si2 = makeShortcut("shortcut2");
         final ShortcutInfo si3 = makeShortcut("shortcut3");
@@ -821,7 +824,7 @@
         assertEquals(1, mManager.getRemainingCallCount());
     }
 
-    public void Icons() throws IOException {
+    public void testIcons() throws IOException {
         final Icon res32x32 = Icon.createWithResource(getTestContext(), R.drawable.black_32x32);
         final Icon res64x64 = Icon.createWithResource(getTestContext(), R.drawable.black_64x64);
         final Icon res512x512 = Icon.createWithResource(getTestContext(), R.drawable.black_512x512);
@@ -1035,7 +1038,7 @@
 */
     }
 
-    public void CleanupDanglingBitmaps() throws Exception {
+    public void testCleanupDanglingBitmaps() throws Exception {
         assertBitmapDirectories(USER_10, EMPTY_STRINGS);
         assertBitmapDirectories(USER_11, EMPTY_STRINGS);
 
@@ -1204,7 +1207,7 @@
                         maxSize));
     }
 
-    public void ShrinkBitmap() {
+    public void testShrinkBitmap() {
         checkShrinkBitmap(32, 32, R.drawable.black_512x512, 32);
         checkShrinkBitmap(511, 511, R.drawable.black_512x512, 511);
         checkShrinkBitmap(512, 512, R.drawable.black_512x512, 512);
@@ -1227,7 +1230,7 @@
         return out.getFile();
     }
 
-    public void OpenIconFileForWrite() throws IOException {
+    public void testOpenIconFileForWrite() throws IOException {
         mInjectedCurrentTimeMillis = 1000;
 
         final File p10_1_1 = openIconFileForWriteAndGetPath(10, CALLING_PACKAGE_1);
@@ -1301,7 +1304,7 @@
         assertFalse(p11_1_3.getName().contains("_"));
     }
 
-    public void UpdateShortcuts() {
+    public void testUpdateShortcuts() {
         runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
             assertTrue(mManager.setDynamicShortcuts(list(
                     makeShortcut("s1"),
@@ -1432,7 +1435,7 @@
         });
     }
 
-    public void UpdateShortcuts_icons() {
+    public void testUpdateShortcuts_icons() {
         runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
             assertTrue(mManager.setDynamicShortcuts(list(
                     makeShortcut("s1")
@@ -1526,7 +1529,7 @@
         });
     }
 
-    public void ShortcutManagerGetShortcuts_shortcutTypes() {
+    public void testShortcutManagerGetShortcuts_shortcutTypes() {
 
         // Create 3 manifest and 3 dynamic shortcuts
         addManifestShortcutResource(
@@ -1617,7 +1620,7 @@
         assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_CACHED), "s1", "s2");
     }
 
-    public void CachedShortcuts() {
+    public void testCachedShortcuts() {
         runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
             assertTrue(mManager.setDynamicShortcuts(list(makeShortcut("s1"),
                     makeLongLivedShortcut("s2"), makeLongLivedShortcut("s3"),
@@ -1701,7 +1704,7 @@
                 "s2");
     }
 
-    public void CachedShortcuts_accessShortcutsPermission() {
+    public void testCachedShortcuts_accessShortcutsPermission() {
         runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
             assertTrue(mManager.setDynamicShortcuts(list(makeShortcut("s1"),
                     makeLongLivedShortcut("s2"), makeLongLivedShortcut("s3"),
@@ -1743,7 +1746,7 @@
         assertShortcutIds(mManager.getShortcuts(ShortcutManager.FLAG_MATCH_CACHED), "s3");
     }
 
-    public void CachedShortcuts_canPassShortcutLimit() {
+    public void testCachedShortcuts_canPassShortcutLimit() {
         // Change the max number of shortcuts.
         mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=4");
 
@@ -1781,7 +1784,7 @@
 
     // === Test for launcher side APIs ===
 
-    public void GetShortcuts() {
+    public void testGetShortcuts() {
 
         // Set up shortcuts.
 
@@ -1998,7 +2001,7 @@
                 "s1", "s3");
     }
 
-    public void GetShortcuts_shortcutKinds() throws Exception {
+    public void testGetShortcuts_shortcutKinds() throws Exception {
         // Create 3 manifest and 3 dynamic shortcuts
         addManifestShortcutResource(
                 new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
@@ -2109,7 +2112,7 @@
         });
     }
 
-    public void GetShortcuts_resolveStrings() throws Exception {
+    public void testGetShortcuts_resolveStrings() throws Exception {
         runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
             ShortcutInfo si = new ShortcutInfo.Builder(mClientContext)
                     .setId("id")
@@ -2157,7 +2160,7 @@
         });
     }
 
-    public void GetShortcuts_personsFlag() {
+    public void testGetShortcuts_personsFlag() {
         ShortcutInfo s = new ShortcutInfo.Builder(mClientContext, "id")
                 .setShortLabel("label")
                 .setActivity(new ComponentName(mClientContext, ShortcutActivity2.class))
@@ -2205,7 +2208,7 @@
     }
 
     // TODO resource
-    public void GetShortcutInfo() {
+    public void testGetShortcutInfo() {
         // Create shortcuts.
         setCaller(CALLING_PACKAGE_1);
         final ShortcutInfo s1_1 = makeShortcut(
@@ -2280,7 +2283,7 @@
         assertEquals("ABC", findById(list, "s1").getTitle());
     }
 
-    public void PinShortcutAndGetPinnedShortcuts() {
+    public void testPinShortcutAndGetPinnedShortcuts() {
         runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
             final ShortcutInfo s1_1 = makeShortcutWithTimestamp("s1", 1000);
             final ShortcutInfo s1_2 = makeShortcutWithTimestamp("s2", 2000);
@@ -2361,7 +2364,7 @@
      * This is similar to the above test, except it used "disable" instead of "remove".  It also
      * does "enable".
      */
-    public void DisableAndEnableShortcuts() {
+    public void testDisableAndEnableShortcuts() {
         runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
             final ShortcutInfo s1_1 = makeShortcutWithTimestamp("s1", 1000);
             final ShortcutInfo s1_2 = makeShortcutWithTimestamp("s2", 2000);
@@ -2486,7 +2489,7 @@
         });
     }
 
-    public void DisableShortcuts_thenRepublish() {
+    public void testDisableShortcuts_thenRepublish() {
         runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
             assertTrue(mManager.setDynamicShortcuts(list(
                     makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
@@ -2556,7 +2559,7 @@
         });
     }
 
-    public void PinShortcutAndGetPinnedShortcuts_multi() {
+    public void testPinShortcutAndGetPinnedShortcuts_multi() {
         // Create some shortcuts.
         runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
             assertTrue(mManager.setDynamicShortcuts(list(
@@ -2832,7 +2835,7 @@
         });
     }
 
-    public void PinShortcutAndGetPinnedShortcuts_assistant() {
+    public void testPinShortcutAndGetPinnedShortcuts_assistant() {
         // Create some shortcuts.
         runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
             assertTrue(mManager.setDynamicShortcuts(list(
@@ -2888,7 +2891,7 @@
         });
     }
 
-    public void PinShortcutAndGetPinnedShortcuts_crossProfile_plusLaunch() {
+    public void testPinShortcutAndGetPinnedShortcuts_crossProfile_plusLaunch() {
         // Create some shortcuts.
         runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
             assertTrue(mManager.setDynamicShortcuts(list(
@@ -3477,7 +3480,7 @@
         });
     }
 
-    public void StartShortcut() {
+    public void testStartShortcut() {
         // Create some shortcuts.
         runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
             final ShortcutInfo s1_1 = makeShortcut(
@@ -3612,7 +3615,7 @@
         // TODO Check extra, etc
     }
 
-    public void LauncherCallback() throws Throwable {
+    public void testLauncherCallback() throws Throwable {
         // Disable throttling for this test.
         mService.updateConfigurationLocked(
                 ConfigConstants.KEY_MAX_UPDATES_PER_INTERVAL + "=99999999,"
@@ -3778,7 +3781,7 @@
                 .isEmpty();
     }
 
-    public void LauncherCallback_crossProfile() throws Throwable {
+    public void testLauncherCallback_crossProfile() throws Throwable {
         prepareCrossProfileDataSet();
 
         final Handler h = new Handler(Looper.getMainLooper());
@@ -3901,7 +3904,7 @@
 
     // === Test for persisting ===
 
-    public void SaveAndLoadUser_empty() {
+    public void testSaveAndLoadUser_empty() {
         assertTrue(mManager.setDynamicShortcuts(list()));
 
         Log.i(TAG, "Saved state");
@@ -3918,7 +3921,7 @@
     /**
      * Try save and load, also stop/start the user.
      */
-    public void SaveAndLoadUser() {
+    public void testSaveAndLoadUser() {
         // First, create some shortcuts and save.
         runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
             final Icon icon1 = Icon.createWithResource(getTestContext(), R.drawable.black_64x16);
@@ -4059,7 +4062,7 @@
         // TODO Check all other fields
     }
 
-    public void LoadCorruptedShortcuts() throws Exception {
+    public void testLoadCorruptedShortcuts() throws Exception {
         initService();
 
         addPackage("com.android.chrome", 0, 0);
@@ -4073,7 +4076,7 @@
         assertNull(ShortcutPackage.loadFromFile(mService, user, corruptedShortcutPackage, false));
     }
 
-    public void SaveCorruptAndLoadUser() throws Exception {
+    public void testSaveCorruptAndLoadUser() throws Exception {
         // First, create some shortcuts and save.
         runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
             final Icon icon1 = Icon.createWithResource(getTestContext(), R.drawable.black_64x16);
@@ -4229,7 +4232,7 @@
         // TODO Check all other fields
     }
 
-    public void CleanupPackage() {
+    public void testCleanupPackage() {
         runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
             assertTrue(mManager.setDynamicShortcuts(list(
                     makeShortcut("s0_1"))));
@@ -4506,7 +4509,7 @@
         mService.saveDirtyInfo();
     }
 
-    public void CleanupPackage_republishManifests() {
+    public void testCleanupPackage_republishManifests() {
         addManifestShortcutResource(
                 new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
                 R.xml.shortcut_2);
@@ -4574,7 +4577,7 @@
         });
     }
 
-    public void HandleGonePackage_crossProfile() {
+    public void testHandleGonePackage_crossProfile() {
         // Create some shortcuts.
         runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
             assertTrue(mManager.setDynamicShortcuts(list(
@@ -4846,7 +4849,7 @@
         assertEquals(expected, spi.canRestoreTo(mService, pi, true));
     }
 
-    public void CanRestoreTo() {
+    public void testCanRestoreTo() {
         addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 10, "sig1");
         addPackage(CALLING_PACKAGE_2, CALLING_UID_2, 10, "sig1", "sig2");
         addPackage(CALLING_PACKAGE_3, CALLING_UID_3, 10, "sig1");
@@ -4909,7 +4912,7 @@
         checkCanRestoreTo(DISABLED_REASON_BACKUP_NOT_SUPPORTED, spi3, true, 10, true, "sig1");
     }
 
-    public void HandlePackageDelete() {
+    public void testHandlePackageDelete() {
         checkHandlePackageDeleteInner((userId, packageName) -> {
             uninstallPackage(userId, packageName);
             mService.mPackageMonitor.onReceive(getTestContext(),
@@ -4917,7 +4920,7 @@
         });
     }
 
-    public void HandlePackageDisable() {
+    public void testHandlePackageDisable() {
         checkHandlePackageDeleteInner((userId, packageName) -> {
             disablePackage(userId, packageName);
             mService.mPackageMonitor.onReceive(getTestContext(),
@@ -5049,7 +5052,7 @@
     }
 
     /** Almost ame as testHandlePackageDelete, except it doesn't uninstall packages. */
-    public void HandlePackageClearData() {
+    public void testHandlePackageClearData() {
         final Icon bmp32x32 = Icon.createWithBitmap(BitmapFactory.decodeResource(
                 getTestContext().getResources(), R.drawable.black_32x32));
         setCaller(CALLING_PACKAGE_1, USER_10);
@@ -5125,7 +5128,7 @@
         assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_3, USER_11));
     }
 
-    public void HandlePackageClearData_manifestRepublished() {
+    public void testHandlePackageClearData_manifestRepublished() {
 
         mRunningUsers.put(USER_11, true);
 
@@ -5167,7 +5170,7 @@
         });
     }
 
-    public void HandlePackageUpdate() throws Throwable {
+    public void testHandlePackageUpdate() throws Throwable {
         // Set up shortcuts and launchers.
 
         final Icon res32x32 = Icon.createWithResource(getTestContext(), R.drawable.black_32x32);
@@ -5341,7 +5344,7 @@
     /**
      * Test the case where an updated app has resource IDs changed.
      */
-    public void HandlePackageUpdate_resIdChanged() throws Exception {
+    public void testHandlePackageUpdate_resIdChanged() throws Exception {
         final Icon icon1 = Icon.createWithResource(getTestContext(), /* res ID */ 1000);
         final Icon icon2 = Icon.createWithResource(getTestContext(), /* res ID */ 1001);
 
@@ -5416,7 +5419,7 @@
         });
     }
 
-    public void HandlePackageUpdate_systemAppUpdate() {
+    public void testHandlePackageUpdate_systemAppUpdate() {
 
         // Package1 is a system app.  Package 2 is not a system app, so it's not scanned
         // in this test at all.
@@ -5522,7 +5525,7 @@
                 mService.getUserShortcutsLocked(USER_10).getLastAppScanOsFingerprint());
     }
 
-    public void HandlePackageChanged() {
+    public void testHandlePackageChanged() {
         final ComponentName ACTIVITY1 = new ComponentName(CALLING_PACKAGE_1, "act1");
         final ComponentName ACTIVITY2 = new ComponentName(CALLING_PACKAGE_1, "act2");
 
@@ -5652,7 +5655,7 @@
         });
     }
 
-    public void HandlePackageUpdate_activityNoLongerMain() throws Throwable {
+    public void testHandlePackageUpdate_activityNoLongerMain() throws Throwable {
         runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
             assertTrue(mManager.setDynamicShortcuts(list(
                     makeShortcutWithActivity("s1a",
@@ -5738,7 +5741,7 @@
      * - Unpinned dynamic shortcuts
      * - Bitmaps
      */
-    public void BackupAndRestore() {
+    public void testBackupAndRestore() {
 
         assertFileNotExists("user-0/shortcut_dump/restore-0-start.txt");
         assertFileNotExists("user-0/shortcut_dump/restore-1-payload.xml");
@@ -5759,7 +5762,7 @@
         checkBackupAndRestore_success(/*firstRestore=*/ true);
     }
 
-    public void BackupAndRestore_backupRestoreTwice() {
+    public void testBackupAndRestore_backupRestoreTwice() {
         prepareForBackupTest();
 
         checkBackupAndRestore_success(/*firstRestore=*/ true);
@@ -5775,7 +5778,7 @@
         checkBackupAndRestore_success(/*firstRestore=*/ false);
     }
 
-    public void BackupAndRestore_restoreToNewVersion() {
+    public void testBackupAndRestore_restoreToNewVersion() {
         prepareForBackupTest();
 
         addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 2);
@@ -5784,7 +5787,7 @@
         checkBackupAndRestore_success(/*firstRestore=*/ true);
     }
 
-    public void BackupAndRestore_restoreToSuperSetSignatures() {
+    public void testBackupAndRestore_restoreToSuperSetSignatures() {
         prepareForBackupTest();
 
         // Change package signatures.
@@ -5981,7 +5984,7 @@
         });
     }
 
-    public void BackupAndRestore_publisherWrongSignature() {
+    public void testBackupAndRestore_publisherWrongSignature() {
         prepareForBackupTest();
 
         addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 10, "sigx"); // different signature
@@ -5989,7 +5992,7 @@
         checkBackupAndRestore_publisherNotRestored(ShortcutInfo.DISABLED_REASON_SIGNATURE_MISMATCH);
     }
 
-    public void BackupAndRestore_publisherNoLongerBackupTarget() {
+    public void testBackupAndRestore_publisherNoLongerBackupTarget() {
         prepareForBackupTest();
 
         updatePackageInfo(CALLING_PACKAGE_1,
@@ -6118,7 +6121,7 @@
         });
     }
 
-    public void BackupAndRestore_launcherLowerVersion() {
+    public void testBackupAndRestore_launcherLowerVersion() {
         prepareForBackupTest();
 
         addPackage(LAUNCHER_1, LAUNCHER_UID_1, 0); // Lower version
@@ -6127,7 +6130,7 @@
         checkBackupAndRestore_success(/*firstRestore=*/ true);
     }
 
-    public void BackupAndRestore_launcherWrongSignature() {
+    public void testBackupAndRestore_launcherWrongSignature() {
         prepareForBackupTest();
 
         addPackage(LAUNCHER_1, LAUNCHER_UID_1, 10, "sigx"); // different signature
@@ -6135,7 +6138,7 @@
         checkBackupAndRestore_launcherNotRestored(true);
     }
 
-    public void BackupAndRestore_launcherNoLongerBackupTarget() {
+    public void testBackupAndRestore_launcherNoLongerBackupTarget() {
         prepareForBackupTest();
 
         updatePackageInfo(LAUNCHER_1,
@@ -6240,7 +6243,7 @@
         });
     }
 
-    public void BackupAndRestore_launcherAndPackageNoLongerBackupTarget() {
+    public void testBackupAndRestore_launcherAndPackageNoLongerBackupTarget() {
         prepareForBackupTest();
 
         updatePackageInfo(CALLING_PACKAGE_1,
@@ -6338,7 +6341,7 @@
         });
     }
 
-    public void BackupAndRestore_disabled() {
+    public void testBackupAndRestore_disabled() {
         prepareCrossProfileDataSet();
 
         // Before doing backup & restore, disable s1.
@@ -6403,7 +6406,7 @@
     }
 
 
-    public void BackupAndRestore_manifestRePublished() {
+    public void testBackupAndRestore_manifestRePublished() {
         // Publish two manifest shortcuts.
         addManifestShortcutResource(
                 new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
@@ -6494,7 +6497,7 @@
      * logcat.
      * - if it has allowBackup=false, we don't touch any of the existing shortcuts.
      */
-    public void BackupAndRestore_appAlreadyInstalledWhenRestored() {
+    public void testBackupAndRestore_appAlreadyInstalledWhenRestored() {
         // Pre-backup.  Same as testBackupAndRestore_manifestRePublished().
 
         // Publish two manifest shortcuts.
@@ -6619,7 +6622,7 @@
     /**
      * Test for restoring the pre-P backup format.
      */
-    public void BackupAndRestore_api27format() throws Exception {
+    public void testBackupAndRestore_api27format() throws Exception {
         final byte[] payload = readTestAsset("shortcut/shortcut_api27_backup.xml").getBytes();
 
         addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 10, "22222");
@@ -6657,7 +6660,7 @@
 
     }
 
-    public void SaveAndLoad_crossProfile() {
+    public void testSaveAndLoad_crossProfile() {
         prepareCrossProfileDataSet();
 
         dumpsysOnLogcat("Before save & load");
@@ -6860,7 +6863,7 @@
                         .getPackageUserId());
     }
 
-    public void OnApplicationActive_permission() {
+    public void testOnApplicationActive_permission() {
         assertExpectException(SecurityException.class, "Missing permission", () ->
                 mManager.onApplicationActive(CALLING_PACKAGE_1, USER_10));
 
@@ -6869,7 +6872,7 @@
         mManager.onApplicationActive(CALLING_PACKAGE_1, USER_10);
     }
 
-    public void GetShareTargets_permission() {
+    public void testGetShareTargets_permission() {
         addPackage(CHOOSER_ACTIVITY_PACKAGE, CHOOSER_ACTIVITY_UID, 10, "sig1");
         mInjectedChooserActivity =
                 ComponentName.createRelative(CHOOSER_ACTIVITY_PACKAGE, ".ChooserActivity");
@@ -6888,7 +6891,7 @@
         });
     }
 
-    public void HasShareTargets_permission() {
+    public void testHasShareTargets_permission() {
         assertExpectException(SecurityException.class, "Missing permission", () ->
                 mManager.hasShareTargets(CALLING_PACKAGE_1));
 
@@ -6897,7 +6900,7 @@
         mManager.hasShareTargets(CALLING_PACKAGE_1);
     }
 
-    public void isSharingShortcut_permission() throws IntentFilter.MalformedMimeTypeException {
+    public void testisSharingShortcut_permission() throws IntentFilter.MalformedMimeTypeException {
         setCaller(LAUNCHER_1, USER_10);
 
         IntentFilter filter_any = new IntentFilter();
@@ -6912,18 +6915,18 @@
         mManager.hasShareTargets(CALLING_PACKAGE_1);
     }
 
-    public void Dumpsys_crossProfile() {
+    public void testDumpsys_crossProfile() {
         prepareCrossProfileDataSet();
         dumpsysOnLogcat("test1", /* force= */ true);
     }
 
-    public void Dumpsys_withIcons() throws IOException {
-        Icons();
+    public void testDumpsys_withIcons() throws IOException {
+        testIcons();
         // Dump after having some icons.
         dumpsysOnLogcat("test1", /* force= */ true);
     }
 
-    public void ManifestShortcut_publishOnUnlockUser() {
+    public void testManifestShortcut_publishOnUnlockUser() {
         addManifestShortcutResource(
                 new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
                 R.xml.shortcut_1);
@@ -7137,7 +7140,7 @@
         assertNull(mService.getPackageShortcutForTest(LAUNCHER_1, USER_10));
     }
 
-    public void ManifestShortcut_publishOnBroadcast() {
+    public void testManifestShortcut_publishOnBroadcast() {
         // First, no packages are installed.
         uninstallPackage(USER_10, CALLING_PACKAGE_1);
         uninstallPackage(USER_10, CALLING_PACKAGE_2);
@@ -7393,7 +7396,7 @@
         });
     }
 
-    public void ManifestShortcuts_missingMandatoryFields() {
+    public void testManifestShortcuts_missingMandatoryFields() {
         // Start with no apps installed.
         uninstallPackage(USER_10, CALLING_PACKAGE_1);
         uninstallPackage(USER_10, CALLING_PACKAGE_2);
@@ -7462,7 +7465,7 @@
         });
     }
 
-    public void ManifestShortcuts_intentDefinitions() {
+    public void testManifestShortcuts_intentDefinitions() {
         addManifestShortcutResource(
                 new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
                 R.xml.shortcut_error_4);
@@ -7604,7 +7607,7 @@
         });
     }
 
-    public void ManifestShortcuts_checkAllFields() {
+    public void testManifestShortcuts_checkAllFields() {
         mService.handleUnlockUser(USER_10);
 
         // Package 1 updated, which has one valid manifest shortcut and one invalid.
@@ -7709,7 +7712,7 @@
         });
     }
 
-    public void ManifestShortcuts_localeChange() throws InterruptedException {
+    public void testManifestShortcuts_localeChange() throws InterruptedException {
         mService.handleUnlockUser(USER_10);
 
         // Package 1 updated, which has one valid manifest shortcut and one invalid.
@@ -7813,7 +7816,7 @@
         });
     }
 
-    public void ManifestShortcuts_updateAndDisabled_notPinned() {
+    public void testManifestShortcuts_updateAndDisabled_notPinned() {
         mService.handleUnlockUser(USER_10);
 
         // First, just publish a manifest shortcut.
@@ -7853,7 +7856,7 @@
         });
     }
 
-    public void ManifestShortcuts_updateAndDisabled_pinned() {
+    public void testManifestShortcuts_updateAndDisabled_pinned() {
         mService.handleUnlockUser(USER_10);
 
         // First, just publish a manifest shortcut.
@@ -7909,7 +7912,7 @@
         });
     }
 
-    public void ManifestShortcuts_duplicateInSingleActivity() {
+    public void testManifestShortcuts_duplicateInSingleActivity() {
         mService.handleUnlockUser(USER_10);
 
         // The XML has two shortcuts with the same ID.
@@ -7934,7 +7937,7 @@
         });
     }
 
-    public void ManifestShortcuts_duplicateInTwoActivities() {
+    public void testManifestShortcuts_duplicateInTwoActivities() {
         mService.handleUnlockUser(USER_10);
 
         // ShortcutActivity has shortcut ms1
@@ -7986,7 +7989,7 @@
     /**
      * Manifest shortcuts cannot override shortcuts that were published via the APIs.
      */
-    public void ManifestShortcuts_cannotOverrideNonManifest() {
+    public void testManifestShortcuts_cannotOverrideNonManifest() {
         mService.handleUnlockUser(USER_10);
 
         // Create a non-pinned dynamic shortcut and a non-dynamic pinned shortcut.
@@ -8059,7 +8062,7 @@
     /**
      * Make sure the APIs won't work on manifest shortcuts.
      */
-    public void ManifestShortcuts_immutable() {
+    public void testManifestShortcuts_immutable() {
         mService.handleUnlockUser(USER_10);
 
         // Create a non-pinned manifest shortcut, a pinned shortcut that was originally
@@ -8152,7 +8155,7 @@
     /**
      * Make sure the APIs won't work on manifest shortcuts.
      */
-    public void ManifestShortcuts_tooMany() {
+    public void testManifestShortcuts_tooMany() {
         // Change the max number of shortcuts.
         mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=3");
 
@@ -8171,7 +8174,7 @@
         });
     }
 
-    public void MaxShortcutCount_set() {
+    public void testMaxShortcutCount_set() {
         // Change the max number of shortcuts.
         mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=3");
 
@@ -8252,7 +8255,7 @@
         });
     }
 
-    public void MaxShortcutCount_add() {
+    public void testMaxShortcutCount_add() {
         // Change the max number of shortcuts.
         mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=3");
 
@@ -8379,7 +8382,7 @@
         });
     }
 
-    public void MaxShortcutCount_update() {
+    public void testMaxShortcutCount_update() {
         // Change the max number of shortcuts.
         mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=3");
 
@@ -8470,7 +8473,7 @@
         });
     }
 
-    public void ShortcutsPushedOutByManifest() {
+    public void testShortcutsPushedOutByManifest() {
         // Change the max number of shortcuts.
         mService.updateConfigurationLocked(ConfigConstants.KEY_MAX_SHORTCUTS + "=3");
 
@@ -8578,7 +8581,7 @@
         });
     }
 
-    public void ReturnedByServer() {
+    public void testReturnedByServer() {
         // Package 1 updated, with manifest shortcuts.
         addManifestShortcutResource(
                 new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
@@ -8624,7 +8627,7 @@
         });
     }
 
-    public void IsForegroundDefaultLauncher_true() {
+    public void testIsForegroundDefaultLauncher_true() {
         // random uid in the USER_10 range.
         final int uid = 1000024;
 
@@ -8635,7 +8638,7 @@
     }
 
 
-    public void IsForegroundDefaultLauncher_defaultButNotForeground() {
+    public void testIsForegroundDefaultLauncher_defaultButNotForeground() {
         // random uid in the USER_10 range.
         final int uid = 1000024;
 
@@ -8645,7 +8648,7 @@
         assertFalse(mInternal.isForegroundDefaultLauncher("default", uid));
     }
 
-    public void IsForegroundDefaultLauncher_foregroundButNotDefault() {
+    public void testIsForegroundDefaultLauncher_foregroundButNotDefault() {
         // random uid in the USER_10 range.
         final int uid = 1000024;
 
@@ -8655,7 +8658,7 @@
         assertFalse(mInternal.isForegroundDefaultLauncher("another", uid));
     }
 
-    public void ParseShareTargetsFromManifest() {
+    public void testParseShareTargetsFromManifest() {
         // These values must exactly match the content of shortcuts_share_targets.xml resource
         List<ShareTargetInfo> expectedValues = new ArrayList<>();
         expectedValues.add(new ShareTargetInfo(
@@ -8707,7 +8710,7 @@
         }
     }
 
-    public void ShareTargetInfo_saveToXml() throws IOException, XmlPullParserException {
+    public void testShareTargetInfo_saveToXml() throws IOException, XmlPullParserException {
         List<ShareTargetInfo> expectedValues = new ArrayList<>();
         expectedValues.add(new ShareTargetInfo(
                 new ShareTargetInfo.TargetData[]{new ShareTargetInfo.TargetData(
@@ -8773,7 +8776,7 @@
         }
     }
 
-    public void IsSharingShortcut() throws IntentFilter.MalformedMimeTypeException {
+    public void testIsSharingShortcut() throws IntentFilter.MalformedMimeTypeException {
         addManifestShortcutResource(
                 new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
                 R.xml.shortcut_share_targets);
@@ -8823,7 +8826,7 @@
                 filter_any));
     }
 
-    public void IsSharingShortcut_PinnedAndCachedOnlyShortcuts()
+    public void testIsSharingShortcut_PinnedAndCachedOnlyShortcuts()
             throws IntentFilter.MalformedMimeTypeException {
         addManifestShortcutResource(
                 new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
@@ -8880,7 +8883,7 @@
                 filter_any));
     }
 
-    public void AddingShortcuts_ExcludesHiddenFromLauncherShortcuts() {
+    public void testAddingShortcuts_ExcludesHiddenFromLauncherShortcuts() {
         final ShortcutInfo s1 = makeShortcutExcludedFromLauncher("s1");
         final ShortcutInfo s2 = makeShortcutExcludedFromLauncher("s2");
         final ShortcutInfo s3 = makeShortcutExcludedFromLauncher("s3");
@@ -8901,7 +8904,7 @@
         });
     }
 
-    public void UpdateShortcuts_ExcludesHiddenFromLauncherShortcuts() {
+    public void testUpdateShortcuts_ExcludesHiddenFromLauncherShortcuts() {
         final ShortcutInfo s1 = makeShortcut("s1");
         final ShortcutInfo s2 = makeShortcut("s2");
         final ShortcutInfo s3 = makeShortcut("s3");
@@ -8914,7 +8917,7 @@
         });
     }
 
-    public void PinHiddenShortcuts_ThrowsException() {
+    public void testPinHiddenShortcuts_ThrowsException() {
         runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
             assertThrown(IllegalArgumentException.class, () -> {
                 mManager.requestPinShortcut(makeShortcutExcludedFromLauncher("s1"), null);
diff --git a/services/tests/timetests/src/com/android/server/timezonedetector/ConfigInternalForTests.java b/services/tests/timetests/src/com/android/server/timezonedetector/ConfigInternalForTests.java
new file mode 100644
index 0000000..47e3dc8
--- /dev/null
+++ b/services/tests/timetests/src/com/android/server/timezonedetector/ConfigInternalForTests.java
@@ -0,0 +1,108 @@
+/*
+ * 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.timezonedetector;
+
+import android.annotation.UserIdInt;
+
+public final class ConfigInternalForTests {
+
+    static final @UserIdInt int USER_ID = 9876;
+
+    static final ConfigurationInternal CONFIG_USER_RESTRICTED_AUTO_DISABLED =
+            new ConfigurationInternal.Builder()
+                    .setUserId(USER_ID)
+                    .setTelephonyDetectionFeatureSupported(true)
+                    .setGeoDetectionFeatureSupported(true)
+                    .setTelephonyFallbackSupported(false)
+                    .setGeoDetectionRunInBackgroundEnabled(false)
+                    .setEnhancedMetricsCollectionEnabled(false)
+                    .setUserConfigAllowed(false)
+                    .setAutoDetectionEnabledSetting(false)
+                    .setLocationEnabledSetting(true)
+                    .setGeoDetectionEnabledSetting(false)
+                    .build();
+
+    static final ConfigurationInternal CONFIG_USER_RESTRICTED_AUTO_ENABLED =
+            new ConfigurationInternal.Builder()
+                    .setUserId(USER_ID)
+                    .setTelephonyDetectionFeatureSupported(true)
+                    .setGeoDetectionFeatureSupported(true)
+                    .setTelephonyFallbackSupported(false)
+                    .setGeoDetectionRunInBackgroundEnabled(false)
+                    .setEnhancedMetricsCollectionEnabled(false)
+                    .setUserConfigAllowed(false)
+                    .setAutoDetectionEnabledSetting(true)
+                    .setLocationEnabledSetting(true)
+                    .setGeoDetectionEnabledSetting(true)
+                    .build();
+
+    static final ConfigurationInternal CONFIG_AUTO_DETECT_NOT_SUPPORTED =
+            new ConfigurationInternal.Builder()
+                    .setUserId(USER_ID)
+                    .setTelephonyDetectionFeatureSupported(false)
+                    .setGeoDetectionFeatureSupported(false)
+                    .setTelephonyFallbackSupported(false)
+                    .setGeoDetectionRunInBackgroundEnabled(false)
+                    .setEnhancedMetricsCollectionEnabled(false)
+                    .setUserConfigAllowed(true)
+                    .setAutoDetectionEnabledSetting(false)
+                    .setLocationEnabledSetting(true)
+                    .setGeoDetectionEnabledSetting(false)
+                    .build();
+
+    static final ConfigurationInternal CONFIG_AUTO_DISABLED_GEO_DISABLED =
+            new ConfigurationInternal.Builder()
+                    .setUserId(USER_ID)
+                    .setTelephonyDetectionFeatureSupported(true)
+                    .setGeoDetectionFeatureSupported(true)
+                    .setTelephonyFallbackSupported(false)
+                    .setGeoDetectionRunInBackgroundEnabled(false)
+                    .setEnhancedMetricsCollectionEnabled(false)
+                    .setUserConfigAllowed(true)
+                    .setAutoDetectionEnabledSetting(false)
+                    .setLocationEnabledSetting(true)
+                    .setGeoDetectionEnabledSetting(false)
+                    .build();
+
+    static final ConfigurationInternal CONFIG_AUTO_ENABLED_GEO_DISABLED =
+            new ConfigurationInternal.Builder()
+                    .setUserId(USER_ID)
+                    .setTelephonyDetectionFeatureSupported(true)
+                    .setGeoDetectionFeatureSupported(true)
+                    .setTelephonyFallbackSupported(false)
+                    .setGeoDetectionRunInBackgroundEnabled(false)
+                    .setEnhancedMetricsCollectionEnabled(false)
+                    .setUserConfigAllowed(true)
+                    .setAutoDetectionEnabledSetting(true)
+                    .setLocationEnabledSetting(true)
+                    .setGeoDetectionEnabledSetting(false)
+                    .build();
+
+    static final ConfigurationInternal CONFIG_AUTO_ENABLED_GEO_ENABLED =
+            new ConfigurationInternal.Builder()
+                    .setUserId(USER_ID)
+                    .setTelephonyDetectionFeatureSupported(true)
+                    .setGeoDetectionFeatureSupported(true)
+                    .setTelephonyFallbackSupported(false)
+                    .setGeoDetectionRunInBackgroundEnabled(false)
+                    .setEnhancedMetricsCollectionEnabled(false)
+                    .setUserConfigAllowed(true)
+                    .setAutoDetectionEnabledSetting(true)
+                    .setLocationEnabledSetting(true)
+                    .setGeoDetectionEnabledSetting(true)
+                    .build();
+}
diff --git a/services/tests/timetests/src/com/android/server/timezonedetector/FakeServiceConfigAccessor.java b/services/tests/timetests/src/com/android/server/timezonedetector/FakeServiceConfigAccessor.java
index fc6afe4..aeb4d9a 100644
--- a/services/tests/timetests/src/com/android/server/timezonedetector/FakeServiceConfigAccessor.java
+++ b/services/tests/timetests/src/com/android/server/timezonedetector/FakeServiceConfigAccessor.java
@@ -31,7 +31,7 @@
 /**
  * A partially implemented, fake implementation of ServiceConfigAccessor for tests.
  *
- * <p>This class has rudamentary support for multiple users, but unlike the real thing, it doesn't
+ * <p>This class has rudimentary support for multiple users, but unlike the real thing, it doesn't
  * simulate that some settings are global and shared between users. It also delivers config updates
  * synchronously.
  */
diff --git a/services/tests/timetests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java b/services/tests/timetests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
index e52e8b6..47a9b2c 100644
--- a/services/tests/timetests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
+++ b/services/tests/timetests/src/com/android/server/timezonedetector/TimeZoneDetectorStrategyImplTest.java
@@ -35,6 +35,12 @@
 
 import static com.android.server.SystemTimeZone.TIME_ZONE_CONFIDENCE_HIGH;
 import static com.android.server.SystemTimeZone.TIME_ZONE_CONFIDENCE_LOW;
+import static com.android.server.timezonedetector.ConfigInternalForTests.CONFIG_AUTO_DETECT_NOT_SUPPORTED;
+import static com.android.server.timezonedetector.ConfigInternalForTests.CONFIG_AUTO_DISABLED_GEO_DISABLED;
+import static com.android.server.timezonedetector.ConfigInternalForTests.CONFIG_AUTO_ENABLED_GEO_DISABLED;
+import static com.android.server.timezonedetector.ConfigInternalForTests.CONFIG_AUTO_ENABLED_GEO_ENABLED;
+import static com.android.server.timezonedetector.ConfigInternalForTests.CONFIG_USER_RESTRICTED_AUTO_ENABLED;
+import static com.android.server.timezonedetector.ConfigInternalForTests.USER_ID;
 import static com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.TELEPHONY_SCORE_HIGH;
 import static com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.TELEPHONY_SCORE_HIGHEST;
 import static com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.TELEPHONY_SCORE_LOW;
@@ -68,6 +74,7 @@
 import android.app.timezonedetector.TelephonyTimeZoneSuggestion.MatchType;
 import android.app.timezonedetector.TelephonyTimeZoneSuggestion.Quality;
 import android.service.timezone.TimeZoneProviderStatus;
+import android.util.IndentingPrintWriter;
 
 import com.android.server.SystemTimeZone.TimeZoneConfidence;
 import com.android.server.timezonedetector.TimeZoneDetectorStrategyImpl.QualifiedTelephonyTimeZoneSuggestion;
@@ -82,6 +89,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.function.Function;
@@ -92,7 +100,6 @@
 @RunWith(JUnitParamsRunner.class)
 public class TimeZoneDetectorStrategyImplTest {
 
-    private static final @UserIdInt int USER_ID = 9876;
     private static final long ARBITRARY_ELAPSED_REALTIME_MILLIS = 1234;
     /** A time zone used for initialization that does not occur elsewhere in tests. */
     private static final String ARBITRARY_TIME_ZONE_ID = "Etc/UTC";
@@ -101,7 +108,7 @@
 
     // Telephony test cases are ordered so that each successive one is of the same or higher score
     // than the previous.
-    private static final TelephonyTestCase[] TELEPHONY_TEST_CASES = new TelephonyTestCase[] {
+    private static final TelephonyTestCase[] TELEPHONY_TEST_CASES = new TelephonyTestCase[]{
             newTelephonyTestCase(MATCH_TYPE_NETWORK_COUNTRY_ONLY,
                     QUALITY_MULTIPLE_ZONES_WITH_DIFFERENT_OFFSETS, TELEPHONY_SCORE_LOW),
             newTelephonyTestCase(MATCH_TYPE_NETWORK_COUNTRY_ONLY,
@@ -118,90 +125,6 @@
                     TELEPHONY_SCORE_HIGHEST),
     };
 
-    private static final ConfigurationInternal CONFIG_USER_RESTRICTED_AUTO_DISABLED =
-            new ConfigurationInternal.Builder()
-                    .setUserId(USER_ID)
-                    .setTelephonyDetectionFeatureSupported(true)
-                    .setGeoDetectionFeatureSupported(true)
-                    .setTelephonyFallbackSupported(false)
-                    .setGeoDetectionRunInBackgroundEnabled(false)
-                    .setEnhancedMetricsCollectionEnabled(false)
-                    .setUserConfigAllowed(false)
-                    .setAutoDetectionEnabledSetting(false)
-                    .setLocationEnabledSetting(true)
-                    .setGeoDetectionEnabledSetting(false)
-                    .build();
-
-    private static final ConfigurationInternal CONFIG_USER_RESTRICTED_AUTO_ENABLED =
-            new ConfigurationInternal.Builder()
-                    .setUserId(USER_ID)
-                    .setTelephonyDetectionFeatureSupported(true)
-                    .setGeoDetectionFeatureSupported(true)
-                    .setTelephonyFallbackSupported(false)
-                    .setGeoDetectionRunInBackgroundEnabled(false)
-                    .setEnhancedMetricsCollectionEnabled(false)
-                    .setUserConfigAllowed(false)
-                    .setAutoDetectionEnabledSetting(true)
-                    .setLocationEnabledSetting(true)
-                    .setGeoDetectionEnabledSetting(true)
-                    .build();
-
-    private static final ConfigurationInternal CONFIG_AUTO_DETECT_NOT_SUPPORTED =
-            new ConfigurationInternal.Builder()
-                    .setUserId(USER_ID)
-                    .setTelephonyDetectionFeatureSupported(false)
-                    .setGeoDetectionFeatureSupported(false)
-                    .setTelephonyFallbackSupported(false)
-                    .setGeoDetectionRunInBackgroundEnabled(false)
-                    .setEnhancedMetricsCollectionEnabled(false)
-                    .setUserConfigAllowed(true)
-                    .setAutoDetectionEnabledSetting(false)
-                    .setLocationEnabledSetting(true)
-                    .setGeoDetectionEnabledSetting(false)
-                    .build();
-
-    private static final ConfigurationInternal CONFIG_AUTO_DISABLED_GEO_DISABLED =
-            new ConfigurationInternal.Builder()
-                    .setUserId(USER_ID)
-                    .setTelephonyDetectionFeatureSupported(true)
-                    .setGeoDetectionFeatureSupported(true)
-                    .setTelephonyFallbackSupported(false)
-                    .setGeoDetectionRunInBackgroundEnabled(false)
-                    .setEnhancedMetricsCollectionEnabled(false)
-                    .setUserConfigAllowed(true)
-                    .setAutoDetectionEnabledSetting(false)
-                    .setLocationEnabledSetting(true)
-                    .setGeoDetectionEnabledSetting(false)
-                    .build();
-
-    private static final ConfigurationInternal CONFIG_AUTO_ENABLED_GEO_DISABLED =
-            new ConfigurationInternal.Builder()
-                    .setUserId(USER_ID)
-                    .setTelephonyDetectionFeatureSupported(true)
-                    .setGeoDetectionFeatureSupported(true)
-                    .setTelephonyFallbackSupported(false)
-                    .setGeoDetectionRunInBackgroundEnabled(false)
-                    .setEnhancedMetricsCollectionEnabled(false)
-                    .setUserConfigAllowed(true)
-                    .setAutoDetectionEnabledSetting(true)
-                    .setLocationEnabledSetting(true)
-                    .setGeoDetectionEnabledSetting(false)
-                    .build();
-
-    private static final ConfigurationInternal CONFIG_AUTO_ENABLED_GEO_ENABLED =
-            new ConfigurationInternal.Builder()
-                    .setUserId(USER_ID)
-                    .setTelephonyDetectionFeatureSupported(true)
-                    .setGeoDetectionFeatureSupported(true)
-                    .setTelephonyFallbackSupported(false)
-                    .setGeoDetectionRunInBackgroundEnabled(false)
-                    .setEnhancedMetricsCollectionEnabled(false)
-                    .setUserConfigAllowed(true)
-                    .setAutoDetectionEnabledSetting(true)
-                    .setLocationEnabledSetting(true)
-                    .setGeoDetectionEnabledSetting(true)
-                    .build();
-
     private static final TelephonyTimeZoneAlgorithmStatus TELEPHONY_ALGORITHM_RUNNING_STATUS =
             new TelephonyTimeZoneAlgorithmStatus(DETECTION_ALGORITHM_STATUS_RUNNING);
 
@@ -421,7 +344,7 @@
                 new QualifiedTelephonyTimeZoneSuggestion(slotIndex1TimeZoneSuggestion,
                         TELEPHONY_SCORE_NONE);
         script.verifyLatestQualifiedTelephonySuggestionReceived(
-                SLOT_INDEX1, expectedSlotIndex1ScoredSuggestion)
+                        SLOT_INDEX1, expectedSlotIndex1ScoredSuggestion)
                 .verifyLatestQualifiedTelephonySuggestionReceived(SLOT_INDEX2, null);
         assertEquals(expectedSlotIndex1ScoredSuggestion,
                 mTimeZoneDetectorStrategy.findBestTelephonySuggestionForTests());
@@ -629,7 +552,7 @@
      */
     @Test
     public void testTelephonySuggestionMultipleSlotIndexSuggestionScoringAndSlotIndexBias() {
-        String[] zoneIds = { "Europe/London", "Europe/Paris" };
+        String[] zoneIds = {"Europe/London", "Europe/Paris"};
         TelephonyTimeZoneSuggestion emptySlotIndex1Suggestion = createEmptySlotIndex1Suggestion();
         TelephonyTimeZoneSuggestion emptySlotIndex2Suggestion = createEmptySlotIndex2Suggestion();
         QualifiedTelephonyTimeZoneSuggestion expectedEmptySlotIndex1ScoredSuggestion =
@@ -672,7 +595,7 @@
 
             // Assert internal service state.
             script.verifyLatestQualifiedTelephonySuggestionReceived(
-                    SLOT_INDEX1, expectedZoneSlotIndex1ScoredSuggestion)
+                            SLOT_INDEX1, expectedZoneSlotIndex1ScoredSuggestion)
                     .verifyLatestQualifiedTelephonySuggestionReceived(
                             SLOT_INDEX2, expectedEmptySlotIndex2ScoredSuggestion);
             assertEquals(expectedZoneSlotIndex1ScoredSuggestion,
@@ -805,14 +728,14 @@
         boolean bypassUserPolicyChecks = false;
         boolean expectedResult = true;
         script.simulateManualTimeZoneSuggestion(
-                USER_ID, manualSuggestion, bypassUserPolicyChecks, expectedResult)
+                        USER_ID, manualSuggestion, bypassUserPolicyChecks, expectedResult)
                 .verifyTimeZoneChangedAndReset(manualSuggestion);
 
         assertEquals(manualSuggestion, mTimeZoneDetectorStrategy.getLatestManualSuggestion());
     }
 
     @Test
-    @Parameters({ "true,true", "true,false", "false,true", "false,false" })
+    @Parameters({"true,true", "true,false", "false,true", "false,false"})
     public void testManualSuggestion_autoTimeEnabled_userRestrictions(
             boolean userConfigAllowed, boolean bypassUserPolicyChecks) {
         ConfigurationInternal config =
@@ -834,7 +757,7 @@
     }
 
     @Test
-    @Parameters({ "true,true", "true,false", "false,true", "false,false" })
+    @Parameters({"true,true", "true,false", "false,true", "false,false"})
     public void testManualSuggestion_autoTimeDisabled_userRestrictions(
             boolean userConfigAllowed, boolean bypassUserPolicyChecks) {
         ConfigurationInternal config =
@@ -849,7 +772,7 @@
         ManualTimeZoneSuggestion manualSuggestion = createManualSuggestion("Europe/Paris");
         boolean expectedResult = userConfigAllowed || bypassUserPolicyChecks;
         script.simulateManualTimeZoneSuggestion(
-                        USER_ID, manualSuggestion, bypassUserPolicyChecks, expectedResult);
+                USER_ID, manualSuggestion, bypassUserPolicyChecks, expectedResult);
         if (expectedResult) {
             script.verifyTimeZoneChangedAndReset(manualSuggestion);
             assertEquals(manualSuggestion, mTimeZoneDetectorStrategy.getLatestManualSuggestion());
@@ -1258,7 +1181,6 @@
             script.simulateLocationAlgorithmEvent(locationAlgorithmEvent)
                     .verifyTimeZoneChangedAndReset(locationAlgorithmEvent)
                     .verifyTelephonyFallbackIsEnabled(false);
-
         }
 
         // Demonstrate what happens when geolocation is uncertain when telephony fallback is
@@ -1569,7 +1491,7 @@
         boolean bypassUserPolicyChecks = false;
         boolean expectedResult = true;
         script.simulateManualTimeZoneSuggestion(
-                USER_ID, manualSuggestion, bypassUserPolicyChecks, expectedResult)
+                        USER_ID, manualSuggestion, bypassUserPolicyChecks, expectedResult)
                 .verifyTimeZoneChangedAndReset(manualSuggestion);
         expectedDeviceTimeZoneId = manualSuggestion.getZoneId();
         assertMetricsState(expectedInternalConfig, expectedDeviceTimeZoneId,
@@ -1880,6 +1802,7 @@
             boolean actualResult = mTimeZoneDetectorStrategy.suggestManualTimeZone(
                     userId, manualTimeZoneSuggestion, bypassUserPolicyChecks);
             assertEquals(expectedResult, actualResult);
+
             return this;
         }
 
@@ -2001,4 +1924,34 @@
         return new TelephonyTestCase(matchType, quality, expectedScore);
     }
 
+    static class FakeTimeZoneChangeEventListener implements TimeZoneChangeListener {
+        private final List<TimeZoneChangeEvent> mEvents = new ArrayList<>();
+
+        FakeTimeZoneChangeEventListener() {
+        }
+
+        @Override
+        public void process(TimeZoneChangeEvent event) {
+            mEvents.add(event);
+        }
+
+        public List<TimeZoneChangeEvent> getTimeZoneChangeEvents() {
+            return mEvents;
+        }
+
+        @Override
+        public void dump(IndentingPrintWriter ipw) {
+            // No-op for tests
+        }
+    }
+
+    private static void assertEmpty(Collection<?> collection) {
+        assertTrue(
+                "Expected empty, but contains (" + collection.size() + ") elements: " + collection,
+                collection.isEmpty());
+    }
+
+    private static void assertNotEmpty(Collection<?> collection) {
+        assertFalse("Expected not empty: " + collection, collection.isEmpty());
+    }
 }
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 af7f703..b332331 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ConditionProvidersTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ConditionProvidersTest.java
@@ -101,9 +101,12 @@
 
         mProviders.notifyConditions("package", msi, conditionsToNotify);
 
-        verify(mCallback).onConditionChanged(eq(Uri.parse("a")), eq(conditionsToNotify[0]));
-        verify(mCallback).onConditionChanged(eq(Uri.parse("b")), eq(conditionsToNotify[1]));
-        verify(mCallback).onConditionChanged(eq(Uri.parse("c")), eq(conditionsToNotify[2]));
+        verify(mCallback).onConditionChanged(eq(Uri.parse("a")), eq(conditionsToNotify[0]),
+                eq(100));
+        verify(mCallback).onConditionChanged(eq(Uri.parse("b")), eq(conditionsToNotify[1]),
+                eq(100));
+        verify(mCallback).onConditionChanged(eq(Uri.parse("c")), eq(conditionsToNotify[2]),
+                eq(100));
         verifyNoMoreInteractions(mCallback);
     }
 
@@ -121,8 +124,10 @@
 
         mProviders.notifyConditions("package", msi, conditionsToNotify);
 
-        verify(mCallback).onConditionChanged(eq(Uri.parse("a")), eq(conditionsToNotify[0]));
-        verify(mCallback).onConditionChanged(eq(Uri.parse("b")), eq(conditionsToNotify[1]));
+        verify(mCallback).onConditionChanged(eq(Uri.parse("a")), eq(conditionsToNotify[0]),
+                eq(100));
+        verify(mCallback).onConditionChanged(eq(Uri.parse("b")), eq(conditionsToNotify[1]),
+                eq(100));
 
         verifyNoMoreInteractions(mCallback);
     }
@@ -141,8 +146,10 @@
 
         mProviders.notifyConditions("package", msi, conditionsToNotify);
 
-        verify(mCallback).onConditionChanged(eq(Uri.parse("a")), eq(conditionsToNotify[0]));
-        verify(mCallback).onConditionChanged(eq(Uri.parse("b")), eq(conditionsToNotify[3]));
+        verify(mCallback).onConditionChanged(eq(Uri.parse("a")), eq(conditionsToNotify[0]),
+                eq(100));
+        verify(mCallback).onConditionChanged(eq(Uri.parse("b")), eq(conditionsToNotify[3]),
+                eq(100));
         verifyNoMoreInteractions(mCallback);
     }
 
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
index d1dc8d6..4f5cdb7 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
@@ -19,9 +19,13 @@
 import static android.service.notification.Adjustment.KEY_IMPORTANCE;
 import static android.service.notification.Adjustment.TYPE_CONTENT_RECOMMENDATION;
 import static android.service.notification.Adjustment.TYPE_NEWS;
+import static android.service.notification.Adjustment.TYPE_OTHER;
 import static android.service.notification.Adjustment.TYPE_PROMOTION;
+import static android.service.notification.Adjustment.TYPE_SOCIAL_MEDIA;
+import static android.service.notification.Flags.notificationClassification;
 
 import static com.android.server.notification.NotificationManagerService.DEFAULT_ALLOWED_ADJUSTMENTS;
+import static com.android.server.notification.NotificationManagerService.DEFAULT_ALLOWED_ADJUSTMENT_KEY_TYPES;
 
 import static com.google.common.truth.Truth.assertThat;
 
@@ -144,6 +148,17 @@
         mAssistants.readXml(parser, mNm::canUseManagedServices, false, USER_ALL);
     }
 
+    private void setDefaultAllowedAdjustmentKeyTypes(NotificationAssistants assistants) {
+        assistants.setAssistantAdjustmentKeyTypeState(TYPE_OTHER, false);
+        assistants.setAssistantAdjustmentKeyTypeState(TYPE_PROMOTION, false);
+        assistants.setAssistantAdjustmentKeyTypeState(TYPE_SOCIAL_MEDIA, false);
+        assistants.setAssistantAdjustmentKeyTypeState(TYPE_NEWS, false);
+        assistants.setAssistantAdjustmentKeyTypeState(TYPE_CONTENT_RECOMMENDATION, false);
+
+        for (int type : DEFAULT_ALLOWED_ADJUSTMENT_KEY_TYPES) {
+            assistants.setAssistantAdjustmentKeyTypeState(type, true);
+        }
+    }
 
     @Before
     public void setUp() throws Exception {
@@ -154,6 +169,9 @@
                 com.android.internal.R.string.config_defaultAssistantAccessComponent,
                 mCn.flattenToString());
         mAssistants = spy(mNm.new NotificationAssistants(mContext, mLock, mUserProfiles, miPm));
+        if (notificationClassification()) {
+            setDefaultAllowedAdjustmentKeyTypes(mAssistants);
+        }
         when(mNm.getBinderService()).thenReturn(mINm);
         mContext.ensureTestableResources();
 
@@ -695,7 +713,7 @@
         mAssistants.setAssistantAdjustmentKeyTypeState(TYPE_CONTENT_RECOMMENDATION, true);
 
         assertThat(mAssistants.getAllowedAdjustmentKeyTypes()).asList()
-                .containsExactlyElementsIn(List.of(TYPE_PROMOTION, TYPE_CONTENT_RECOMMENDATION));
+                .containsExactly(TYPE_PROMOTION, TYPE_CONTENT_RECOMMENDATION);
     }
 
     @Test
@@ -716,7 +734,7 @@
         writeXmlAndReload(USER_ALL);
 
         assertThat(mAssistants.getAllowedAdjustmentKeyTypes()).asList()
-                .containsExactlyElementsIn(List.of(TYPE_NEWS, TYPE_CONTENT_RECOMMENDATION));
+                .containsExactly(TYPE_NEWS, TYPE_CONTENT_RECOMMENDATION);
     }
 
     @Test
@@ -732,76 +750,146 @@
 
     @Test
     @EnableFlags(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI)
-    public void testSetAssistantAdjustmentKeyTypeStateForPackage_allowsAndDenies() {
-        // Given that a package is allowed to have its type adjusted,
-        String allowedPackage = "allowed.package";
-        assertThat(mAssistants.getTypeAdjustmentDeniedPackages()).isEmpty();
-        mAssistants.setTypeAdjustmentForPackageState(allowedPackage, true);
-
-        assertThat(mAssistants.getTypeAdjustmentDeniedPackages()).isEmpty();
-        assertTrue(mAssistants.isTypeAdjustmentAllowedForPackage(allowedPackage));
-
-        // Set type adjustment disallowed for this package
-        mAssistants.setTypeAdjustmentForPackageState(allowedPackage, false);
-
-        // Then the package is marked as denied
-        assertThat(mAssistants.getTypeAdjustmentDeniedPackages()).asList()
-                .containsExactly(allowedPackage);
-        assertFalse(mAssistants.isTypeAdjustmentAllowedForPackage(allowedPackage));
-
-        // Set type adjustment allowed again
-        mAssistants.setTypeAdjustmentForPackageState(allowedPackage, true);
-
-        // Then the package is marked as allowed again
-        assertThat(mAssistants.getTypeAdjustmentDeniedPackages()).isEmpty();
-        assertTrue(mAssistants.isTypeAdjustmentAllowedForPackage(allowedPackage));
+    public void testSetAssistantAdjustmentKeyTypeStateForPackage_usesGlobalDefault() {
+        String pkg = "my.package";
+        setDefaultAllowedAdjustmentKeyTypes(mAssistants);
+        assertThat(mAssistants.isTypeAdjustmentAllowedForPackage(pkg, TYPE_PROMOTION)).isTrue();
+        assertThat(mAssistants.isTypeAdjustmentAllowedForPackage(pkg, TYPE_NEWS)).isFalse();
+        assertThat(mAssistants.getAllowedAdjustmentKeyTypesForPackage(pkg)).asList()
+                .containsExactlyElementsIn(DEFAULT_ALLOWED_ADJUSTMENT_KEY_TYPES);
     }
 
     @Test
     @EnableFlags(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI)
-    public void testSetAssistantAdjustmentKeyTypeStateForPackage_deniesMultiple() {
-        // Given packages not allowed to have their type adjusted,
-        String deniedPkg1 = "denied.Pkg1";
-        String deniedPkg2 = "denied.Pkg2";
-        String deniedPkg3 = "denied.Pkg3";
-        // Set type adjustment disallowed for these packages
-        mAssistants.setTypeAdjustmentForPackageState(deniedPkg1, false);
-        mAssistants.setTypeAdjustmentForPackageState(deniedPkg2, false);
-        mAssistants.setTypeAdjustmentForPackageState(deniedPkg3, false);
+    public void testSetAssistantAdjustmentKeyTypeStateForPackage_allowsAndDenies() {
+        setDefaultAllowedAdjustmentKeyTypes(mAssistants);
+        // Given that a package is set to have a type adjustment allowed,
+        String pkg = "my.package";
+        mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(pkg, TYPE_NEWS, true);
 
-        // Then the packages are marked as denied
-        assertThat(mAssistants.getTypeAdjustmentDeniedPackages()).asList()
-                .containsExactlyElementsIn(List.of(deniedPkg1, deniedPkg2, deniedPkg3));
-        assertFalse(mAssistants.isTypeAdjustmentAllowedForPackage(deniedPkg1));
-        assertFalse(mAssistants.isTypeAdjustmentAllowedForPackage(deniedPkg2));
-        assertFalse(mAssistants.isTypeAdjustmentAllowedForPackage(deniedPkg3));
+        // The newly set state is the combination of the global default and the newly set type.
+        assertThat(mAssistants.getAllowedAdjustmentKeyTypesForPackage(pkg)).asList()
+                .containsExactly(TYPE_NEWS, TYPE_PROMOTION);
+        assertThat(mAssistants.isTypeAdjustmentAllowedForPackage(pkg, TYPE_NEWS)).isTrue();
 
-        // And when we re-allow one of them,
-        mAssistants.setTypeAdjustmentForPackageState(deniedPkg2, true);
+        // Set type adjustment disallowed for this package
+        mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(pkg, TYPE_NEWS, false);
+        mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(pkg, TYPE_PROMOTION, false);
 
-        // Then the rest of the original packages are still marked as denied.
-        assertThat(mAssistants.getTypeAdjustmentDeniedPackages()).asList()
-                .containsExactlyElementsIn(List.of(deniedPkg1, deniedPkg3));
-        assertFalse(mAssistants.isTypeAdjustmentAllowedForPackage(deniedPkg1));
-        assertTrue(mAssistants.isTypeAdjustmentAllowedForPackage(deniedPkg2));
-        assertFalse(mAssistants.isTypeAdjustmentAllowedForPackage(deniedPkg3));
+        // Then the package is marked as denied
+        assertThat(mAssistants.getAllowedAdjustmentKeyTypesForPackage(pkg)).isEmpty();
+        assertThat(mAssistants.isTypeAdjustmentAllowedForPackage(pkg, TYPE_NEWS)).isFalse();
+
+        // Set type adjustment allowed again
+        mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(pkg, TYPE_NEWS, true);
+        mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(pkg, TYPE_PROMOTION, true);
+
+        // Then the package is marked as allowed again
+        assertThat(mAssistants.getAllowedAdjustmentKeyTypesForPackage(pkg)).asList()
+                .containsExactly(TYPE_NEWS, TYPE_PROMOTION);
+        assertThat(mAssistants.isTypeAdjustmentAllowedForPackage(pkg, TYPE_NEWS)).isTrue();
+        assertThat(mAssistants.isTypeAdjustmentAllowedForPackage(pkg, TYPE_PROMOTION)).isTrue();
+
+        // Set type adjustment promotions false,
+        mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(pkg, TYPE_PROMOTION, false);
+        assertThat(mAssistants.getAllowedAdjustmentKeyTypesForPackage(pkg)).asList()
+                .containsExactly(TYPE_NEWS);
+        assertThat(mAssistants.isTypeAdjustmentAllowedForPackage(pkg, TYPE_NEWS)).isTrue();
+        assertThat(mAssistants.isTypeAdjustmentAllowedForPackage(pkg, TYPE_PROMOTION)).isFalse();
+    }
+
+    @Test
+    @EnableFlags(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI)
+    public void testSetAssistantAdjustmentKeyTypeStateForPackage_allowsMultiplePkgs() {
+        setDefaultAllowedAdjustmentKeyTypes(mAssistants);
+        // Given packages allowed to have their type adjusted to  TYPE_NEWS,
+        String allowedPkg1 = "allowed.Pkg1";
+        String allowedPkg2 = "allowed.Pkg2";
+        String allowedPkg3 = "allowed.Pkg3";
+        // Set type adjustment allowed for these packages
+        mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(allowedPkg1, TYPE_NEWS, true);
+        mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(allowedPkg2, TYPE_NEWS, true);
+        mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(allowedPkg3, TYPE_NEWS, true);
+
+        // The newly set state is the combination of the global default and the newly set type.
+        assertThat(mAssistants.getAllowedAdjustmentKeyTypesForPackage(allowedPkg1)).asList()
+                .containsExactly(TYPE_NEWS, TYPE_PROMOTION);
+        assertThat(mAssistants.getAllowedAdjustmentKeyTypesForPackage(allowedPkg2)).asList()
+                .containsExactly(TYPE_NEWS, TYPE_PROMOTION);
+        assertThat(mAssistants.getAllowedAdjustmentKeyTypesForPackage(allowedPkg3)).asList()
+                .containsExactly(TYPE_NEWS, TYPE_PROMOTION);
+        assertThat(mAssistants.isTypeAdjustmentAllowedForPackage(allowedPkg1, TYPE_NEWS)).isTrue();
+        assertThat(mAssistants.isTypeAdjustmentAllowedForPackage(allowedPkg2, TYPE_NEWS)).isTrue();
+        assertThat(mAssistants.isTypeAdjustmentAllowedForPackage(allowedPkg3, TYPE_NEWS)).isTrue();
+
+        // And when we deny some of them,
+        mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(allowedPkg2, TYPE_NEWS, false);
+        mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(allowedPkg2, TYPE_PROMOTION,
+                false);
+        mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(allowedPkg3, TYPE_PROMOTION,
+                false);
+
+        // Then the rest of the original packages are still marked as allowed.
+        assertThat(mAssistants.getAllowedAdjustmentKeyTypesForPackage(allowedPkg1)).asList()
+                .containsExactly(TYPE_NEWS, TYPE_PROMOTION);
+        assertThat(mAssistants.getAllowedAdjustmentKeyTypesForPackage(allowedPkg2)).isEmpty();
+        assertThat(mAssistants.getAllowedAdjustmentKeyTypesForPackage(allowedPkg3)).asList()
+                .containsExactly(TYPE_NEWS);
+        assertThat(mAssistants.isTypeAdjustmentAllowedForPackage(allowedPkg1, TYPE_NEWS)).isTrue();
+        assertThat(mAssistants.isTypeAdjustmentAllowedForPackage(allowedPkg2, TYPE_NEWS)).isFalse();
+        assertThat(mAssistants.isTypeAdjustmentAllowedForPackage(allowedPkg3, TYPE_NEWS)).isTrue();
     }
 
     @Test
     @EnableFlags(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI)
     public void testSetAssistantAdjustmentKeyTypeStateForPackage_readWriteXml() throws Exception {
+        setDefaultAllowedAdjustmentKeyTypes(mAssistants);
         mAssistants.loadDefaultsFromConfig(true);
         String deniedPkg1 = "denied.Pkg1";
         String allowedPkg2 = "allowed.Pkg2";
-        String deniedPkg3 = "denied.Pkg3";
+        String allowedPkg3 = "allowed.Pkg3";
         // Set type adjustment disallowed or allowed for these packages
-        mAssistants.setTypeAdjustmentForPackageState(deniedPkg1, false);
-        mAssistants.setTypeAdjustmentForPackageState(allowedPkg2, true);
-        mAssistants.setTypeAdjustmentForPackageState(deniedPkg3, false);
+        mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(deniedPkg1, TYPE_PROMOTION, false);
+        mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(allowedPkg2, TYPE_NEWS, true);
+        mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(allowedPkg3, TYPE_NEWS, true);
+        mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(allowedPkg3, TYPE_SOCIAL_MEDIA,
+                true);
 
         writeXmlAndReload(USER_ALL);
 
-        assertThat(mAssistants.getTypeAdjustmentDeniedPackages()).asList()
-                .containsExactlyElementsIn(List.of(deniedPkg1, deniedPkg3));
+        assertThat(mAssistants.getAllowedAdjustmentKeyTypesForPackage(deniedPkg1)).isEmpty();
+        assertThat(mAssistants.getAllowedAdjustmentKeyTypesForPackage(allowedPkg2)).asList()
+                .containsExactly(TYPE_NEWS, TYPE_PROMOTION);
+        assertThat(mAssistants.getAllowedAdjustmentKeyTypesForPackage(allowedPkg3)).asList()
+                .containsExactly(TYPE_NEWS, TYPE_SOCIAL_MEDIA, TYPE_PROMOTION);
+    }
+
+    @Test
+    @EnableFlags(android.app.Flags.FLAG_NOTIFICATION_CLASSIFICATION_UI)
+    public void testSetAssistantAdjustmentKeyTypeStateForPackage_noGlobalImpact() throws Exception {
+        setDefaultAllowedAdjustmentKeyTypes(mAssistants);
+        // When the global state is changed,
+        mAssistants.setAssistantAdjustmentKeyTypeState(TYPE_NEWS, true);
+
+        // The package state reflects the global state.
+        String pkg = "my.package";
+        assertThat(mAssistants.isTypeAdjustmentAllowedForPackage(pkg, TYPE_PROMOTION)).isTrue();
+        assertThat(mAssistants.isTypeAdjustmentAllowedForPackage(pkg, TYPE_NEWS)).isTrue();
+        assertThat(mAssistants.getAllowedAdjustmentKeyTypesForPackage(pkg)).asList()
+                .containsExactly(TYPE_NEWS, TYPE_PROMOTION);
+
+        // Once the package specific state is modified,
+        mAssistants.setAssistantAdjustmentKeyTypeStateForPackage(pkg, TYPE_SOCIAL_MEDIA, true);
+
+        // The package specific state combines the global state with those modifications
+        assertThat(mAssistants.isTypeAdjustmentAllowedForPackage(pkg, TYPE_SOCIAL_MEDIA)).isTrue();
+        assertThat(mAssistants.getAllowedAdjustmentKeyTypesForPackage(pkg)).asList()
+                .containsExactly(TYPE_NEWS, TYPE_PROMOTION, TYPE_SOCIAL_MEDIA);
+
+        // And further changes to the global state are ignored.
+        mAssistants.setAssistantAdjustmentKeyTypeState(TYPE_NEWS, false);
+        assertThat(mAssistants.isTypeAdjustmentAllowedForPackage(pkg, TYPE_NEWS)).isTrue();
+        assertThat(mAssistants.getAllowedAdjustmentKeyTypesForPackage(pkg)).asList()
+                .containsExactly(TYPE_NEWS, TYPE_PROMOTION, TYPE_SOCIAL_MEDIA);
     }
 }
\ No newline at end of file
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 601023f..7885c9b 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -365,6 +365,9 @@
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
 
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
 import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
 import java.io.ByteArrayInputStream;
@@ -380,9 +383,6 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.function.Consumer;
 
-import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
-import platform.test.runner.parameterized.Parameters;
-
 @SmallTest
 @RunWith(ParameterizedAndroidJunit4.class)
 @RunWithLooper
@@ -644,6 +644,9 @@
         doNothing().when(mContext).sendBroadcast(any(), anyString());
         doNothing().when(mContext).sendBroadcastAsUser(any(), any());
         doNothing().when(mContext).sendBroadcastAsUser(any(), any(), any());
+        doNothing().when(mContext).sendBroadcastMultiplePermissions(any(), any(), any(), any());
+        doReturn(mContext).when(mContext).createContextAsUser(eq(mUser), anyInt());
+
         TestableContentResolver cr = mock(TestableContentResolver.class);
         when(mContext.getContentResolver()).thenReturn(cr);
         doNothing().when(cr).registerContentObserver(any(), anyBoolean(), any(), anyInt());
@@ -7631,7 +7634,7 @@
         when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
         when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(true);
         when(mAssistants.isAdjustmentKeyTypeAllowed(anyInt())).thenReturn(true);
-        when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString())).thenReturn(true);
+        when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString(), anyInt())).thenReturn(true);
 
         // Set up notifications that will be adjusted
         final NotificationRecord r1 = spy(generateNotificationRecord(
@@ -11210,7 +11213,7 @@
         // Representative used to verify getCallingZenUser().
         mBinderService.getAutomaticZenRules();
 
-        verify(zenModeHelper).getAutomaticZenRules(eq(UserHandle.CURRENT));
+        verify(zenModeHelper).getAutomaticZenRules(eq(UserHandle.CURRENT), anyInt());
     }
 
     @Test
@@ -11222,7 +11225,7 @@
         // Representative used to verify getCallingZenUser().
         mBinderService.getAutomaticZenRules();
 
-        verify(zenModeHelper).getAutomaticZenRules(eq(Binder.getCallingUserHandle()));
+        verify(zenModeHelper).getAutomaticZenRules(eq(Binder.getCallingUserHandle()), anyInt());
     }
 
     /** Prepares for a zen-related test that uses a mocked {@link ZenModeHelper}. */
@@ -11235,7 +11238,8 @@
     }
 
     @Test
-    public void onZenModeChanged_sendsBroadcasts() throws Exception {
+    @DisableFlags(Flags.FLAG_NM_BINDER_PERF_REDUCE_ZEN_BROADCASTS)
+    public void onZenModeChanged_sendsBroadcasts_oldBehavior() throws Exception {
         when(mAmi.getCurrentUserId()).thenReturn(100);
         when(mUmInternal.getProfileIds(eq(100), anyBoolean())).thenReturn(new int[]{100, 101, 102});
         when(mConditionProviders.getAllowedPackages(anyInt())).then(new Answer<List<String>>() {
@@ -11288,6 +11292,74 @@
     }
 
     @Test
+    @EnableFlags(Flags.FLAG_NM_BINDER_PERF_REDUCE_ZEN_BROADCASTS)
+    public void onZenModeChanged_sendsBroadcasts() throws Exception {
+        when(mAmi.getCurrentUserId()).thenReturn(100);
+        when(mUmInternal.getProfileIds(eq(100), anyBoolean())).thenReturn(new int[]{100, 101, 102});
+        when(mConditionProviders.getAllowedPackages(anyInt())).then(new Answer<List<String>>() {
+            @Override
+            public List<String> answer(InvocationOnMock invocation) {
+                int userId = invocation.getArgument(0);
+                switch (userId) {
+                    case 100:
+                        return Lists.newArrayList("a", "b", "c");
+                    case 101:
+                        return Lists.newArrayList();
+                    case 102:
+                        return Lists.newArrayList("b");
+                    default:
+                        throw new IllegalArgumentException(
+                                "Why would you ask for packages of userId " + userId + "?");
+                }
+            }
+        });
+        Context context100 = mock(Context.class);
+        doReturn(context100).when(mContext).createContextAsUser(eq(UserHandle.of(100)), anyInt());
+        Context context101 = mock(Context.class);
+        doReturn(context101).when(mContext).createContextAsUser(eq(UserHandle.of(101)), anyInt());
+        Context context102 = mock(Context.class);
+        doReturn(context102).when(mContext).createContextAsUser(eq(UserHandle.of(102)), anyInt());
+
+        mService.getBinderService().setZenMode(Settings.Global.ZEN_MODE_NO_INTERRUPTIONS, null,
+                "testing!", false);
+        waitForIdle();
+
+        // Verify broadcasts per user: registered receivers first, then DND packages.
+        InOrder inOrder = inOrder(context100, context101, context102);
+
+        inOrder.verify(context100).sendBroadcastMultiplePermissions(
+                eqIntent(new Intent(ACTION_INTERRUPTION_FILTER_CHANGED)
+                        .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)),
+                eq(new String[0]), eq(new String[0]), eq(new String[] {"a", "b", "c"}));
+        inOrder.verify(context100).sendBroadcast(
+                eqIntent(new Intent(ACTION_INTERRUPTION_FILTER_CHANGED)
+                        .setPackage("a")
+                        .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT)));
+        inOrder.verify(context100).sendBroadcast(
+                eqIntent(new Intent(ACTION_INTERRUPTION_FILTER_CHANGED)
+                        .setPackage("b")
+                        .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT)));
+        inOrder.verify(context100).sendBroadcast(
+                eqIntent(new Intent(ACTION_INTERRUPTION_FILTER_CHANGED)
+                        .setPackage("c")
+                        .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT)));
+
+        inOrder.verify(context101).sendBroadcastMultiplePermissions(
+                eqIntent(new Intent(ACTION_INTERRUPTION_FILTER_CHANGED)
+                        .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)),
+                eq(new String[0]), eq(new String[0]), eq(new String[] {}));
+
+        inOrder.verify(context102).sendBroadcastMultiplePermissions(
+                eqIntent(new Intent(ACTION_INTERRUPTION_FILTER_CHANGED)
+                        .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)),
+                eq(new String[0]), eq(new String[0]), eq(new String[] {"b"}));
+        inOrder.verify(context102).sendBroadcast(
+                eqIntent(new Intent(ACTION_INTERRUPTION_FILTER_CHANGED)
+                        .setPackage("b")
+                        .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT)));
+    }
+
+    @Test
     @EnableFlags(android.app.Flags.FLAG_MODES_API)
     public void onAutomaticRuleStatusChanged_sendsBroadcastToRuleOwner() throws Exception {
         mService.mZenModeHelper.getCallbacks().forEach(c -> c.onAutomaticRuleStatusChanged(
@@ -17305,7 +17377,7 @@
                 NotificationManagerService.WorkerHandler.class);
         mService.setHandler(handler);
         when(mAssistants.isAdjustmentKeyTypeAllowed(anyInt())).thenReturn(true);
-        when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString())).thenReturn(true);
+        when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString(), anyInt())).thenReturn(true);
 
         Bundle signals = new Bundle();
         signals.putInt(KEY_TYPE, TYPE_NEWS);
@@ -17349,7 +17421,11 @@
                 NotificationManagerService.WorkerHandler.class);
         mService.setHandler(handler);
         when(mAssistants.isAdjustmentKeyTypeAllowed(anyInt())).thenReturn(true);
-        when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString())).thenReturn(true);
+        when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString(), eq(TYPE_NEWS)))
+                .thenReturn(true);
+        // Blocking adjustments for a different type does nothing
+        when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString(), eq(TYPE_PROMOTION)))
+                .thenReturn(false);
 
         Bundle signals = new Bundle();
         signals.putInt(KEY_TYPE, TYPE_NEWS);
@@ -17364,8 +17440,9 @@
 
         assertThat(r.getChannel().getId()).isEqualTo(NEWS_ID);
 
-        // When we block adjustments for this package
-        when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString())).thenReturn(false);
+        // When we block adjustments for this package/type
+        when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString(), eq(TYPE_PROMOTION)))
+                .thenReturn(false);
 
         signals.putInt(KEY_TYPE, TYPE_PROMOTION);
         mBinderService.applyAdjustmentFromAssistant(null, adjustment);
@@ -17695,7 +17772,7 @@
         when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
         when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(true);
         when(mAssistants.isAdjustmentKeyTypeAllowed(anyInt())).thenReturn(true);
-        when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString())).thenReturn(true);
+        when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString(), anyInt())).thenReturn(true);
 
         // Post a single notification
         final boolean hasOriginalSummary = false;
@@ -17735,7 +17812,7 @@
         when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
         when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(true);
         when(mAssistants.isAdjustmentKeyTypeAllowed(anyInt())).thenReturn(true);
-        when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString())).thenReturn(true);
+        when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString(), anyInt())).thenReturn(true);
 
         // Post grouped notifications
         final String originalGroupName = "originalGroup";
@@ -17784,7 +17861,7 @@
         when(mAssistants.isSameUser(any(), anyInt())).thenReturn(true);
         when(mAssistants.isServiceTokenValidLocked(any())).thenReturn(true);
         when(mAssistants.isAdjustmentKeyTypeAllowed(anyInt())).thenReturn(true);
-        when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString())).thenReturn(true);
+        when(mAssistants.isTypeAdjustmentAllowedForPackage(anyString(), anyInt())).thenReturn(true);
 
         // Post grouped notifications
         final String originalGroupName = "originalGroup";
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
index 8e79514..f41805d 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java
@@ -66,7 +66,6 @@
 import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__DENIED;
 import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__GRANTED;
 import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES__FSI_STATE__NOT_REQUESTED;
-import static com.android.server.notification.Flags.FLAG_NOTIFICATION_VERIFY_CHANNEL_SOUND_URI;
 import static com.android.server.notification.Flags.FLAG_PERSIST_INCOMPLETE_RESTORE_DATA;
 import static com.android.server.notification.NotificationChannelLogger.NotificationChannelEvent.NOTIFICATION_CHANNEL_UPDATED_BY_USER;
 import static com.android.server.notification.PreferencesHelper.DEFAULT_BUBBLE_PREFERENCE;
@@ -164,6 +163,7 @@
 import com.android.os.AtomsProto.PackageNotificationPreferences;
 import com.android.server.UiServiceTestCase;
 import com.android.server.notification.PermissionHelper.PackagePermission;
+import com.android.server.uri.UriGrantsManagerInternal;
 
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
@@ -179,6 +179,9 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
+import platform.test.runner.parameterized.Parameters;
+
 import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
 import java.io.ByteArrayInputStream;
@@ -199,9 +202,6 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ThreadLocalRandom;
 
-import platform.test.runner.parameterized.ParameterizedAndroidJunit4;
-import platform.test.runner.parameterized.Parameters;
-
 @SmallTest
 @RunWith(ParameterizedAndroidJunit4.class)
 @EnableFlags(FLAG_PERSIST_INCOMPLETE_RESTORE_DATA)
@@ -239,9 +239,10 @@
 
     private NotificationManager.Policy mTestNotificationPolicy;
 
-    private PreferencesHelper mHelper;
-    // fresh object for testing xml reading
-    private PreferencesHelper mXmlHelper;
+    private TestPreferencesHelper mHelper;
+    // fresh object for testing xml reading; also TestPreferenceHelper in order to avoid interacting
+    // with real IpcDataCaches
+    private TestPreferencesHelper mXmlHelper;
     private AudioAttributes mAudioAttributes;
     private NotificationChannelLoggerFake mLogger = new NotificationChannelLoggerFake();
 
@@ -378,10 +379,10 @@
         when(mUserProfiles.getCurrentProfileIds()).thenReturn(currentProfileIds);
         when(mClock.millis()).thenReturn(System.currentTimeMillis());
 
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
+        mHelper = new TestPreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
                 mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mUserProfiles,
                 mUgmInternal, false, mClock);
-        mXmlHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
+        mXmlHelper = new TestPreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
                 mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mUserProfiles,
                 mUgmInternal, false, mClock);
         resetZenModeHelper();
@@ -793,7 +794,7 @@
 
     @Test
     public void testReadXml_oldXml_migrates() throws Exception {
-        mXmlHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
+        mXmlHelper = new TestPreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
                 mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mUserProfiles,
                 mUgmInternal, /* showReviewPermissionsNotification= */ true, mClock);
 
@@ -929,7 +930,7 @@
 
     @Test
     public void testReadXml_newXml_noMigration_showPermissionNotification() throws Exception {
-        mXmlHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
+        mXmlHelper = new TestPreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
                 mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mUserProfiles,
                 mUgmInternal, /* showReviewPermissionsNotification= */ true, mClock);
 
@@ -988,7 +989,7 @@
 
     @Test
     public void testReadXml_newXml_permissionNotificationOff() throws Exception {
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
+        mHelper = new TestPreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
                 mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mUserProfiles,
                 mUgmInternal, /* showReviewPermissionsNotification= */ false, mClock);
 
@@ -1047,7 +1048,7 @@
 
     @Test
     public void testReadXml_newXml_noMigration_noPermissionNotification() throws Exception {
-        mHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
+        mHelper = new TestPreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
                 mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mUserProfiles,
                 mUgmInternal, /* showReviewPermissionsNotification= */ true, mClock);
 
@@ -1641,7 +1642,7 @@
         serializer.flush();
 
         // simulate load after reboot
-        mXmlHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
+        mXmlHelper = new TestPreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
                 mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mUserProfiles,
                 mUgmInternal, false, mClock);
         loadByteArrayXml(baos.toByteArray(), false, USER_ALL);
@@ -1696,7 +1697,7 @@
                 Duration.ofDays(2).toMillis() + System.currentTimeMillis());
 
         // simulate load after reboot
-        mXmlHelper = new PreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
+        mXmlHelper = new TestPreferencesHelper(getContext(), mPm, mHandler, mMockZenModeHelper,
                 mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mUserProfiles,
                 mUgmInternal, false, mClock);
         loadByteArrayXml(xml.getBytes(), false, USER_ALL);
@@ -1774,10 +1775,10 @@
         when(contentResolver.getResourceId(ANDROID_RES_SOUND_URI)).thenReturn(resId).thenThrow(
                 new FileNotFoundException("")).thenReturn(resId);
 
-        mHelper = new PreferencesHelper(mContext, mPm, mHandler, mMockZenModeHelper,
+        mHelper = new TestPreferencesHelper(mContext, mPm, mHandler, mMockZenModeHelper,
                 mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mUserProfiles,
                 mUgmInternal, false, mClock);
-        mXmlHelper = new PreferencesHelper(mContext, mPm, mHandler, mMockZenModeHelper,
+        mXmlHelper = new TestPreferencesHelper(mContext, mPm, mHandler, mMockZenModeHelper,
                 mPermissionHelper, mPermissionManager, mLogger, mAppOpsManager, mUserProfiles,
                 mUgmInternal, false, mClock);
 
@@ -3190,7 +3191,6 @@
     }
 
     @Test
-    @EnableFlags(FLAG_NOTIFICATION_VERIFY_CHANNEL_SOUND_URI)
     public void testCreateChannel_noSoundUriPermission_contentSchemeVerified() {
         final Uri sound = Uri.parse(SCHEME_CONTENT + "://media/test/sound/uri");
 
@@ -3210,7 +3210,6 @@
     }
 
     @Test
-    @EnableFlags(FLAG_NOTIFICATION_VERIFY_CHANNEL_SOUND_URI)
     public void testCreateChannel_noSoundUriPermission_fileSchemaIgnored() {
         final Uri sound = Uri.parse(SCHEME_FILE + "://path/sound");
 
@@ -3229,7 +3228,6 @@
     }
 
     @Test
-    @EnableFlags(FLAG_NOTIFICATION_VERIFY_CHANNEL_SOUND_URI)
     public void testCreateChannel_noSoundUriPermission_resourceSchemaIgnored() {
         final Uri sound = Uri.parse(SCHEME_ANDROID_RESOURCE + "://resId/sound");
 
@@ -6573,4 +6571,223 @@
         mHelper.setCanBePromoted(PKG_P, UID_P, false, false);
         assertThat(mHelper.canBePromoted(PKG_P, UID_P)).isTrue();
     }
+
+    @Test
+    @EnableFlags(android.app.Flags.FLAG_NM_BINDER_PERF_CACHE_CHANNELS)
+    public void testInvalidateChannelCache_invalidateOnCreationAndChange() {
+        mHelper.resetCacheInvalidation();
+        NotificationChannel channel = new NotificationChannel("id", "name", IMPORTANCE_DEFAULT);
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false, UID_N_MR1,
+                false);
+
+        // new channel should invalidate the cache.
+        assertThat(mHelper.hasCacheBeenInvalidated()).isTrue();
+
+        // when the channel data is updated, should invalidate the cache again after that.
+        mHelper.resetCacheInvalidation();
+        NotificationChannel newChannel = channel.copy();
+        newChannel.setName("new name");
+        newChannel.setImportance(IMPORTANCE_HIGH);
+        mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, newChannel, true, UID_N_MR1, false);
+        assertThat(mHelper.hasCacheBeenInvalidated()).isTrue();
+
+        // also for conversations
+        mHelper.resetCacheInvalidation();
+        String parentId = "id";
+        String convId = "conversation";
+        NotificationChannel conv = new NotificationChannel(
+                String.format(CONVERSATION_CHANNEL_ID_FORMAT, parentId, convId), "conversation",
+                IMPORTANCE_DEFAULT);
+        conv.setConversationId(parentId, convId);
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, conv, true, false, UID_N_MR1,
+                false);
+        assertThat(mHelper.hasCacheBeenInvalidated()).isTrue();
+
+        mHelper.resetCacheInvalidation();
+        NotificationChannel newConv = conv.copy();
+        newConv.setName("changed");
+        mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, newConv, true, UID_N_MR1, false);
+        assertThat(mHelper.hasCacheBeenInvalidated()).isTrue();
+    }
+
+    @Test
+    @EnableFlags(android.app.Flags.FLAG_NM_BINDER_PERF_CACHE_CHANNELS)
+    public void testInvalidateChannelCache_invalidateOnDelete() {
+        NotificationChannel channel = new NotificationChannel("id", "name", IMPORTANCE_DEFAULT);
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false, UID_N_MR1,
+                false);
+
+        // ignore any invalidations up until now
+        mHelper.resetCacheInvalidation();
+
+        mHelper.deleteNotificationChannel(PKG_N_MR1, UID_N_MR1, "id", UID_N_MR1, false);
+        assertThat(mHelper.hasCacheBeenInvalidated()).isTrue();
+
+        // recreate channel and now permanently delete
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false, UID_N_MR1,
+                false);
+        mHelper.resetCacheInvalidation();
+        mHelper.permanentlyDeleteNotificationChannel(PKG_N_MR1, UID_N_MR1, "id");
+        assertThat(mHelper.hasCacheBeenInvalidated()).isTrue();
+    }
+
+    @Test
+    @EnableFlags(android.app.Flags.FLAG_NM_BINDER_PERF_CACHE_CHANNELS)
+    public void testInvalidateChannelCache_noInvalidationWhenNoChange() {
+        NotificationChannel channel = new NotificationChannel("id", "name", IMPORTANCE_DEFAULT);
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false, UID_N_MR1,
+                false);
+
+        // ignore any invalidations up until now
+        mHelper.resetCacheInvalidation();
+
+        // newChannel, same as the old channel
+        NotificationChannel newChannel = channel.copy();
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, newChannel, true, false, UID_N_MR1,
+                false);
+        mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, newChannel, true, UID_N_MR1, false);
+
+        // because there were no effective changes, we should not see any cache invalidations
+        assertThat(mHelper.hasCacheBeenInvalidated()).isFalse();
+
+        // deletions of a nonexistent channel also don't change anything
+        mHelper.resetCacheInvalidation();
+        mHelper.deleteNotificationChannel(PKG_N_MR1, UID_N_MR1, "nonexistent", UID_N_MR1, false);
+        assertThat(mHelper.hasCacheBeenInvalidated()).isFalse();
+    }
+
+    @Test
+    @EnableFlags(android.app.Flags.FLAG_NM_BINDER_PERF_CACHE_CHANNELS)
+    public void testInvalidateCache_multipleUsersAndPackages() {
+        // Setup: create channels for:
+        // pkg O, user
+        // pkg O, work (same channel ID, different user)
+        // pkg N_MR1, user
+        // pkg N_MR1, user, conversation child of above
+        String p2u1ConvId = String.format(CONVERSATION_CHANNEL_ID_FORMAT, "p2", "conv");
+        NotificationChannel p1u1 = new NotificationChannel("p1", "p1u1", IMPORTANCE_DEFAULT);
+        NotificationChannel p1u2 = new NotificationChannel("p1", "p1u2", IMPORTANCE_DEFAULT);
+        NotificationChannel p2u1 = new NotificationChannel("p2", "p2u1", IMPORTANCE_DEFAULT);
+        NotificationChannel p2u1Conv = new NotificationChannel(p2u1ConvId, "p2u1 conv",
+                IMPORTANCE_DEFAULT);
+        p2u1Conv.setConversationId("p2", "conv");
+
+        mHelper.createNotificationChannel(PKG_O, UID_O, p1u1, true,
+                false, UID_O, false);
+        mHelper.createNotificationChannel(PKG_O, UID_O + UserHandle.PER_USER_RANGE, p1u2, true,
+                false, UID_O + UserHandle.PER_USER_RANGE, false);
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, p2u1, true,
+                false, UID_N_MR1, false);
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, p2u1Conv, true,
+                false, UID_N_MR1, false);
+        mHelper.resetCacheInvalidation();
+
+        // Update to an existent channel, with a change: should invalidate
+        NotificationChannel p1u1New = p1u1.copy();
+        p1u1New.setName("p1u1 new");
+        mHelper.updateNotificationChannel(PKG_O, UID_O, p1u1New, true, UID_O, false);
+        assertThat(mHelper.hasCacheBeenInvalidated()).isTrue();
+
+        // Do it again, but no change for this user
+        mHelper.resetCacheInvalidation();
+        mHelper.updateNotificationChannel(PKG_O, UID_O, p1u1New.copy(), true, UID_O, false);
+        assertThat(mHelper.hasCacheBeenInvalidated()).isFalse();
+
+        // Delete conversations, but for a package without those conversations
+        mHelper.resetCacheInvalidation();
+        mHelper.deleteConversations(PKG_O, UID_O, Set.of(p2u1Conv.getConversationId()), UID_O,
+                false);
+        assertThat(mHelper.hasCacheBeenInvalidated()).isFalse();
+
+        // Now delete conversations for the right package
+        mHelper.resetCacheInvalidation();
+        mHelper.deleteConversations(PKG_N_MR1, UID_N_MR1, Set.of(p2u1Conv.getConversationId()),
+                UID_N_MR1, false);
+        assertThat(mHelper.hasCacheBeenInvalidated()).isTrue();
+    }
+
+    @Test
+    @EnableFlags(android.app.Flags.FLAG_NM_BINDER_PERF_CACHE_CHANNELS)
+    public void testInvalidateCache_userRemoved() throws Exception {
+        NotificationChannel c1 = new NotificationChannel("id1", "name1", IMPORTANCE_DEFAULT);
+        int uid1 = UserHandle.getUid(1, 1);
+        setUpPackageWithUid("pkg1", uid1);
+        mHelper.createNotificationChannel("pkg1", uid1, c1, true, false, uid1, false);
+        mHelper.resetCacheInvalidation();
+
+        // delete user 1; should invalidate cache
+        mHelper.onUserRemoved(1);
+        assertThat(mHelper.hasCacheBeenInvalidated()).isTrue();
+    }
+
+    @Test
+    @EnableFlags(android.app.Flags.FLAG_NM_BINDER_PERF_CACHE_CHANNELS)
+    public void testInvalidateCache_packagesChanged() {
+        NotificationChannel channel1 =
+                new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH);
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true, false,
+                UID_N_MR1, false);
+
+        // package deleted: expect cache invalidation
+        mHelper.resetCacheInvalidation();
+        mHelper.onPackagesChanged(true, USER_SYSTEM, new String[]{PKG_N_MR1},
+                new int[]{UID_N_MR1});
+        assertThat(mHelper.hasCacheBeenInvalidated()).isTrue();
+
+        // re-created: expect cache invalidation again
+        mHelper.resetCacheInvalidation();
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel1, true, false,
+                UID_N_MR1, false);
+        mHelper.onPackagesChanged(false, USER_SYSTEM, new String[]{PKG_N_MR1},
+                new int[]{UID_N_MR1});
+        assertThat(mHelper.hasCacheBeenInvalidated()).isTrue();
+    }
+
+    @Test
+    @DisableFlags(android.app.Flags.FLAG_NM_BINDER_PERF_CACHE_CHANNELS)
+    public void testInvalidateCache_flagOff_neverTouchesCache() {
+        // Do a bunch of channel-changing operations.
+        NotificationChannel channel =
+                new NotificationChannel("id", "name1", NotificationManager.IMPORTANCE_HIGH);
+        mHelper.createNotificationChannel(PKG_N_MR1, UID_N_MR1, channel, true, false,
+                UID_N_MR1, false);
+
+        NotificationChannel copy = channel.copy();
+        copy.setName("name2");
+        mHelper.updateNotificationChannel(PKG_N_MR1, UID_N_MR1, copy, true, UID_N_MR1, false);
+        mHelper.deleteNotificationChannel(PKG_N_MR1, UID_N_MR1, "id", UID_N_MR1, false);
+
+        assertThat(mHelper.hasCacheBeenInvalidated()).isFalse();
+    }
+
+    // Test version of PreferencesHelper whose only functional difference is that it does not
+    // interact with the real IpcDataCache, and instead tracks whether or not the cache has been
+    // invalidated since creation or the last reset.
+    private static class TestPreferencesHelper extends PreferencesHelper {
+        private boolean mCacheInvalidated = false;
+
+        TestPreferencesHelper(Context context, PackageManager pm, RankingHandler rankingHandler,
+                ZenModeHelper zenHelper, PermissionHelper permHelper, PermissionManager permManager,
+                NotificationChannelLogger notificationChannelLogger,
+                AppOpsManager appOpsManager, ManagedServices.UserProfiles userProfiles,
+                UriGrantsManagerInternal ugmInternal,
+                boolean showReviewPermissionsNotification, Clock clock) {
+            super(context, pm, rankingHandler, zenHelper, permHelper, permManager,
+                    notificationChannelLogger, appOpsManager, userProfiles, ugmInternal,
+                    showReviewPermissionsNotification, clock);
+        }
+
+        @Override
+        protected void invalidateNotificationChannelCache() {
+            mCacheInvalidated = true;
+        }
+
+        boolean hasCacheBeenInvalidated() {
+            return mCacheInvalidated;
+        }
+
+        void resetCacheInvalidation() {
+            mCacheInvalidated = false;
+        }
+    }
 }
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 1884bbd..6ef078b 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -291,7 +291,8 @@
 
     @Parameters(name = "{0}")
     public static List<FlagsParameterization> getParams() {
-        return FlagsParameterization.allCombinationsOf(FLAG_MODES_UI, FLAG_BACKUP_RESTORE_LOGGING);
+        return FlagsParameterization.allCombinationsOf(FLAG_MODES_UI, FLAG_BACKUP_RESTORE_LOGGING,
+                com.android.server.notification.Flags.FLAG_FIX_CALLING_UID_FROM_CPS);
     }
 
     public ZenModeHelperTest(FlagsParameterization flags) {
@@ -2617,7 +2618,7 @@
     }
 
     @Test
-    public void testSetAutomaticZenRuleState_nullPkg() {
+    public void testSetAutomaticZenRuleStateFromConditionProvider_nullPkg() {
         AutomaticZenRule zenRule = new AutomaticZenRule("name",
                 null,
                 new ComponentName(mContext.getPackageName(), "ScheduleConditionProvider"),
@@ -2627,10 +2628,9 @@
 
         String id = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT, null, zenRule,
                 ORIGIN_APP, "test", CUSTOM_PKG_UID);
-        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, zenRule.getConditionId(),
-                new Condition(zenRule.getConditionId(), "", STATE_TRUE),
-                ORIGIN_APP,
-                CUSTOM_PKG_UID);
+        mZenModeHelper.setAutomaticZenRuleStateFromConditionProvider(UserHandle.CURRENT,
+                zenRule.getConditionId(), new Condition(zenRule.getConditionId(), "", STATE_TRUE),
+                ORIGIN_APP, CUSTOM_PKG_UID);
 
         ZenModeConfig.ZenRule ruleInConfig = mZenModeHelper.mConfig.automaticRules.get(id);
         assertEquals(STATE_TRUE, ruleInConfig.condition.state);
@@ -2726,8 +2726,8 @@
                 ORIGIN_SYSTEM, "test", SYSTEM_UID);
 
         Condition condition = new Condition(sharedUri, "", STATE_TRUE);
-        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, sharedUri, condition,
-                ORIGIN_SYSTEM, SYSTEM_UID);
+        mZenModeHelper.setAutomaticZenRuleStateFromConditionProvider(UserHandle.CURRENT, sharedUri,
+                condition, ORIGIN_SYSTEM, SYSTEM_UID);
 
         for (ZenModeConfig.ZenRule rule : mZenModeHelper.mConfig.automaticRules.values()) {
             if (rule.id.equals(id)) {
@@ -2741,8 +2741,8 @@
         }
 
         condition = new Condition(sharedUri, "", STATE_FALSE);
-        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, sharedUri, condition,
-                ORIGIN_SYSTEM, SYSTEM_UID);
+        mZenModeHelper.setAutomaticZenRuleStateFromConditionProvider(UserHandle.CURRENT, sharedUri,
+                condition, ORIGIN_SYSTEM, SYSTEM_UID);
 
         for (ZenModeConfig.ZenRule rule : mZenModeHelper.mConfig.automaticRules.values()) {
             if (rule.id.equals(id)) {
@@ -2780,9 +2780,10 @@
                         .setOwner(OWNER)
                         .setDeviceEffects(zde)
                         .build(),
-                ORIGIN_APP, "reasons", 0);
+                ORIGIN_APP, "reasons", CUSTOM_PKG_UID);
 
-        AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+        AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId,
+                CUSTOM_PKG_UID);
         assertThat(savedRule.getDeviceEffects()).isEqualTo(
                 new ZenDeviceEffects.Builder()
                         .setShouldDisplayGrayscale(true)
@@ -2814,9 +2815,10 @@
                         .setOwner(OWNER)
                         .setDeviceEffects(zde)
                         .build(),
-                ORIGIN_SYSTEM, "reasons", 0);
+                ORIGIN_SYSTEM, "reasons", SYSTEM_UID);
 
-        AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+        AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId,
+                SYSTEM_UID);
         assertThat(savedRule.getDeviceEffects()).isEqualTo(zde);
     }
 
@@ -2845,7 +2847,8 @@
                 ORIGIN_USER_IN_SYSTEMUI,
                 "reasons", 0);
 
-        AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+        AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId,
+                SYSTEM_UID);
 
         assertThat(savedRule.getDeviceEffects()).isEqualTo(zde);
     }
@@ -2863,7 +2866,7 @@
                         .setOwner(OWNER)
                         .setDeviceEffects(original)
                         .build(),
-                ORIGIN_SYSTEM, "reasons", 0);
+                ORIGIN_SYSTEM, "reasons", SYSTEM_UID);
 
         ZenDeviceEffects updateFromApp = new ZenDeviceEffects.Builder()
                 .setShouldUseNightMode(true) // Good
@@ -2875,9 +2878,10 @@
                         .setOwner(OWNER)
                         .setDeviceEffects(updateFromApp)
                         .build(),
-                ORIGIN_APP, "reasons", 0);
+                ORIGIN_APP, "reasons", CUSTOM_PKG_UID);
 
-        AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+        AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId,
+                CUSTOM_PKG_UID);
         assertThat(savedRule.getDeviceEffects()).isEqualTo(
                 new ZenDeviceEffects.Builder()
                         .setShouldUseNightMode(true) // From update.
@@ -2898,7 +2902,7 @@
                         .setOwner(OWNER)
                         .setDeviceEffects(original)
                         .build(),
-                ORIGIN_SYSTEM, "reasons", 0);
+                ORIGIN_SYSTEM, "reasons", SYSTEM_UID);
 
         ZenDeviceEffects updateFromSystem = new ZenDeviceEffects.Builder()
                 .setShouldUseNightMode(true) // Good
@@ -2908,9 +2912,10 @@
                 new AutomaticZenRule.Builder("Rule", CONDITION_ID)
                         .setDeviceEffects(updateFromSystem)
                         .build(),
-                ORIGIN_SYSTEM, "reasons", 0);
+                ORIGIN_SYSTEM, "reasons", SYSTEM_UID);
 
-        AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+        AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId,
+                SYSTEM_UID);
         assertThat(savedRule.getDeviceEffects()).isEqualTo(updateFromSystem);
     }
 
@@ -2926,7 +2931,7 @@
                         .setOwner(OWNER)
                         .setDeviceEffects(original)
                         .build(),
-                ORIGIN_SYSTEM, "reasons", 0);
+                ORIGIN_SYSTEM, "reasons", SYSTEM_UID);
 
         ZenDeviceEffects updateFromUser = new ZenDeviceEffects.Builder()
                 .setShouldUseNightMode(true)
@@ -2939,9 +2944,10 @@
                 new AutomaticZenRule.Builder("Rule", CONDITION_ID)
                         .setDeviceEffects(updateFromUser)
                         .build(),
-                ORIGIN_USER_IN_SYSTEMUI, "reasons", 0);
+                ORIGIN_USER_IN_SYSTEMUI, "reasons", SYSTEM_UID);
 
-        AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+        AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId,
+                SYSTEM_UID);
 
         assertThat(savedRule.getDeviceEffects()).isEqualTo(updateFromUser);
     }
@@ -2959,15 +2965,16 @@
                                 .allowCalls(ZenPolicy.PEOPLE_TYPE_NONE) // default is stars
                                 .build())
                         .build(),
-                ORIGIN_APP, "reasons", 0);
+                ORIGIN_APP, "reasons", CUSTOM_PKG_UID);
 
         mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId,
                 new AutomaticZenRule.Builder("Rule", CONDITION_ID)
                         // no zen policy
                         .build(),
-                ORIGIN_APP, "reasons", 0);
+                ORIGIN_APP, "reasons", CUSTOM_PKG_UID);
 
-        AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+        AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId,
+                CUSTOM_PKG_UID);
         assertThat(savedRule.getZenPolicy().getPriorityCategoryCalls())
                 .isEqualTo(STATE_DISALLOW);
     }
@@ -2988,7 +2995,7 @@
                                 .allowReminders(true)
                                 .build())
                         .build(),
-                ORIGIN_SYSTEM, "reasons", 0);
+                ORIGIN_SYSTEM, "reasons", SYSTEM_UID);
 
         mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId,
                 new AutomaticZenRule.Builder("Rule", CONDITION_ID)
@@ -2996,9 +3003,10 @@
                                 .allowCalls(ZenPolicy.PEOPLE_TYPE_CONTACTS)
                                 .build())
                         .build(),
-                ORIGIN_APP, "reasons", 0);
+                ORIGIN_APP, "reasons", CUSTOM_PKG_UID);
 
-        AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+        AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId,
+                CUSTOM_PKG_UID);
         assertThat(savedRule.getZenPolicy().getPriorityCategoryCalls())
                 .isEqualTo(STATE_ALLOW);  // from update
         assertThat(savedRule.getZenPolicy().getPriorityCallSenders())
@@ -4441,7 +4449,8 @@
         rule.triggerDescription = TRIGGER_DESC;
 
         mZenModeHelper.mConfig.automaticRules.put(rule.id, rule);
-        AutomaticZenRule actual = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, rule.id);
+        AutomaticZenRule actual = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, rule.id,
+                SYSTEM_UID);
 
         assertEquals(NAME, actual.getName());
         assertEquals(OWNER, actual.getOwner());
@@ -4508,16 +4517,17 @@
                 .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY)
                 .build();
         String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
-                mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", SYSTEM_UID);
-        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+                mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", CUSTOM_PKG_UID);
+        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId,
+                CUSTOM_PKG_UID);
 
         // Checks the name can be changed by the app because the user has not modified it.
         AutomaticZenRule azrUpdate = new AutomaticZenRule.Builder(rule)
                 .setName("NewName")
                 .build();
         mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, azrUpdate, ORIGIN_APP,
-                "reason", SYSTEM_UID);
-        rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+                "reason", CUSTOM_PKG_UID);
+        rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId, CUSTOM_PKG_UID);
         assertThat(rule.getName()).isEqualTo("NewName");
 
         // The user modifies some other field in the rule, which makes the rule as a whole not
@@ -4534,8 +4544,8 @@
                 .setName("NewAppName")
                 .build();
         mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, azrUpdate, ORIGIN_APP,
-                "reason", SYSTEM_UID);
-        rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+                "reason", CUSTOM_PKG_UID);
+        rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId, CUSTOM_PKG_UID);
         assertThat(rule.getName()).isEqualTo("NewAppName");
 
         // The user modifies the name.
@@ -4544,7 +4554,7 @@
                 .build();
         mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, azrUpdate,
                 ORIGIN_USER_IN_SYSTEMUI, "reason", SYSTEM_UID);
-        rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+        rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId, CUSTOM_PKG_UID);
         assertThat(rule.getName()).isEqualTo("UserProvidedName");
 
         // The app is no longer able to modify the name.
@@ -4552,8 +4562,8 @@
                 .setName("NewAppName")
                 .build();
         mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, azrUpdate, ORIGIN_APP,
-                "reason", SYSTEM_UID);
-        rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+                "reason", CUSTOM_PKG_UID);
+        rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId, CUSTOM_PKG_UID);
         assertThat(rule.getName()).isEqualTo("UserProvidedName");
     }
 
@@ -4568,8 +4578,9 @@
                 .build();
         // Adds the rule using the app, to avoid having any user modified bits set.
         String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
-                mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", SYSTEM_UID);
-        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+                mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", CUSTOM_PKG_UID);
+        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId,
+                CUSTOM_PKG_UID);
 
         // Modifies the filter, icon, zen policy, and device effects
         ZenPolicy policy = new ZenPolicy.Builder(rule.getZenPolicy())
@@ -4589,7 +4600,7 @@
         // Update the rule with the AZR from origin user.
         mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, azrUpdate,
                 ORIGIN_USER_IN_SYSTEMUI, "reason", SYSTEM_UID);
-        rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+        rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId, CUSTOM_PKG_UID);
 
         // UPDATE_ORIGIN_USER should change the bitmask and change the values.
         assertThat(rule.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_PRIORITY);
@@ -4625,8 +4636,9 @@
                 .build();
         // Adds the rule using the app, to avoid having any user modified bits set.
         String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
-                mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", SYSTEM_UID);
-        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+                mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", CUSTOM_PKG_UID);
+        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId,
+                CUSTOM_PKG_UID);
 
         // Modifies the icon, zen policy and device effects
         ZenPolicy policy = new ZenPolicy.Builder(rule.getZenPolicy())
@@ -4646,7 +4658,7 @@
         // Update the rule with the AZR from origin systemUI.
         mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, azrUpdate, ORIGIN_SYSTEM,
                 "reason", SYSTEM_UID);
-        rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+        rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId, CUSTOM_PKG_UID);
 
         // UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI should change the value but NOT update the bitmask.
         assertThat(rule.getIconResId()).isEqualTo(ICON_RES_ID);
@@ -4675,8 +4687,9 @@
                 .build();
         // Adds the rule using the app, to avoid having any user modified bits set.
         String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
-                mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", SYSTEM_UID);
-        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+                mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", CUSTOM_PKG_UID);
+        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId,
+                CUSTOM_PKG_UID);
 
         ZenPolicy policy = new ZenPolicy.Builder()
                 .allowReminders(true)
@@ -4693,7 +4706,7 @@
         // Since the rule is not already user modified, UPDATE_ORIGIN_APP can modify the rule.
         // The bitmask is not modified.
         mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, azrUpdate, ORIGIN_APP,
-                "reason", SYSTEM_UID);
+                "reason", CUSTOM_PKG_UID);
 
         ZenRule storedRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
         assertThat(storedRule.userModifiedFields).isEqualTo(0);
@@ -4717,9 +4730,9 @@
         // Zen rule update coming from the app again. This cannot fully update the rule, because
         // the rule is already considered user modified.
         mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleIdUser, azrUpdate, ORIGIN_APP,
-                "reason", SYSTEM_UID);
+                "reason", CUSTOM_PKG_UID);
         AutomaticZenRule ruleUser = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT,
-                ruleIdUser);
+                ruleIdUser, CUSTOM_PKG_UID);
 
         // The app can only change the value if the rule is not already user modified,
         // so the rule is not changed, and neither is the bitmask.
@@ -4749,8 +4762,9 @@
                         .build())
                 .build();
         String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
-                mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", SYSTEM_UID);
-        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+                mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", CUSTOM_PKG_UID);
+        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId,
+                CUSTOM_PKG_UID);
 
         // The values are modified but the bitmask is not.
         assertThat(rule.getZenPolicy().getPriorityCategoryReminders())
@@ -4771,7 +4785,7 @@
                 .build();
         // Adds the rule using the app, to avoid having any user modified bits set.
         String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
-                mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", SYSTEM_UID);
+                mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", CUSTOM_PKG_UID);
 
         AutomaticZenRule azr = new AutomaticZenRule.Builder(azrBase)
                 // Sets Device Effects to null
@@ -4781,8 +4795,9 @@
         // Zen rule update coming from app, but since the rule isn't already
         // user modified, it can be updated.
         mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, azr, ORIGIN_APP, "reason",
-                SYSTEM_UID);
-        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+                CUSTOM_PKG_UID);
+        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId,
+                CUSTOM_PKG_UID);
 
         // When AZR's ZenDeviceEffects is null, the updated rule's device effects are kept.
         assertThat(rule.getDeviceEffects()).isEqualTo(zde);
@@ -4797,8 +4812,7 @@
                 .build();
         // Adds the rule using the app, to avoid having any user modified bits set.
         String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
-                mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", SYSTEM_UID);
-        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+                mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", CUSTOM_PKG_UID);
 
         AutomaticZenRule azr = new AutomaticZenRule.Builder(azrBase)
                 // Set zen policy to null
@@ -4808,8 +4822,9 @@
         // Zen rule update coming from app, but since the rule isn't already
         // user modified, it can be updated.
         mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, azr, ORIGIN_APP, "reason",
-                SYSTEM_UID);
-        rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+                CUSTOM_PKG_UID);
+        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId,
+                CUSTOM_PKG_UID);
 
         // When AZR's ZenPolicy is null, we expect the updated rule's policy to be unchanged
         // (equivalent to the provided policy, with additional fields filled in with defaults).
@@ -4829,8 +4844,7 @@
                 .build();
         // Adds the rule using the app, to avoid having any user modified bits set.
         String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
-                mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", SYSTEM_UID);
-        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+                mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", CUSTOM_PKG_UID);
 
         // Create a fully populated ZenPolicy.
         ZenPolicy policy = new ZenPolicy.Builder()
@@ -4860,7 +4874,8 @@
         // Default config defined in getDefaultConfigParser() is used as the original rule.
         mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, azr,
                 ORIGIN_USER_IN_SYSTEMUI, "reason", SYSTEM_UID);
-        rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId,
+                CUSTOM_PKG_UID);
 
         // New ZenPolicy differs from the default config
         assertThat(rule.getZenPolicy()).isNotNull();
@@ -4890,8 +4905,9 @@
                 .build();
         // Adds the rule using the app, to avoid having any user modified bits set.
         String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
-                mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", SYSTEM_UID);
-        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+                mContext.getPackageName(), azrBase, ORIGIN_APP, "reason", CUSTOM_PKG_UID);
+        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId,
+                CUSTOM_PKG_UID);
 
         ZenDeviceEffects deviceEffects = new ZenDeviceEffects.Builder()
                 .setShouldDisplayGrayscale(true)
@@ -4903,7 +4919,7 @@
         // Applies the update to the rule.
         mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, azr,
                 ORIGIN_USER_IN_SYSTEMUI, "reason", SYSTEM_UID);
-        rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+        rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId, CUSTOM_PKG_UID);
 
         // New ZenDeviceEffects is used; all fields considered set, since previously were null.
         assertThat(rule.getDeviceEffects()).isNotNull();
@@ -5286,7 +5302,8 @@
         mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, update,
                 ORIGIN_USER_IN_SYSTEMUI, "reason", SYSTEM_UID);
 
-        AutomaticZenRule result = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+        AutomaticZenRule result = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId,
+                SYSTEM_UID);
         assertThat(result).isNotNull();
         assertThat(result.getOwner().getClassName()).isEqualTo("brand.new.cps");
     }
@@ -5306,7 +5323,8 @@
         mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId, update,
                 ORIGIN_USER_IN_SYSTEMUI, "reason", SYSTEM_UID);
 
-        AutomaticZenRule result = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+        AutomaticZenRule result = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId,
+                CUSTOM_PKG_UID);
         assertThat(result).isNotNull();
         assertThat(result.getOwner().getClassName()).isEqualTo("old.third.party.cps");
     }
@@ -5518,8 +5536,8 @@
                 .build();
         String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
                 mContext.getPackageName(), rule, ORIGIN_APP, "add it", CUSTOM_PKG_UID);
-        assertThat(mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId).getCreationTime())
-                .isEqualTo(1000);
+        assertThat(mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId,
+                CUSTOM_PKG_UID).getCreationTime()).isEqualTo(1000);
 
         // User customizes it.
         AutomaticZenRule userUpdate = new AutomaticZenRule.Builder(rule)
@@ -5546,7 +5564,7 @@
         // - ZenPolicy is the one that the user had set.
         // - rule still has the user-modified fields.
         AutomaticZenRule finalRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT,
-                newRuleId);
+                newRuleId, CUSTOM_PKG_UID);
         assertThat(finalRule.getCreationTime()).isEqualTo(1000); // And not 3000.
         assertThat(newRuleId).isEqualTo(ruleId);
         assertThat(finalRule.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_ALARMS);
@@ -5575,8 +5593,8 @@
                 .build();
         String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
                 mContext.getPackageName(), rule, ORIGIN_APP, "add it", CUSTOM_PKG_UID);
-        assertThat(mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId).getCreationTime())
-                .isEqualTo(1000);
+        assertThat(mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId,
+                CUSTOM_PKG_UID).getCreationTime()).isEqualTo(1000);
 
         // App deletes it.
         mTestClock.advanceByMillis(1000);
@@ -5592,7 +5610,7 @@
 
         // Verify that the rule was recreated. This means id and creation time are new.
         AutomaticZenRule finalRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT,
-                newRuleId);
+                newRuleId, CUSTOM_PKG_UID);
         assertThat(finalRule.getCreationTime()).isEqualTo(3000);
         assertThat(newRuleId).isNotEqualTo(ruleId);
     }
@@ -5609,8 +5627,8 @@
                 .build();
         String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
                 mContext.getPackageName(), rule, ORIGIN_APP, "add it", CUSTOM_PKG_UID);
-        assertThat(mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId).getCreationTime())
-                .isEqualTo(1000);
+        assertThat(mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId, CUSTOM_PKG_UID)
+                .getCreationTime()).isEqualTo(1000);
 
         // User customizes it.
         mTestClock.advanceByMillis(1000);
@@ -5637,7 +5655,7 @@
         // Verify that the rule was recreated. This means id and creation time are new, and the rule
         // matches the latest data supplied to addAZR.
         AutomaticZenRule finalRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT,
-                newRuleId);
+                newRuleId, CUSTOM_PKG_UID);
         assertThat(finalRule.getCreationTime()).isEqualTo(4000);
         assertThat(newRuleId).isNotEqualTo(ruleId);
         assertThat(finalRule.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_PRIORITY);
@@ -5660,8 +5678,8 @@
                 .build();
         String ruleId = mZenModeHelper.addAutomaticZenRule(UserHandle.CURRENT,
                 mContext.getPackageName(), rule, ORIGIN_APP, "add it", CUSTOM_PKG_UID);
-        assertThat(mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId).getCreationTime())
-                .isEqualTo(1000);
+        assertThat(mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId,
+                CUSTOM_PKG_UID).getCreationTime()).isEqualTo(1000);
 
         // User customizes it.
         mTestClock.advanceByMillis(1000);
@@ -5686,7 +5704,7 @@
 
         // Verify that the rule was recreated. This means id and creation time are new.
         AutomaticZenRule finalRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT,
-                newRuleId);
+                newRuleId, CUSTOM_PKG_UID);
         assertThat(finalRule.getCreationTime()).isEqualTo(4000);
         assertThat(newRuleId).isNotEqualTo(ruleId);
     }
@@ -5728,7 +5746,7 @@
         // Verify that the rule was NOT restored:
         assertThat(newRuleId).isNotEqualTo(ruleId);
         AutomaticZenRule finalRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT,
-                newRuleId);
+                newRuleId, CUSTOM_PKG_UID);
         assertThat(finalRule.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_ALARMS);
         assertThat(finalRule.getOwner()).isEqualTo(new ComponentName("second", "owner"));
 
@@ -5869,7 +5887,7 @@
         // The rule is restored...
         assertThat(newRuleId).isEqualTo(ruleId);
         AutomaticZenRule finalRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT,
-                newRuleId);
+                newRuleId, CUSTOM_PKG_UID);
         assertThat(finalRule.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_ALARMS);
 
         // ... but it is NOT active
@@ -5923,7 +5941,7 @@
         // The rule is restored...
         assertThat(newRuleId).isEqualTo(ruleId);
         AutomaticZenRule finalRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT,
-                newRuleId);
+                newRuleId, CUSTOM_PKG_UID);
         assertThat(finalRule.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_ALARMS);
 
         // ... but it is NEITHER active NOR snoozed.
@@ -6005,22 +6023,22 @@
                 ORIGIN_APP, "reasons", CUSTOM_PKG_UID);
 
         // Null condition -> STATE_FALSE
-        assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, id))
+        assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, id, CUSTOM_PKG_UID))
                 .isEqualTo(Condition.STATE_FALSE);
 
         mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, id, CONDITION_TRUE, ORIGIN_APP,
                 CUSTOM_PKG_UID);
-        assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, id))
+        assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, id, CUSTOM_PKG_UID))
                 .isEqualTo(Condition.STATE_TRUE);
 
         mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, id, CONDITION_FALSE, ORIGIN_APP,
                 CUSTOM_PKG_UID);
-        assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, id))
+        assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, id, CUSTOM_PKG_UID))
                 .isEqualTo(Condition.STATE_FALSE);
 
         mZenModeHelper.removeAutomaticZenRule(UserHandle.CURRENT, id, ORIGIN_APP, "",
                 CUSTOM_PKG_UID);
-        assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, id))
+        assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, id, CUSTOM_PKG_UID))
                 .isEqualTo(Condition.STATE_UNKNOWN);
     }
 
@@ -6036,8 +6054,8 @@
         mZenModeHelper.setConfig(config, null, ORIGIN_INIT, "", SYSTEM_UID);
         assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_ALARMS);
 
-        assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, "systemRule"))
-                .isEqualTo(Condition.STATE_UNKNOWN);
+        assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, "systemRule",
+                CUSTOM_PKG_UID)).isEqualTo(Condition.STATE_UNKNOWN);
     }
 
     @Test
@@ -6063,7 +6081,7 @@
 
     @Test
     @EnableFlags(FLAG_MODES_API)
-    public void setAutomaticZenRuleState_conditionForNotOwnedRule_ignored() {
+    public void setAutomaticZenRuleStateFromConditionProvider_conditionForNotOwnedRule_ignored() {
         // Assume existence of an other-package-owned rule that is currently ACTIVE.
         assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF);
         ZenRule otherRule = newZenRule("another.package", Instant.now(), null);
@@ -6075,7 +6093,8 @@
         assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_ALARMS);
 
         // Should be ignored.
-        mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, otherRule.conditionId,
+        mZenModeHelper.setAutomaticZenRuleStateFromConditionProvider(UserHandle.CURRENT,
+                otherRule.conditionId,
                 new Condition(otherRule.conditionId, "off", Condition.STATE_FALSE),
                 ORIGIN_APP, CUSTOM_PKG_UID);
 
@@ -6182,7 +6201,8 @@
                 .isEqualTo(ZEN_MODE_IMPORTANT_INTERRUPTIONS);
 
         // From user, update that rule's interruption filter.
-        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId,
+                SYSTEM_UID);
         AutomaticZenRule userUpdateRule = new AutomaticZenRule.Builder(rule)
                 .setInterruptionFilter(INTERRUPTION_FILTER_ALARMS)
                 .build();
@@ -6214,7 +6234,8 @@
                 .isEqualTo(ZEN_MODE_IMPORTANT_INTERRUPTIONS);
 
         // From user, update something in that rule, but not the interruption filter.
-        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId,
+                SYSTEM_UID);
         AutomaticZenRule userUpdateRule = new AutomaticZenRule.Builder(rule)
                 .setName("Renamed")
                 .build();
@@ -6315,7 +6336,8 @@
         String ruleId = ZenModeConfig.implicitRuleId(mContext.getPackageName());
 
         // User chooses a new name.
-        AutomaticZenRule azr = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+        AutomaticZenRule azr = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId,
+                SYSTEM_UID);
         mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId,
                 new AutomaticZenRule.Builder(azr).setName("User chose this").build(),
                 ORIGIN_USER_IN_SYSTEMUI, "reason", SYSTEM_UID);
@@ -6414,7 +6436,8 @@
                 mZenModeHelper.mConfig.getZenPolicy()).allowMedia(true).build();
 
         // From user, update that rule's policy.
-        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId,
+                SYSTEM_UID);
         ZenPolicy userUpdateZenPolicy = new ZenPolicy.Builder().disallowAllSounds()
                 .allowAlarms(true).build();
         AutomaticZenRule userUpdateRule = new AutomaticZenRule.Builder(rule)
@@ -6456,7 +6479,8 @@
                 mZenModeHelper.mConfig.getZenPolicy()).allowMedia(true).build();
 
         // From user, update something in that rule, but not the ZenPolicy.
-        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+        AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId,
+                SYSTEM_UID);
         AutomaticZenRule userUpdateRule = new AutomaticZenRule.Builder(rule)
                 .setName("Rule renamed, not touching policy")
                 .build();
@@ -6509,7 +6533,8 @@
         String ruleId = ZenModeConfig.implicitRuleId(mContext.getPackageName());
 
         // User chooses a new name.
-        AutomaticZenRule azr = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId);
+        AutomaticZenRule azr = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT, ruleId,
+                SYSTEM_UID);
         mZenModeHelper.updateAutomaticZenRule(UserHandle.CURRENT, ruleId,
                 new AutomaticZenRule.Builder(azr).setName("User chose this").build(),
                 ORIGIN_USER_IN_SYSTEMUI, "reason", SYSTEM_UID);
@@ -6645,7 +6670,7 @@
                 new AutomaticZenRule.Builder("Rule", CONDITION_ID).setIconResId(resourceId).build(),
                 ORIGIN_APP, "reason", CUSTOM_PKG_UID);
         AutomaticZenRule storedRule = mZenModeHelper.getAutomaticZenRule(UserHandle.CURRENT,
-                ruleId);
+                ruleId, CUSTOM_PKG_UID);
 
         assertThat(storedRule.getIconResId()).isEqualTo(0);
     }
@@ -7087,8 +7112,8 @@
                 ORIGIN_USER_IN_SYSTEMUI, SYSTEM_UID);
 
         implicitRule = getZenRule(implicitRuleId(CUSTOM_PKG_NAME));
-        assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, implicitRule.id))
-                .isEqualTo(STATE_TRUE);
+        assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, implicitRule.id,
+                CUSTOM_PKG_UID)).isEqualTo(STATE_TRUE);
         assertThat(implicitRule.isActive()).isTrue();
         assertThat(implicitRule.getConditionOverride()).isEqualTo(OVERRIDE_NONE);
     }
@@ -7108,8 +7133,8 @@
                 ORIGIN_USER_IN_SYSTEMUI, SYSTEM_UID);
 
         implicitRule = getZenRule(implicitRuleId(CUSTOM_PKG_NAME));
-        assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, implicitRule.id))
-                .isEqualTo(STATE_FALSE);
+        assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, implicitRule.id,
+                CUSTOM_PKG_UID)).isEqualTo(STATE_FALSE);
         assertThat(implicitRule.isActive()).isFalse();
         assertThat(implicitRule.getConditionOverride()).isEqualTo(OVERRIDE_NONE);
     }
@@ -7177,7 +7202,7 @@
         mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, ruleId,
                 new Condition(rule.getConditionId(), "manual-on", STATE_TRUE, SOURCE_USER_ACTION),
                 ORIGIN_USER_IN_SYSTEMUI, SYSTEM_UID);
-        assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, ruleId))
+        assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, ruleId, SYSTEM_UID))
                 .isEqualTo(STATE_TRUE);
         ZenRule zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
         assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_ACTIVATE);
@@ -7192,14 +7217,14 @@
         mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL, null);
 
         if (Flags.modesUi()) {
-            assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, ruleId))
-                    .isEqualTo(STATE_TRUE);
+            assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, ruleId,
+                    SYSTEM_UID)).isEqualTo(STATE_TRUE);
             zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
             assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_ACTIVATE);
             assertThat(zenRule.condition).isNull();
         } else {
-            assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, ruleId))
-                    .isEqualTo(STATE_FALSE);
+            assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, ruleId,
+                    SYSTEM_UID)).isEqualTo(STATE_FALSE);
         }
     }
 
@@ -7218,7 +7243,8 @@
         mZenModeHelper.setAutomaticZenRuleState(UserHandle.CURRENT, ruleId,
                 new Condition(rule.getConditionId(), "snooze", STATE_FALSE, SOURCE_USER_ACTION),
                 ORIGIN_USER_IN_SYSTEMUI, SYSTEM_UID);
-        assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, ruleId))
+        assertThat(
+                mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, ruleId, CUSTOM_PKG_UID))
                 .isEqualTo(STATE_FALSE);
         ZenRule zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
         assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_DEACTIVATE);
@@ -7232,7 +7258,8 @@
         TypedXmlPullParser parser = getParserForByteStream(xmlBytes);
         mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL, null);
 
-        assertThat(mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, ruleId))
+        assertThat(
+                mZenModeHelper.getAutomaticZenRuleState(UserHandle.CURRENT, ruleId, CUSTOM_PKG_UID))
                 .isEqualTo(STATE_TRUE);
         zenRule = mZenModeHelper.mConfig.automaticRules.get(ruleId);
         assertThat(zenRule.getConditionOverride()).isEqualTo(OVERRIDE_NONE);
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/BasicToPwleSegmentAdapterTest.java b/services/tests/vibrator/src/com/android/server/vibrator/BasicToPwleSegmentAdapterTest.java
new file mode 100644
index 0000000..09f573c
--- /dev/null
+++ b/services/tests/vibrator/src/com/android/server/vibrator/BasicToPwleSegmentAdapterTest.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.server.vibrator;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.hardware.vibrator.IVibrator;
+import android.os.VibratorInfo;
+import android.os.vibrator.BasicPwleSegment;
+import android.os.vibrator.Flags;
+import android.os.vibrator.PwleSegment;
+import android.os.vibrator.StepSegment;
+import android.os.vibrator.VibrationEffectSegment;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.IntStream;
+
+public class BasicToPwleSegmentAdapterTest {
+
+    private static final float TEST_RESONANT_FREQUENCY = 150;
+    private static final float[] TEST_FREQUENCIES =
+            new float[]{90f, 120f, 150f, 60f, 30f, 210f, 270f, 300f, 240f, 180f};
+    private static final float[] TEST_OUTPUT_ACCELERATIONS =
+            new float[]{1.2f, 1.8f, 2.4f, 0.6f, 0.1f, 2.2f, 1.0f, 0.5f, 1.9f, 3.0f};
+
+    private static final VibratorInfo.FrequencyProfile TEST_FREQUENCY_PROFILE =
+            new VibratorInfo.FrequencyProfile(TEST_RESONANT_FREQUENCY, TEST_FREQUENCIES,
+                    TEST_OUTPUT_ACCELERATIONS);
+
+    private static final VibratorInfo.FrequencyProfile EMPTY_FREQUENCY_PROFILE =
+            new VibratorInfo.FrequencyProfile(TEST_RESONANT_FREQUENCY, null, null);
+
+    private BasicToPwleSegmentAdapter mAdapter;
+
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+    @Before
+    public void setUp() throws Exception {
+        mAdapter = new BasicToPwleSegmentAdapter();
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+    public void testBasicPwleSegments_withFeatureFlagDisabled_returnsOriginalSegments() {
+        List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
+                //  startIntensity, endIntensity, startSharpness, endSharpness, duration
+                new BasicPwleSegment(0.2f, 0.8f, 0.2f, 0.4f, 20),
+                new BasicPwleSegment(0.8f, 0.2f, 0.4f, 0.5f, 100),
+                new BasicPwleSegment(0.2f, 0.65f, 0.5f, 0.5f, 50)));
+        List<VibrationEffectSegment> originalSegments = new ArrayList<>(segments);
+
+        VibratorInfo vibratorInfo = createVibratorInfo(
+                TEST_FREQUENCY_PROFILE, IVibrator.CAP_COMPOSE_PWLE_EFFECTS_V2);
+
+        assertThat(mAdapter.adaptToVibrator(vibratorInfo, segments, /*repeatIndex= */ -1))
+                .isEqualTo(-1);
+        assertThat(mAdapter.adaptToVibrator(vibratorInfo, segments, /*repeatIndex= */ 1))
+                .isEqualTo(1);
+
+        assertThat(segments).isEqualTo(originalSegments);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+    public void testBasicPwleSegments_noPwleCapability_returnsOriginalSegments() {
+        List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
+                //  startIntensity, endIntensity, startSharpness, endSharpness, duration
+                new BasicPwleSegment(0.2f, 0.8f, 0.2f, 0.4f, 20),
+                new BasicPwleSegment(0.8f, 0.2f, 0.4f, 0.5f, 100),
+                new BasicPwleSegment(0.2f, 0.65f, 0.5f, 0.5f, 50)));
+        List<VibrationEffectSegment> originalSegments = new ArrayList<>(segments);
+
+        VibratorInfo vibratorInfo = createVibratorInfo(TEST_FREQUENCY_PROFILE);
+
+        assertThat(mAdapter.adaptToVibrator(vibratorInfo, segments, /*repeatIndex= */ -1))
+                .isEqualTo(-1);
+        assertThat(mAdapter.adaptToVibrator(vibratorInfo, segments, /*repeatIndex= */ 1))
+                .isEqualTo(1);
+
+        assertThat(segments).isEqualTo(originalSegments);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+    public void testBasicPwleSegments_invalidFrequencyProfile_returnsOriginalSegments() {
+        List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
+                //  startIntensity, endIntensity, startSharpness, endSharpness, duration
+                new BasicPwleSegment(0.2f, 0.8f, 0.2f, 0.4f, 20),
+                new BasicPwleSegment(0.8f, 0.2f, 0.4f, 0.5f, 100),
+                new BasicPwleSegment(0.2f, 0.65f, 0.5f, 0.5f, 50)));
+        List<VibrationEffectSegment> originalSegments = new ArrayList<>(segments);
+        VibratorInfo vibratorInfo = createVibratorInfo(
+                EMPTY_FREQUENCY_PROFILE, IVibrator.CAP_COMPOSE_PWLE_EFFECTS_V2);
+
+        assertThat(mAdapter.adaptToVibrator(vibratorInfo, segments, /*repeatIndex= */ -1))
+                .isEqualTo(-1);
+        assertThat(mAdapter.adaptToVibrator(vibratorInfo, segments, /*repeatIndex= */ 1))
+                .isEqualTo(1);
+
+        assertThat(segments).isEqualTo(originalSegments);
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
+    public void testBasicPwleSegments_withPwleCapability_adaptSegmentsCorrectly() {
+        List<VibrationEffectSegment> segments = new ArrayList<>(Arrays.asList(
+                new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 40f, /* duration= */ 100),
+                //  startIntensity, endIntensity, startSharpness, endSharpness, duration
+                new BasicPwleSegment(0.0f, 1.0f, 0.0f, 1.0f, 100),
+                new BasicPwleSegment(0.0f, 1.0f, 0.0f, 1.0f, 100),
+                new BasicPwleSegment(0.0f, 1.0f, 0.0f, 1.0f, 100)));
+        List<VibrationEffectSegment> expectedSegments = Arrays.asList(
+                new StepSegment(/* amplitude= */ 1, /* frequencyHz= */ 40f, /* duration= */ 100),
+                //  startAmplitude, endAmplitude, startFrequencyHz, endFrequencyHz, duration
+                new PwleSegment(0.0f, 1.0f, 30.0f, 300.0f, 100),
+                new PwleSegment(0.0f, 1.0f, 30.0f, 300.0f, 100),
+                new PwleSegment(0.0f, 1.0f, 30.0f, 300.0f, 100));
+        VibratorInfo vibratorInfo = createVibratorInfo(
+                TEST_FREQUENCY_PROFILE, IVibrator.CAP_COMPOSE_PWLE_EFFECTS_V2);
+
+        assertThat(mAdapter.adaptToVibrator(vibratorInfo, segments, /*repeatIndex= */ 1))
+                .isEqualTo(1);
+
+        assertThat(segments).isEqualTo(expectedSegments);
+    }
+
+    private static VibratorInfo createVibratorInfo(VibratorInfo.FrequencyProfile frequencyProfile,
+            int... capabilities) {
+        return new VibratorInfo.Builder(0)
+                .setCapabilities(IntStream.of(capabilities).reduce((a, b) -> a | b).orElse(0))
+                .setFrequencyProfile(frequencyProfile)
+                .build();
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java b/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java
index 9d4d94b..85ef466 100644
--- a/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java
+++ b/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java
@@ -758,6 +758,18 @@
     }
 
     @Test
+    @EnableFlags(com.android.hardware.input.Flags.FLAG_ENABLE_VOICE_ACCESS_KEY_GESTURES)
+    public void testKeyGestureToggleVoiceAccess() {
+        Assert.assertTrue(
+                sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_VOICE_ACCESS));
+        mPhoneWindowManager.assertVoiceAccess(true);
+
+        Assert.assertTrue(
+                sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_VOICE_ACCESS));
+        mPhoneWindowManager.assertVoiceAccess(false);
+    }
+
+    @Test
     public void testKeyGestureToggleDoNotDisturb() {
         mPhoneWindowManager.overrideZenMode(Settings.Global.ZEN_MODE_OFF);
         Assert.assertTrue(
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 6c48ba2..4ff3d43 100644
--- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
+++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java
@@ -201,6 +201,8 @@
     private boolean mIsTalkBackEnabled;
     private boolean mIsTalkBackShortcutGestureEnabled;
 
+    private boolean mIsVoiceAccessEnabled;
+
     private Intent mBrowserIntent;
     private Intent mSmsIntent;
 
@@ -225,6 +227,18 @@
         }
     }
 
+    private class TestVoiceAccessShortcutController extends VoiceAccessShortcutController {
+        TestVoiceAccessShortcutController(Context context) {
+            super(context);
+        }
+
+        @Override
+        boolean toggleVoiceAccess(int currentUserId) {
+            mIsVoiceAccessEnabled = !mIsVoiceAccessEnabled;
+            return mIsVoiceAccessEnabled;
+        }
+    }
+
     private class TestInjector extends PhoneWindowManager.Injector {
         TestInjector(Context context, WindowManagerPolicy.WindowManagerFuncs funcs) {
             super(context, funcs);
@@ -260,6 +274,10 @@
             return new TestTalkbackShortcutController(mContext);
         }
 
+        VoiceAccessShortcutController getVoiceAccessShortcutController() {
+            return new TestVoiceAccessShortcutController(mContext);
+        }
+
         WindowWakeUpPolicy getWindowWakeUpPolicy() {
             return mWindowWakeUpPolicy;
         }
@@ -1024,6 +1042,11 @@
         Assert.assertEquals(expectEnabled, mIsTalkBackEnabled);
     }
 
+    void assertVoiceAccess(boolean expectEnabled) {
+        mTestLooper.dispatchAll();
+        Assert.assertEquals(expectEnabled, mIsVoiceAccessEnabled);
+    }
+
     void assertKeyGestureEventSentToKeyGestureController(int gestureType) {
         verify(mInputManagerInternal)
                 .handleKeyGestureInKeyGestureController(anyInt(), any(), anyInt(), eq(gestureType));
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 c9cbe0f..6fad82b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -210,7 +210,7 @@
     }
 
     private TestStartingWindowOrganizer registerTestStartingWindowOrganizer() {
-        return new TestStartingWindowOrganizer(mAtm);
+        return new TestStartingWindowOrganizer(mAtm, mDisplayContent);
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationOverridesTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationOverridesTest.java
index 9d191ce..a0727a7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationOverridesTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationOverridesTest.java
@@ -335,7 +335,7 @@
         }
 
         private AppCompatOrientationOverrides getTopOrientationOverrides() {
-            return activity().top().mAppCompatController.getAppCompatOrientationOverrides();
+            return activity().top().mAppCompatController.getOrientationOverrides();
         }
     }
 }
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 a21ab5d..4faa714 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatOrientationPolicyTest.java
@@ -601,7 +601,7 @@
         }
 
         private AppCompatOrientationOverrides getTopOrientationOverrides() {
-            return activity().top().mAppCompatController.getAppCompatOrientationOverrides();
+            return activity().top().mAppCompatController.getOrientationOverrides();
         }
 
         private AppCompatOrientationPolicy getTopAppCompatOrientationPolicy() {
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 463254c..50419d4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatReachabilityOverridesTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatReachabilityOverridesTest.java
@@ -159,8 +159,8 @@
         @Override
         void onPostActivityCreation(@NonNull ActivityRecord activity) {
             super.onPostActivityCreation(activity);
-            spyOn(activity.mAppCompatController.getAppCompatReachabilityOverrides());
-            activity.mAppCompatController.getAppCompatReachabilityPolicy()
+            spyOn(activity.mAppCompatController.getReachabilityOverrides());
+            activity.mAppCompatController.getReachabilityPolicy()
                     .setLetterboxInnerBoundsSupplier(mLetterboxInnerBoundsSupplier);
         }
 
@@ -196,7 +196,7 @@
 
         @NonNull
         private AppCompatReachabilityOverrides getAppCompatReachabilityOverrides() {
-            return activity().top().mAppCompatController.getAppCompatReachabilityOverrides();
+            return activity().top().mAppCompatController.getReachabilityOverrides();
         }
 
     }
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 ddc4de9..09b8bce 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatReachabilityPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatReachabilityPolicyTest.java
@@ -246,8 +246,8 @@
         @Override
         void onPostActivityCreation(@NonNull ActivityRecord activity) {
             super.onPostActivityCreation(activity);
-            spyOn(activity.mAppCompatController.getAppCompatReachabilityOverrides());
-            activity.mAppCompatController.getAppCompatReachabilityPolicy()
+            spyOn(activity.mAppCompatController.getReachabilityOverrides());
+            activity.mAppCompatController.getReachabilityPolicy()
                     .setLetterboxInnerBoundsSupplier(mLetterboxInnerBoundsSupplier);
         }
 
@@ -281,12 +281,12 @@
 
         @NonNull
         private AppCompatReachabilityOverrides getAppCompatReachabilityOverrides() {
-            return activity().top().mAppCompatController.getAppCompatReachabilityOverrides();
+            return activity().top().mAppCompatController.getReachabilityOverrides();
         }
 
         @NonNull
         private AppCompatReachabilityPolicy getAppCompatReachabilityPolicy() {
-            return activity().top().mAppCompatController.getAppCompatReachabilityPolicy();
+            return activity().top().mAppCompatController.getReachabilityPolicy();
         }
 
     }
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 b8d554b..98a4fb3c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppCompatResizeOverridesTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatResizeOverridesTest.java
@@ -184,12 +184,12 @@
 
         void checkShouldOverrideForceResizeApp(boolean expected) {
             Assert.assertEquals(expected, activity().top().mAppCompatController
-                    .getAppCompatResizeOverrides().shouldOverrideForceResizeApp());
+                    .getResizeOverrides().shouldOverrideForceResizeApp());
         }
 
         void checkShouldOverrideForceNonResizeApp(boolean expected) {
             Assert.assertEquals(expected, activity().top().mAppCompatController
-                    .getAppCompatResizeOverrides().shouldOverrideForceNonResizeApp());
+                    .getResizeOverrides().shouldOverrideForceNonResizeApp());
         }
     }
 
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 b6e393d..03d9042 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -342,8 +342,8 @@
     public void testCancelRemoteAnimationWhenFreeze() {
         final DisplayContent dc = createNewDisplay(Display.STATE_ON);
         doReturn(false).when(dc).onDescendantOrientationChanged(any());
-        final WindowState exitingAppWindow = createWindow(null /* parent */, TYPE_BASE_APPLICATION,
-                dc, "exiting app");
+        final WindowState exitingAppWindow = newWindowBuilder("exiting app",
+                TYPE_BASE_APPLICATION).setDisplay(dc).build();
         final ActivityRecord exitingActivity = exitingAppWindow.mActivityRecord;
         // Wait until everything in animation handler get executed to prevent the exiting window
         // from being removed during WindowSurfacePlacer Traversal.
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 e0e8aa4..fbb123e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
@@ -17,8 +17,6 @@
 package com.android.server.wm;
 
 import static android.app.ActivityOptions.ANIM_SCENE_TRANSITION;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
 import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_EXT_ENABLE_ON_BACK_INVOKED_CALLBACK;
 import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
@@ -60,8 +58,6 @@
 import android.os.RemoteCallback;
 import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
-import android.platform.test.annotations.RequiresFlagsDisabled;
-import android.util.ArraySet;
 import android.view.WindowManager;
 import android.window.BackAnimationAdapter;
 import android.window.BackMotionEvent;
@@ -76,7 +72,6 @@
 import android.window.WindowOnBackInvokedDispatcher;
 
 import com.android.server.LocalServices;
-import com.android.window.flags.Flags;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -625,43 +620,6 @@
     }
 
     @Test
-    @RequiresFlagsDisabled(Flags.FLAG_MIGRATE_PREDICTIVE_BACK_TRANSITION)
-    public void testTransitionHappensCancelNavigation() {
-        // Create a floating task and a fullscreen task, then navigating on fullscreen task.
-        // The navigation should not been cancelled when transition happens on floating task, and
-        // only be cancelled when transition happens on the navigating task.
-        final Task floatingTask = createTask(mDisplayContent, WINDOWING_MODE_MULTI_WINDOW,
-                ACTIVITY_TYPE_STANDARD);
-        final ActivityRecord baseFloatingActivity = createActivityRecord(floatingTask);
-
-        final Task fullscreenTask = createTopTaskWithActivity();
-        withSystemCallback(fullscreenTask);
-        final ActivityRecord baseFullscreenActivity = fullscreenTask.getTopMostActivity();
-
-        final CountDownLatch navigationObserver = new CountDownLatch(1);
-        startBackNavigation(navigationObserver);
-
-        final ArraySet<ActivityRecord> opening = new ArraySet<>();
-        final ArraySet<ActivityRecord> closing = new ArraySet<>();
-        final ActivityRecord secondFloatingActivity = createActivityRecord(floatingTask);
-        opening.add(secondFloatingActivity);
-        closing.add(baseFloatingActivity);
-        mBackNavigationController.removeIfContainsBackAnimationTargets(opening, closing);
-        assertEquals("Transition happen on an irrelevant task, callback should not been called",
-                1, navigationObserver.getCount());
-
-        // Create a new activity above navigation target, the transition should cancel navigation.
-        final ActivityRecord topFullscreenActivity = createActivityRecord(fullscreenTask);
-        opening.clear();
-        closing.clear();
-        opening.add(topFullscreenActivity);
-        closing.add(baseFullscreenActivity);
-        mBackNavigationController.removeIfContainsBackAnimationTargets(opening, closing);
-        assertEquals("Transition happen on navigation task, callback should have been called",
-                0, navigationObserver.getCount());
-    }
-
-    @Test
     public void testWindowFocusChangeCancelNavigation() {
         Task task = createTopTaskWithActivity();
         withSystemCallback(task);
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 854bda0..51706d7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackgroundActivityStartControllerTests.java
@@ -572,7 +572,6 @@
                         + "inVisibleTask: false; "
                         + "balAllowedByPiCreator: BSP.ALLOW_BAL; "
                         + "balAllowedByPiCreatorWithHardening: BSP.ALLOW_BAL; "
-                        + "resultIfPiCreatorAllowsBal: null; "
                         + "callerStartMode: MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED; "
                         + "hasRealCaller: true; "
                         + "isCallForResult: false; "
@@ -589,7 +588,6 @@
                         + "originatingPendingIntent: null; "
                         + "realCallerApp: null; "
                         + "balAllowedByPiSender: BSP.ALLOW_BAL; "
-                        + "resultIfPiSenderAllowsBal: null; "
                         + "realCallerStartMode: MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED; "
                         + "balRequireOptInByPendingIntentCreator: true; "
                         + "balDontBringExistingBackgroundTaskStackToFg: true]");
@@ -677,7 +675,6 @@
                         + "inVisibleTask: false; "
                         + "balAllowedByPiCreator: BSP.NONE; "
                         + "balAllowedByPiCreatorWithHardening: BSP.NONE; "
-                        + "resultIfPiCreatorAllowsBal: null; "
                         + "callerStartMode: MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED; "
                         + "hasRealCaller: true; "
                         + "isCallForResult: false; "
@@ -694,7 +691,6 @@
                         + "originatingPendingIntent: PendingIntentRecord; "
                         + "realCallerApp: null; "
                         + "balAllowedByPiSender: BSP.ALLOW_FGS; "
-                        + "resultIfPiSenderAllowsBal: null; "
                         + "realCallerStartMode: MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED; "
                         + "balRequireOptInByPendingIntentCreator: true; "
                         + "balDontBringExistingBackgroundTaskStackToFg: true]");
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 14276ae..7033d79 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentDeferredUpdateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentDeferredUpdateTests.java
@@ -266,10 +266,10 @@
         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");
+        final WindowState defaultDisplayWindow = newWindowBuilder("DefaultDisplayWindow",
+                TYPE_BASE_APPLICATION).setDisplay(mDisplayContent).build();
+        final WindowState secondaryDisplayWindow = newWindowBuilder("SecondaryDisplayWindow",
+                TYPE_BASE_APPLICATION).setDisplay(mSecondaryDisplayContent).build();
         makeWindowVisibleAndNotDrawn(defaultDisplayWindow, secondaryDisplayWindow);
 
         // Mark as display switching only for the default display as we filter out
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 5cd2a99..5486aa3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -2332,7 +2332,7 @@
         dc.mCurrentUniqueDisplayId = mDisplayInfo.uniqueId + "-test";
         // Trigger display changed.
         updateDisplay(dc);
-        // Ensure overridden size and denisty match the most up-to-date values in settings for the
+        // Ensure overridden size and density match the most up-to-date values in settings for the
         // display.
         verifySizes(dc, forcedWidth, forcedHeight, forcedDensity);
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
index ea925c0..4854f0d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
@@ -88,7 +88,7 @@
     }
 
     private WindowState createDreamWindow() {
-        final WindowState win = createDreamWindow(null, TYPE_BASE_APPLICATION, "dream");
+        final WindowState win = createDreamWindow("dream", TYPE_BASE_APPLICATION);
         final WindowManager.LayoutParams attrs = win.mAttrs;
         attrs.width = MATCH_PARENT;
         attrs.height = MATCH_PARENT;
diff --git a/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java
index bd15bc4..347d1bc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DualDisplayAreaGroupPolicyTest.java
@@ -379,13 +379,11 @@
         assertThat(imeContainer.getRootDisplayArea()).isEqualTo(mDisplay);
         assertThat(mDisplay.findAreaForTokenInLayer(imeToken)).isEqualTo(imeContainer);
 
-        final WindowState firstActivityWin =
-                createWindow(null /* parent */, TYPE_APPLICATION_STARTING, mFirstActivity,
-                        "firstActivityWin");
+        final WindowState firstActivityWin = newWindowBuilder("firstActivityWin",
+                TYPE_APPLICATION_STARTING).setWindowToken(mFirstActivity).build();
         spyOn(firstActivityWin);
-        final WindowState secondActivityWin =
-                createWindow(null /* parent */, TYPE_APPLICATION_STARTING, mSecondActivity,
-                        "firstActivityWin");
+        final WindowState secondActivityWin = newWindowBuilder("secondActivityWin",
+                TYPE_APPLICATION_STARTING).setWindowToken(mSecondActivity).build();
         spyOn(secondActivityWin);
 
         // firstActivityWin should be the target
@@ -424,13 +422,11 @@
         setupImeWindow();
         final DisplayArea.Tokens imeContainer = mDisplay.getImeContainer();
         final WindowToken imeToken = tokenOfType(TYPE_INPUT_METHOD);
-        final WindowState firstActivityWin =
-                createWindow(null /* parent */, TYPE_APPLICATION_STARTING, mFirstActivity,
-                        "firstActivityWin");
+        final WindowState firstActivityWin = newWindowBuilder("firstActivityWin",
+                TYPE_APPLICATION_STARTING).setWindowToken(mFirstActivity).build();
         spyOn(firstActivityWin);
-        final WindowState secondActivityWin =
-                createWindow(null /* parent */, TYPE_APPLICATION_STARTING, mSecondActivity,
-                        "secondActivityWin");
+        final WindowState secondActivityWin = newWindowBuilder("secondActivityWin",
+                TYPE_APPLICATION_STARTING).setWindowToken(mSecondActivity).build();
         spyOn(secondActivityWin);
 
         // firstActivityWin should be the target
@@ -464,9 +460,8 @@
         assertThat(imeContainer.getRootDisplayArea()).isEqualTo(mDisplay);
         assertThat(mDisplay.findAreaForTokenInLayer(imeToken)).isEqualTo(imeContainer);
 
-        final WindowState firstActivityWin =
-                createWindow(null /* parent */, TYPE_APPLICATION_STARTING, mFirstActivity,
-                        "firstActivityWin");
+        final WindowState firstActivityWin = newWindowBuilder("firstActivityWin",
+                TYPE_APPLICATION_STARTING).setWindowToken(mFirstActivity).build();
         spyOn(firstActivityWin);
         // firstActivityWin should be the target
         doReturn(true).when(firstActivityWin).canBeImeTarget();
@@ -499,9 +494,8 @@
         assertThat(imeContainer.getRootDisplayArea()).isEqualTo(mDisplay);
 
         // firstActivityWin should be the target
-        final WindowState firstActivityWin =
-                createWindow(null /* parent */, TYPE_APPLICATION_STARTING, mFirstActivity,
-                        "firstActivityWin");
+        final WindowState firstActivityWin = newWindowBuilder("firstActivityWin",
+                TYPE_APPLICATION_STARTING).setWindowToken(mFirstActivity).build();
         spyOn(firstActivityWin);
         doReturn(true).when(firstActivityWin).canBeImeTarget();
         WindowState imeTarget = mDisplay.computeImeTarget(true /* updateImeTarget */);
@@ -560,8 +554,8 @@
     }
 
     private void setupImeWindow() {
-        final WindowState imeWindow = createWindow(null /* parent */,
-                TYPE_INPUT_METHOD, mDisplay, "mImeWindow");
+        final WindowState imeWindow = newWindowBuilder("mImeWindow", TYPE_INPUT_METHOD).setDisplay(
+                mDisplay).build();
         imeWindow.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
         mDisplay.mInputMethodWindow = imeWindow;
     }
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 9d9f24c..96b11a8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -330,7 +330,7 @@
         if (horizontalReachability) {
             final Consumer<Integer> doubleClick =
                     (Integer x) -> {
-                        mActivity.mAppCompatController.getAppCompatReachabilityPolicy()
+                        mActivity.mAppCompatController.getReachabilityPolicy()
                                 .handleDoubleTap(x, displayHeight / 2);
                         mActivity.mRootWindowContainer.performSurfacePlacement();
                     };
@@ -360,7 +360,7 @@
         } else {
             final Consumer<Integer> doubleClick =
                     (Integer y) -> {
-                        mActivity.mAppCompatController.getAppCompatReachabilityPolicy()
+                        mActivity.mAppCompatController.getReachabilityPolicy()
                                 .handleDoubleTap(displayWidth / 2, y);
                         mActivity.mRootWindowContainer.performSurfacePlacement();
                     };
@@ -421,7 +421,7 @@
 
         final Consumer<Integer> doubleClick =
                 (Integer y) -> {
-                    activity.mAppCompatController.getAppCompatReachabilityPolicy()
+                    activity.mAppCompatController.getReachabilityPolicy()
                             .handleDoubleTap(dw / 2, y);
                     activity.mRootWindowContainer.performSurfacePlacement();
                 };
@@ -834,7 +834,7 @@
         // Change the fixed orientation.
         mActivity.setRequestedOrientation(SCREEN_ORIENTATION_LANDSCAPE);
         assertTrue(mActivity.isRelaunching());
-        assertTrue(mActivity.mAppCompatController.getAppCompatOrientationOverrides()
+        assertTrue(mActivity.mAppCompatController.getOrientationOverrides()
                 .getIsRelaunchingAfterRequestedOrientationChanged());
 
         assertFitted();
@@ -3427,7 +3427,7 @@
 
         setUpAllowThinLetterboxed(/* thinLetterboxAllowed */ false);
         final AppCompatReachabilityOverrides reachabilityOverrides =
-                mActivity.mAppCompatController.getAppCompatReachabilityOverrides();
+                mActivity.mAppCompatController.getReachabilityOverrides();
         assertFalse(reachabilityOverrides.isVerticalReachabilityEnabled());
         assertFalse(reachabilityOverrides.isHorizontalReachabilityEnabled());
     }
@@ -3451,7 +3451,7 @@
         assertEquals(WINDOWING_MODE_MULTI_WINDOW, mActivity.getWindowingMode());
 
         // Horizontal reachability is disabled because the app is in split screen.
-        assertFalse(mActivity.mAppCompatController.getAppCompatReachabilityOverrides()
+        assertFalse(mActivity.mAppCompatController.getReachabilityOverrides()
                 .isHorizontalReachabilityEnabled());
     }
 
@@ -3475,7 +3475,7 @@
         assertEquals(WINDOWING_MODE_MULTI_WINDOW, mActivity.getWindowingMode());
 
         // Vertical reachability is disabled because the app is in split screen.
-        assertFalse(mActivity.mAppCompatController.getAppCompatReachabilityOverrides()
+        assertFalse(mActivity.mAppCompatController.getReachabilityOverrides()
                 .isVerticalReachabilityEnabled());
     }
 
@@ -3498,7 +3498,7 @@
         // Vertical reachability is disabled because the app does not match parent width
         assertNotEquals(mActivity.getScreenResolvedBounds().width(),
                 mActivity.mDisplayContent.getBounds().width());
-        assertFalse(mActivity.mAppCompatController.getAppCompatReachabilityOverrides()
+        assertFalse(mActivity.mAppCompatController.getReachabilityOverrides()
                 .isVerticalReachabilityEnabled());
     }
 
@@ -3516,7 +3516,7 @@
         assertEquals(new Rect(0, 0, 0, 0), mActivity.getBounds());
 
         // Vertical reachability is still enabled as resolved bounds is not empty
-        assertTrue(mActivity.mAppCompatController.getAppCompatReachabilityOverrides()
+        assertTrue(mActivity.mAppCompatController.getReachabilityOverrides()
                 .isVerticalReachabilityEnabled());
     }
 
@@ -3533,7 +3533,7 @@
         assertEquals(new Rect(0, 0, 0, 0), mActivity.getBounds());
 
         // Horizontal reachability is still enabled as resolved bounds is not empty
-        assertTrue(mActivity.mAppCompatController.getAppCompatReachabilityOverrides()
+        assertTrue(mActivity.mAppCompatController.getReachabilityOverrides()
                 .isHorizontalReachabilityEnabled());
     }
 
@@ -3548,7 +3548,7 @@
         prepareMinAspectRatio(mActivity, OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE,
                 SCREEN_ORIENTATION_PORTRAIT);
 
-        assertTrue(mActivity.mAppCompatController.getAppCompatReachabilityOverrides()
+        assertTrue(mActivity.mAppCompatController.getReachabilityOverrides()
                 .isHorizontalReachabilityEnabled());
     }
 
@@ -3563,7 +3563,7 @@
         prepareMinAspectRatio(mActivity, OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE,
                 SCREEN_ORIENTATION_LANDSCAPE);
 
-        assertTrue(mActivity.mAppCompatController.getAppCompatReachabilityOverrides()
+        assertTrue(mActivity.mAppCompatController.getReachabilityOverrides()
                 .isVerticalReachabilityEnabled());
     }
 
@@ -3585,7 +3585,7 @@
         // Horizontal reachability is disabled because the app does not match parent height
         assertNotEquals(mActivity.getScreenResolvedBounds().height(),
                 mActivity.mDisplayContent.getBounds().height());
-        assertFalse(mActivity.mAppCompatController.getAppCompatReachabilityOverrides()
+        assertFalse(mActivity.mAppCompatController.getReachabilityOverrides()
                 .isHorizontalReachabilityEnabled());
     }
 
@@ -3608,7 +3608,7 @@
         // Horizontal reachability is enabled because the app matches parent height
         assertEquals(mActivity.getScreenResolvedBounds().height(),
                 mActivity.mDisplayContent.getBounds().height());
-        assertTrue(mActivity.mAppCompatController.getAppCompatReachabilityOverrides()
+        assertTrue(mActivity.mAppCompatController.getReachabilityOverrides()
                 .isHorizontalReachabilityEnabled());
     }
 
@@ -3631,7 +3631,7 @@
         // Vertical reachability is enabled because the app matches parent width
         assertEquals(mActivity.getScreenResolvedBounds().width(),
                 mActivity.mDisplayContent.getBounds().width());
-        assertTrue(mActivity.mAppCompatController.getAppCompatReachabilityOverrides()
+        assertTrue(mActivity.mAppCompatController.getReachabilityOverrides()
                 .isVerticalReachabilityEnabled());
     }
 
@@ -4315,7 +4315,7 @@
 
         // Make sure app doesn't jump to top (default tabletop position) when unfolding.
         assertEquals(1.0f, mActivity.mAppCompatController
-                .getAppCompatReachabilityOverrides().getVerticalPositionMultiplier(mActivity
+                .getReachabilityOverrides().getVerticalPositionMultiplier(mActivity
                         .getParent().getConfiguration()), 0);
 
         // Simulate display fully open after unfolding.
@@ -4323,7 +4323,7 @@
         doReturn(false).when(mActivity.mDisplayContent).inTransition();
 
         assertEquals(1.0f, mActivity.mAppCompatController
-                .getAppCompatReachabilityOverrides().getVerticalPositionMultiplier(mActivity
+                .getReachabilityOverrides().getVerticalPositionMultiplier(mActivity
                         .getParent().getConfiguration()), 0);
     }
 
@@ -5028,7 +5028,7 @@
 
     private void setUpAllowThinLetterboxed(boolean thinLetterboxAllowed) {
         final AppCompatReachabilityOverrides reachabilityOverrides =
-                mActivity.mAppCompatController.getAppCompatReachabilityOverrides();
+                mActivity.mAppCompatController.getReachabilityOverrides();
         spyOn(reachabilityOverrides);
         doReturn(thinLetterboxAllowed).when(reachabilityOverrides)
                 .allowVerticalReachabilityForThinLetterbox();
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 921228f..a12831e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -75,6 +75,7 @@
 import android.view.DisplayInfo;
 import android.view.InputChannel;
 import android.view.SurfaceControl;
+import android.window.ConfigurationChangeSetting;
 
 import com.android.dx.mockito.inline.extended.StaticMockitoSession;
 import com.android.internal.os.BackgroundThread;
@@ -99,6 +100,7 @@
 import com.android.server.statusbar.StatusBarManagerInternal;
 import com.android.server.testutils.StubTransaction;
 import com.android.server.uri.UriGrantsManagerInternal;
+import com.android.window.flags.Flags;
 
 import org.junit.rules.TestRule;
 import org.junit.runner.Description;
@@ -501,6 +503,10 @@
         LocalServices.removeServiceForTest(UserManagerInternal.class);
         LocalServices.removeServiceForTest(ImeTargetVisibilityPolicy.class);
         LocalServices.removeServiceForTest(GrammaticalInflectionManagerInternal.class);
+        if (Flags.condenseConfigurationChangeForSimpleMode()) {
+            LocalServices.removeServiceForTest(
+                    ConfigurationChangeSetting.ConfigurationChangeSettingInternal.class);
+        }
     }
 
     Description getDescription() {
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 19c1ce2..ef58498 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentOrganizerControllerTest.java
@@ -638,6 +638,7 @@
     @Test
     public void testApplyTransaction_enforceHierarchyChange_deleteTaskFragment() {
         doReturn(true).when(mTaskFragment).isAttached();
+        doReturn(1).when(mTaskFragment).getNonFinishingActivityCount();
         mWindowOrganizerController.mLaunchTaskFragments.put(mFragmentToken, mTaskFragment);
 
         // Throw exception if the transaction is trying to change a window that is not organized by
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 c0f251e..2997173 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskFragmentTest.java
@@ -132,8 +132,8 @@
         final int parentSw = parentConfig.smallestScreenWidthDp;
         final Rect bounds = new Rect(parentBounds);
         bounds.inset(100, 100);
-        mTaskFragment.setBounds(bounds);
         mTaskFragment.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+        mTaskFragment.setBounds(bounds);
         // Calculate its own sw with smaller bounds in multi-window mode.
         assertNotEquals(parentSw, mTaskFragment.getConfiguration().smallestScreenWidthDp);
 
@@ -878,8 +878,10 @@
                 .build();
         final ActivityRecord activity0 = tf0.getTopMostActivity();
         final ActivityRecord activity1 = tf1.getTopMostActivity();
-        final WindowState win0 = createWindow(null, TYPE_BASE_APPLICATION, activity0, "win0");
-        final WindowState win1 = createWindow(null, TYPE_BASE_APPLICATION, activity1, "win1");
+        final WindowState win0 = newWindowBuilder("win0", TYPE_BASE_APPLICATION).setWindowToken(
+                activity0).build();
+        final WindowState win1 = newWindowBuilder("win1", TYPE_BASE_APPLICATION).setWindowToken(
+                activity1).build();
         doReturn(false).when(mDisplayContent).shouldImeAttachedToApp();
 
         mDisplayContent.setImeInputTarget(win0);
@@ -1174,8 +1176,8 @@
     }
 
     private WindowState createAppWindow(ActivityRecord app, String name) {
-        final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, app, name,
-                0 /* ownerId */, false /* ownerCanAddInternalSystemWindow */, new TestIWindow());
+        final WindowState win = newWindowBuilder(name, TYPE_BASE_APPLICATION).setWindowToken(
+                app).setClientWindow(new TestIWindow()).build();
         mWm.mWindowMap.put(win.mClient.asBinder(), win);
         return win;
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java
index f145b40..f9250f9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java
@@ -63,7 +63,7 @@
 
     @Test
     public void testAppRemoved() {
-        final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window");
+        final WindowState window = newWindowBuilder("window", FIRST_APPLICATION_WINDOW).build();
         mCache.putSnapshot(window.getTask(), createSnapshot());
         assertNotNull(mCache.getSnapshot(window.getTask().mTaskId, false /* isLowResolution */));
         mCache.onAppRemoved(window.mActivityRecord);
@@ -72,7 +72,7 @@
 
     @Test
     public void testAppDied() {
-        final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window");
+        final WindowState window = newWindowBuilder("window", FIRST_APPLICATION_WINDOW).build();
         mCache.putSnapshot(window.getTask(), createSnapshot());
         assertNotNull(mCache.getSnapshot(window.getTask().mTaskId, false /* isLowResolution */));
         mCache.onAppDied(window.mActivityRecord);
@@ -81,7 +81,7 @@
 
     @Test
     public void testTaskRemoved() {
-        final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window");
+        final WindowState window = newWindowBuilder("window", FIRST_APPLICATION_WINDOW).build();
         mCache.putSnapshot(window.getTask(), createSnapshot());
         assertNotNull(mCache.getSnapshot(window.getTask().mTaskId, false /* isLowResolution */));
         mCache.onIdRemoved(window.getTask().mTaskId);
@@ -90,7 +90,7 @@
 
     @Test
     public void testReduced_notCached() {
-        final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window");
+        final WindowState window = newWindowBuilder("window", FIRST_APPLICATION_WINDOW).build();
         mPersister.persistSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId, createSnapshot());
         mSnapshotPersistQueue.waitForQueueEmpty();
         assertNull(mCache.getSnapshot(window.getTask().mTaskId, false /* isLowResolution */));
@@ -105,7 +105,7 @@
 
     @Test
     public void testRestoreFromDisk() {
-        final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window");
+        final WindowState window = newWindowBuilder("window", FIRST_APPLICATION_WINDOW).build();
         mPersister.persistSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId, createSnapshot());
         mSnapshotPersistQueue.waitForQueueEmpty();
         assertNull(mCache.getSnapshot(window.getTask().mTaskId, false /* isLowResolution */));
@@ -117,7 +117,7 @@
 
     @Test
     public void testClearCache() {
-        final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window");
+        final WindowState window = newWindowBuilder("window", FIRST_APPLICATION_WINDOW).build();
         mCache.putSnapshot(window.getTask(), mSnapshot);
         assertEquals(mSnapshot, mCache.getSnapshot(window.getTask().mTaskId,
                 false /* isLowResolution */));
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
index c6b2a6b..1bca53a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
@@ -74,8 +74,8 @@
 
     @Test
     public void testGetClosingApps_closing() {
-        final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW,
-                "closingWindow");
+        final WindowState closingWindow = newWindowBuilder("closingWindow",
+                FIRST_APPLICATION_WINDOW).build();
         closingWindow.mActivityRecord.commitVisibility(
                 false /* visible */, true /* performLayout */);
         final ArraySet<ActivityRecord> closingApps = new ArraySet<>();
@@ -88,8 +88,8 @@
 
     @Test
     public void testGetClosingApps_notClosing() {
-        final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW,
-                "closingWindow");
+        final WindowState closingWindow = newWindowBuilder("closingWindow",
+                FIRST_APPLICATION_WINDOW).build();
         final WindowState openingWindow = createAppWindow(closingWindow.getTask(),
                 FIRST_APPLICATION_WINDOW, "openingWindow");
         closingWindow.mActivityRecord.commitVisibility(
@@ -105,8 +105,8 @@
 
     @Test
     public void testGetClosingApps_skipClosingAppsSnapshotTasks() {
-        final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW,
-                "closingWindow");
+        final WindowState closingWindow = newWindowBuilder("closingWindow",
+                FIRST_APPLICATION_WINDOW).build();
         closingWindow.mActivityRecord.commitVisibility(
                 false /* visible */, true /* performLayout */);
         final ArraySet<ActivityRecord> closingApps = new ArraySet<>();
@@ -133,19 +133,19 @@
 
     @Test
     public void testGetSnapshotMode() {
-        final WindowState disabledWindow = createWindow(null,
-                FIRST_APPLICATION_WINDOW, mDisplayContent, "disabledWindow");
+        final WindowState disabledWindow = newWindowBuilder("disabledWindow",
+                FIRST_APPLICATION_WINDOW).setDisplay(mDisplayContent).build();
         disabledWindow.mActivityRecord.setRecentsScreenshotEnabled(false);
         assertEquals(SNAPSHOT_MODE_APP_THEME,
                 mWm.mTaskSnapshotController.getSnapshotMode(disabledWindow.getTask()));
 
-        final WindowState normalWindow = createWindow(null,
-                FIRST_APPLICATION_WINDOW, mDisplayContent, "normalWindow");
+        final WindowState normalWindow = newWindowBuilder("normalWindow",
+                FIRST_APPLICATION_WINDOW).setDisplay(mDisplayContent).build();
         assertEquals(SNAPSHOT_MODE_REAL,
                 mWm.mTaskSnapshotController.getSnapshotMode(normalWindow.getTask()));
 
-        final WindowState secureWindow = createWindow(null,
-                FIRST_APPLICATION_WINDOW, mDisplayContent, "secureWindow");
+        final WindowState secureWindow = newWindowBuilder("secureWindow",
+                FIRST_APPLICATION_WINDOW).setDisplay(mDisplayContent).build();
         secureWindow.mAttrs.flags |= FLAG_SECURE;
         assertEquals(SNAPSHOT_MODE_APP_THEME,
                 mWm.mTaskSnapshotController.getSnapshotMode(secureWindow.getTask()));
@@ -297,8 +297,8 @@
         spyOn(mWm.mTaskSnapshotController);
         doReturn(false).when(mWm.mTaskSnapshotController).shouldDisableSnapshots();
 
-        final WindowState normalWindow = createWindow(null,
-                FIRST_APPLICATION_WINDOW, mDisplayContent, "normalWindow");
+        final WindowState normalWindow = newWindowBuilder("normalWindow",
+                FIRST_APPLICATION_WINDOW).setDisplay(mDisplayContent).build();
         final TaskSnapshot snapshot = new TaskSnapshotPersisterTestBase.TaskSnapshotBuilder()
                 .setTopActivityComponent(normalWindow.mActivityRecord.mActivityComponent).build();
         doReturn(snapshot).when(mWm.mTaskSnapshotController).snapshot(any());
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotLowResDisabledTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotLowResDisabledTest.java
index 9bde066..51ea498 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotLowResDisabledTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotLowResDisabledTest.java
@@ -41,7 +41,7 @@
  * Test class for {@link TaskSnapshotPersister} and {@link AppSnapshotLoader}
  *
  * Build/Install/Run:
- * atest TaskSnapshotPersisterLoaderTest
+ * atest TaskSnapshotLowResDisabledTest
  */
 @MediumTest
 @Presubmit
@@ -126,7 +126,7 @@
 
     @Test
     public void testReduced_notCached() {
-        final WindowState window = createWindow(null, FIRST_APPLICATION_WINDOW, "window");
+        final WindowState window = newWindowBuilder("window", FIRST_APPLICATION_WINDOW).build();
         mPersister.persistSnapshot(window.getTask().mTaskId, mWm.mCurrentUserId, createSnapshot());
         mSnapshotPersistQueue.waitForQueueEmpty();
         assertNull(mCache.getSnapshot(window.getTask().mTaskId, false /* isLowResolution */));
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index 1fa6578..5ed2df3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -504,8 +504,8 @@
         assertTrue(child.isAnimating(PARENTS, ANIMATION_TYPE_APP_TRANSITION));
         assertFalse(child.isAnimating(PARENTS, ANIMATION_TYPE_SCREEN_ROTATION));
 
-        final WindowState windowState = createWindow(null /* parent */, TYPE_BASE_APPLICATION,
-                mDisplayContent, "TestWindowState");
+        final WindowState windowState = newWindowBuilder("TestWindowState",
+                TYPE_BASE_APPLICATION).setDisplay(mDisplayContent).build();
         WindowContainer parent = windowState.getParent();
         spyOn(windowState.mSurfaceAnimator);
         doReturn(true).when(windowState.mSurfaceAnimator).isAnimating();
@@ -1045,8 +1045,8 @@
 
         // An animating window with mRemoveOnExit can be removed by handleCompleteDeferredRemoval
         // once it no longer animates.
-        final WindowState exitingWindow = createWindow(null, TYPE_APPLICATION_OVERLAY,
-                displayContent, "exiting window");
+        final WindowState exitingWindow = newWindowBuilder("exiting window",
+                TYPE_APPLICATION_OVERLAY).setDisplay(displayContent).build();
         exitingWindow.startAnimation(exitingWindow.getPendingTransaction(),
                 mock(AnimationAdapter.class), false /* hidden */,
                 SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION);
@@ -1063,7 +1063,7 @@
         final ActivityRecord r = new TaskBuilder(mSupervisor).setCreateActivity(true)
                 .setDisplay(displayContent).build().getTopMostActivity();
         // Add a window and make the activity animating so the removal of activity is deferred.
-        createWindow(null, TYPE_BASE_APPLICATION, r, "win");
+        newWindowBuilder("win", TYPE_BASE_APPLICATION).setWindowToken(r).build();
         doReturn(true).when(r).isAnimating(anyInt(), anyInt());
 
         displayContent.remove();
@@ -1216,7 +1216,8 @@
     public void testFreezeInsets() {
         final Task task = createTask(mDisplayContent);
         final ActivityRecord activity = createActivityRecord(mDisplayContent, task);
-        final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, activity, "win");
+        final WindowState win = newWindowBuilder("win", TYPE_BASE_APPLICATION).setWindowToken(
+                activity).build();
 
         // Set visibility to false, verify the main window of the task will be set the frozen
         // insets state immediately.
@@ -1233,7 +1234,8 @@
         final Task rootTask = createTask(mDisplayContent);
         final Task task = createTaskInRootTask(rootTask, 0 /* userId */);
         final ActivityRecord activity = createActivityRecord(mDisplayContent, task);
-        final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, activity, "win");
+        final WindowState win = newWindowBuilder("win", TYPE_BASE_APPLICATION).setWindowToken(
+                activity).build();
         task.getDisplayContent().prepareAppTransition(TRANSIT_CLOSE);
         spyOn(win);
         doReturn(true).when(task).okToAnimate();
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 72935cb..8606581 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java
@@ -49,9 +49,10 @@
     @SetupWindows(addWindows = { W_DOCK_DIVIDER, W_INPUT_METHOD })
     @Test
     public void testDockedDividerPosition() {
-        final WindowState splitScreenWindow = createWindow(null,
-                WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION,
-                mDisplayContent, "splitScreenWindow");
+        final WindowState splitScreenWindow = newWindowBuilder("splitScreenWindow",
+                TYPE_BASE_APPLICATION).setWindowingMode(
+                WINDOWING_MODE_MULTI_WINDOW).setActivityType(ACTIVITY_TYPE_STANDARD).setDisplay(
+                mDisplayContent).build();
 
         mDisplayContent.setImeLayeringTarget(splitScreenWindow);
 
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 94c7a32..d1f5d15 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -84,6 +84,8 @@
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.InputConfig;
+import android.os.Parcel;
+import android.os.Parcelable;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.UserHandle;
@@ -92,6 +94,7 @@
 import android.platform.test.annotations.EnableFlags;
 import android.platform.test.annotations.Presubmit;
 import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.provider.Settings;
 import android.util.ArraySet;
 import android.util.MergedConfiguration;
 import android.view.ContentRecordingSession;
@@ -109,6 +112,7 @@
 import android.view.WindowRelayoutResult;
 import android.window.ActivityWindowInfo;
 import android.window.ClientWindowFrames;
+import android.window.ConfigurationChangeSetting;
 import android.window.InputTransferToken;
 import android.window.ScreenCapture;
 import android.window.WindowContainerToken;
@@ -126,12 +130,15 @@
 import com.google.common.truth.Expect;
 
 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 java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Stream;
 
 /**
  * Build/Install/Run:
@@ -153,9 +160,15 @@
     @Rule
     public Expect mExpect = Expect.create();
 
+    @Before
+    public void setUp() {
+        Settings.System.clearProviderForTest();
+    }
+
     @After
     public void tearDown() {
         mWm.mSensitiveContentPackages.clearBlockedApps();
+        Settings.System.clearProviderForTest();
     }
 
     @Test
@@ -1487,14 +1500,83 @@
         verify(mWm.mWindowPlacerLocked).requestTraversal();
     }
 
+    @Test
+    @EnableFlags(Flags.FLAG_CONDENSE_CONFIGURATION_CHANGE_FOR_SIMPLE_MODE)
+    public void createImplFromParcel_invalidSettingType_throwsException() {
+        final Parcelable.Creator<ConfigurationChangeSetting> creator =
+                new ConfigurationChangeSetting.CreatorImpl(true /* isSystem */);
+        final Parcel parcel = Parcel.obtain();
+        try {
+            parcel.writeInt(ConfigurationChangeSetting.SETTING_TYPE_UNKNOWN);
+            parcel.setDataPosition(0);
 
-    class TestResultReceiver implements IResultReceiver {
+            assertThrows(IllegalArgumentException.class, () -> {
+                creator.createFromParcel(parcel);
+            });
+        } finally {
+            parcel.recycle();
+        }
+    }
+
+    @Test
+    @EnableFlags(Flags.FLAG_CONDENSE_CONFIGURATION_CHANGE_FOR_SIMPLE_MODE)
+    public void setConfigurationChangeSettingsForUser_createsFromParcel_callsSettingImpl()
+            throws Settings.SettingNotFoundException {
+        final int userId = 0;
+        final int forcedDensity = 400;
+        final float forcedFontScaleFactor = 1.15f;
+        final Parcelable.Creator<ConfigurationChangeSetting> creator =
+                new ConfigurationChangeSetting.CreatorImpl(true /* isSystem */);
+        final List<ConfigurationChangeSetting> settings = Stream.of(
+                // Display Size
+                new ConfigurationChangeSetting.DensitySetting(DEFAULT_DISPLAY, forcedDensity),
+                // Font Size
+                new ConfigurationChangeSetting.FontScaleSetting(forcedFontScaleFactor)
+        ).map(setting -> simulateIpcTransfer(setting, creator)).toList();
+
+        mWm.setConfigurationChangeSettingsForUser(settings, UserHandle.USER_CURRENT);
+
+        verify(mDisplayContent).setForcedDensity(forcedDensity, userId);
+        assertEquals(forcedFontScaleFactor, Settings.System.getFloat(
+                mContext.getContentResolver(), Settings.System.FONT_SCALE), 0.1f /* delta */);
+        verify(mAtm).updateFontScaleIfNeeded(userId);
+    }
+
+    @Test
+    @DisableFlags(Flags.FLAG_CONDENSE_CONFIGURATION_CHANGE_FOR_SIMPLE_MODE)
+    public void setConfigurationChangeSettingsForUser_flagDisabled_throwsException() {
+        final List<ConfigurationChangeSetting> settings = List.of();
+
+        assertThrows(IllegalStateException.class, () -> {
+            mWm.setConfigurationChangeSettingsForUser(settings, UserHandle.USER_CURRENT);
+        });
+    }
+
+    /**
+     * Simulates IPC transfer by writing the setting to a parcel and reading it back.
+     *
+     * @param setting the setting to transfer.
+     * @param creator the creator to use for reconstructing the setting from the parcel.
+     * @return a new instance of the setting created from the parcel.
+     */
+    private static <T extends ConfigurationChangeSetting> T simulateIpcTransfer(
+            T setting, Parcelable.Creator<T> creator) {
+        final Parcel parcel = Parcel.obtain();
+        try {
+            setting.writeToParcel(parcel, 0);
+            parcel.setDataPosition(0);
+            return creator.createFromParcel(parcel);
+        } finally {
+            parcel.recycle();
+        }
+    }
+
+    private static class TestResultReceiver implements IResultReceiver {
         public android.os.Bundle resultData;
         private final IBinder mBinder = mock(IBinder.class);
 
         @Override
-        public void send(int resultCode, android.os.Bundle resultData)
-                throws android.os.RemoteException {
+        public void send(int resultCode, android.os.Bundle resultData) {
             this.resultData = resultData;
         }
 
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 50e0e18..ab9abfc 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -154,9 +154,11 @@
 
     @Test
     public void testIsParentWindowHidden() {
-        final WindowState parentWindow = createWindow(null, TYPE_APPLICATION, "parentWindow");
-        final WindowState child1 = createWindow(parentWindow, FIRST_SUB_WINDOW, "child1");
-        final WindowState child2 = createWindow(parentWindow, FIRST_SUB_WINDOW, "child2");
+        final WindowState parentWindow = newWindowBuilder("parentWindow", TYPE_APPLICATION).build();
+        final WindowState child1 = newWindowBuilder("child1", FIRST_SUB_WINDOW).setParent(
+                parentWindow).build();
+        final WindowState child2 = newWindowBuilder("child2", FIRST_SUB_WINDOW).setParent(
+                parentWindow).build();
 
         // parentWindow is initially set to hidden.
         assertTrue(parentWindow.mHidden);
@@ -172,10 +174,12 @@
 
     @Test
     public void testIsChildWindow() {
-        final WindowState parentWindow = createWindow(null, TYPE_APPLICATION, "parentWindow");
-        final WindowState child1 = createWindow(parentWindow, FIRST_SUB_WINDOW, "child1");
-        final WindowState child2 = createWindow(parentWindow, FIRST_SUB_WINDOW, "child2");
-        final WindowState randomWindow = createWindow(null, TYPE_APPLICATION, "randomWindow");
+        final WindowState parentWindow = newWindowBuilder("parentWindow", TYPE_APPLICATION).build();
+        final WindowState child1 = newWindowBuilder("child1", FIRST_SUB_WINDOW).setParent(
+                parentWindow).build();
+        final WindowState child2 = newWindowBuilder("child2", FIRST_SUB_WINDOW).setParent(
+                parentWindow).build();
+        final WindowState randomWindow = newWindowBuilder("randomWindow", TYPE_APPLICATION).build();
 
         assertFalse(parentWindow.isChildWindow());
         assertTrue(child1.isChildWindow());
@@ -185,12 +189,15 @@
 
     @Test
     public void testHasChild() {
-        final WindowState win1 = createWindow(null, TYPE_APPLICATION, "win1");
-        final WindowState win11 = createWindow(win1, FIRST_SUB_WINDOW, "win11");
-        final WindowState win12 = createWindow(win1, FIRST_SUB_WINDOW, "win12");
-        final WindowState win2 = createWindow(null, TYPE_APPLICATION, "win2");
-        final WindowState win21 = createWindow(win2, FIRST_SUB_WINDOW, "win21");
-        final WindowState randomWindow = createWindow(null, TYPE_APPLICATION, "randomWindow");
+        final WindowState win1 = newWindowBuilder("win1", TYPE_APPLICATION).build();
+        final WindowState win11 = newWindowBuilder("win11", FIRST_SUB_WINDOW).setParent(
+                win1).build();
+        final WindowState win12 = newWindowBuilder("win12", FIRST_SUB_WINDOW).setParent(
+                win1).build();
+        final WindowState win2 = newWindowBuilder("win2", TYPE_APPLICATION).build();
+        final WindowState win21 = newWindowBuilder("win21", FIRST_SUB_WINDOW).setParent(
+                win2).build();
+        final WindowState randomWindow = newWindowBuilder("randomWindow", TYPE_APPLICATION).build();
 
         assertTrue(win1.hasChild(win11));
         assertTrue(win1.hasChild(win12));
@@ -206,9 +213,11 @@
 
     @Test
     public void testGetParentWindow() {
-        final WindowState parentWindow = createWindow(null, TYPE_APPLICATION, "parentWindow");
-        final WindowState child1 = createWindow(parentWindow, FIRST_SUB_WINDOW, "child1");
-        final WindowState child2 = createWindow(parentWindow, FIRST_SUB_WINDOW, "child2");
+        final WindowState parentWindow = newWindowBuilder("parentWindow", TYPE_APPLICATION).build();
+        final WindowState child1 = newWindowBuilder("child1", FIRST_SUB_WINDOW).setParent(
+                parentWindow).build();
+        final WindowState child2 = newWindowBuilder("child2", FIRST_SUB_WINDOW).setParent(
+                parentWindow).build();
 
         assertNull(parentWindow.getParentWindow());
         assertEquals(parentWindow, child1.getParentWindow());
@@ -217,8 +226,8 @@
 
     @Test
     public void testOverlayWindowHiddenWhenSuspended() {
-        final WindowState overlayWindow = spy(createWindow(null, TYPE_APPLICATION_OVERLAY,
-                "overlayWindow"));
+        final WindowState overlayWindow = spy(
+                newWindowBuilder("overlayWindow", TYPE_APPLICATION_OVERLAY).build());
         overlayWindow.setHiddenWhileSuspended(true);
         verify(overlayWindow).hide(true /* doAnimation */, true /* requestAnim */);
         overlayWindow.setHiddenWhileSuspended(false);
@@ -227,9 +236,11 @@
 
     @Test
     public void testGetTopParentWindow() {
-        final WindowState root = createWindow(null, TYPE_APPLICATION, "root");
-        final WindowState child1 = createWindow(root, FIRST_SUB_WINDOW, "child1");
-        final WindowState child2 = createWindow(child1, FIRST_SUB_WINDOW, "child2");
+        final WindowState root = newWindowBuilder("root", TYPE_APPLICATION).build();
+        final WindowState child1 = newWindowBuilder("child1", FIRST_SUB_WINDOW).setParent(
+                root).build();
+        final WindowState child2 = newWindowBuilder("child2", FIRST_SUB_WINDOW).setParent(
+                child1).build();
 
         assertEquals(root, root.getTopParentWindow());
         assertEquals(root, child1.getTopParentWindow());
@@ -244,7 +255,7 @@
 
     @Test
     public void testIsOnScreen_hiddenByPolicy() {
-        final WindowState window = createWindow(null, TYPE_APPLICATION, "window");
+        final WindowState window = newWindowBuilder("window", TYPE_APPLICATION).build();
         window.setHasSurface(true);
         assertTrue(window.isOnScreen());
         window.hide(false /* doAnimation */, false /* requestAnim */);
@@ -273,8 +284,8 @@
 
     @Test
     public void testCanBeImeTarget() {
-        final WindowState appWindow = createWindow(null, TYPE_APPLICATION, "appWindow");
-        final WindowState imeWindow = createWindow(null, TYPE_INPUT_METHOD, "imeWindow");
+        final WindowState appWindow = newWindowBuilder("appWindow", TYPE_APPLICATION).build();
+        final WindowState imeWindow = newWindowBuilder("imeWindow", TYPE_INPUT_METHOD).build();
 
         // Setting FLAG_NOT_FOCUSABLE prevents the window from being an IME target.
         appWindow.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
@@ -328,16 +339,17 @@
 
     @Test
     public void testGetWindow() {
-        final WindowState root = createWindow(null, TYPE_APPLICATION, "root");
-        final WindowState mediaChild = createWindow(root, TYPE_APPLICATION_MEDIA, "mediaChild");
-        final WindowState mediaOverlayChild = createWindow(root,
-                TYPE_APPLICATION_MEDIA_OVERLAY, "mediaOverlayChild");
-        final WindowState attachedDialogChild = createWindow(root,
-                TYPE_APPLICATION_ATTACHED_DIALOG, "attachedDialogChild");
-        final WindowState subPanelChild = createWindow(root,
-                TYPE_APPLICATION_SUB_PANEL, "subPanelChild");
-        final WindowState aboveSubPanelChild = createWindow(root,
-                TYPE_APPLICATION_ABOVE_SUB_PANEL, "aboveSubPanelChild");
+        final WindowState root = newWindowBuilder("root", TYPE_APPLICATION).build();
+        final WindowState mediaChild = newWindowBuilder("mediaChild",
+                TYPE_APPLICATION_MEDIA).setParent(root).build();
+        final WindowState mediaOverlayChild = newWindowBuilder("mediaOverlayChild",
+                TYPE_APPLICATION_MEDIA_OVERLAY).setParent(root).build();
+        final WindowState attachedDialogChild = newWindowBuilder("attachedDialogChild",
+                TYPE_APPLICATION_ATTACHED_DIALOG).setParent(root).build();
+        final WindowState subPanelChild = newWindowBuilder("subPanelChild",
+                TYPE_APPLICATION_SUB_PANEL).setParent(root).build();
+        final WindowState aboveSubPanelChild = newWindowBuilder("aboveSubPanelChild",
+                TYPE_APPLICATION_ABOVE_SUB_PANEL).setParent(root).build();
 
         final LinkedList<WindowState> windows = new LinkedList<>();
 
@@ -358,7 +370,7 @@
 
     @Test
     public void testDestroySurface() {
-        final WindowState win = createWindow(null, TYPE_APPLICATION, "win");
+        final WindowState win = newWindowBuilder("win", TYPE_APPLICATION).build();
         win.mHasSurface = win.mAnimatingExit = true;
         win.mWinAnimator.mSurfaceControl = mock(SurfaceControl.class);
         win.onExitAnimationDone();
@@ -384,8 +396,10 @@
         // Call prepareWindowToDisplayDuringRelayout for a window without FLAG_TURN_SCREEN_ON before
         // calling setCurrentLaunchCanTurnScreenOn for windows with flag in the same activity.
         final ActivityRecord activity = createActivityRecord(mDisplayContent);
-        final WindowState first = createWindow(null, TYPE_APPLICATION, activity, "first");
-        final WindowState second = createWindow(null, TYPE_APPLICATION, activity, "second");
+        final WindowState first = newWindowBuilder("first", TYPE_APPLICATION).setWindowToken(
+                activity).build();
+        final WindowState second = newWindowBuilder("second", TYPE_APPLICATION).setWindowToken(
+                activity).build();
 
         testPrepareWindowToDisplayDuringRelayout(first, false /* expectedWakeupCalled */,
                 true /* expectedCurrentLaunchCanTurnScreenOn */);
@@ -423,10 +437,10 @@
         // Call prepareWindowToDisplayDuringRelayout for a windows that are not children of an
         // activity. Both windows have the FLAG_TURNS_SCREEN_ON so both should call wakeup
         final WindowToken windowToken = createTestWindowToken(FIRST_SUB_WINDOW, mDisplayContent);
-        final WindowState firstWindow = createWindow(null, TYPE_APPLICATION, windowToken,
-                "firstWindow");
-        final WindowState secondWindow = createWindow(null, TYPE_APPLICATION, windowToken,
-                "secondWindow");
+        final WindowState firstWindow = newWindowBuilder("firstWindow",
+                TYPE_APPLICATION).setWindowToken(windowToken).build();
+        final WindowState secondWindow = newWindowBuilder("secondWindow",
+                TYPE_APPLICATION).setWindowToken(windowToken).build();
         firstWindow.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
         secondWindow.mAttrs.flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
 
@@ -459,7 +473,7 @@
 
     @Test
     public void testCanAffectSystemUiFlags() {
-        final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+        final WindowState app = newWindowBuilder("app", TYPE_APPLICATION).build();
         app.mActivityRecord.setVisible(true);
         assertTrue(app.canAffectSystemUiFlags());
         app.mActivityRecord.setVisible(false);
@@ -471,7 +485,7 @@
 
     @Test
     public void testCanAffectSystemUiFlags_starting() {
-        final WindowState app = createWindow(null, TYPE_APPLICATION_STARTING, "app");
+        final WindowState app = newWindowBuilder("app", TYPE_APPLICATION_STARTING).build();
         app.mActivityRecord.setVisible(true);
         app.mStartingData = new SnapshotStartingData(mWm, null, 0);
         assertFalse(app.canAffectSystemUiFlags());
@@ -481,7 +495,7 @@
 
     @Test
     public void testCanAffectSystemUiFlags_disallow() {
-        final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+        final WindowState app = newWindowBuilder("app", TYPE_APPLICATION).build();
         app.mActivityRecord.setVisible(true);
         assertTrue(app.canAffectSystemUiFlags());
         app.getTask().setCanAffectSystemUiFlags(false);
@@ -538,9 +552,11 @@
 
     @Test
     public void testIsSelfOrAncestorWindowAnimating() {
-        final WindowState root = createWindow(null, TYPE_APPLICATION, "root");
-        final WindowState child1 = createWindow(root, FIRST_SUB_WINDOW, "child1");
-        final WindowState child2 = createWindow(child1, FIRST_SUB_WINDOW, "child2");
+        final WindowState root = newWindowBuilder("root", TYPE_APPLICATION).build();
+        final WindowState child1 = newWindowBuilder("child1", FIRST_SUB_WINDOW).setParent(
+                root).build();
+        final WindowState child2 = newWindowBuilder("child2", FIRST_SUB_WINDOW).setParent(
+                child1).build();
         assertFalse(child2.isSelfOrAncestorWindowAnimatingExit());
         child2.mAnimatingExit = true;
         assertTrue(child2.isSelfOrAncestorWindowAnimatingExit());
@@ -551,7 +567,7 @@
 
     @Test
     public void testDeferredRemovalByAnimating() {
-        final WindowState appWindow = createWindow(null, TYPE_APPLICATION, "appWindow");
+        final WindowState appWindow = newWindowBuilder("appWindow", TYPE_APPLICATION).build();
         makeWindowVisible(appWindow);
         spyOn(appWindow.mWinAnimator);
         doReturn(true).when(appWindow.mWinAnimator).getShown();
@@ -571,8 +587,9 @@
 
     @Test
     public void testOnExitAnimationDone() {
-        final WindowState parent = createWindow(null, TYPE_APPLICATION, "parent");
-        final WindowState child = createWindow(parent, TYPE_APPLICATION_PANEL, "child");
+        final WindowState parent = newWindowBuilder("parent", TYPE_APPLICATION).build();
+        final WindowState child = newWindowBuilder("child", TYPE_APPLICATION_PANEL).setParent(
+                parent).build();
         final SurfaceControl.Transaction t = parent.getPendingTransaction();
         child.startAnimation(t, mock(AnimationAdapter.class), false /* hidden */,
                 SurfaceAnimator.ANIMATION_TYPE_WINDOW_ANIMATION);
@@ -609,7 +626,7 @@
 
     @Test
     public void testLayoutSeqResetOnReparent() {
-        final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+        final WindowState app = newWindowBuilder("app", TYPE_APPLICATION).build();
         app.mLayoutSeq = 1;
         mDisplayContent.mLayoutSeq = 1;
 
@@ -622,7 +639,7 @@
 
     @Test
     public void testDisplayIdUpdatedOnReparent() {
-        final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+        final WindowState app = newWindowBuilder("app", TYPE_APPLICATION).build();
         // fake a different display
         app.mInputWindowHandle.setDisplayId(mDisplayContent.getDisplayId() + 1);
         app.onDisplayChanged(mDisplayContent);
@@ -633,7 +650,7 @@
 
     @Test
     public void testApplyWithNextDraw() {
-        final WindowState win = createWindow(null, TYPE_APPLICATION_OVERLAY, "app");
+        final WindowState win = newWindowBuilder("app", TYPE_APPLICATION_OVERLAY).build();
         final SurfaceControl.Transaction[] handledT = { null };
         // The normal case that the draw transaction is applied with finishing drawing.
         win.applyWithNextDraw(t -> handledT[0] = t);
@@ -657,7 +674,7 @@
 
     @Test
     public void testSeamlesslyRotateWindow() {
-        final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+        final WindowState app = newWindowBuilder("app", TYPE_APPLICATION).build();
         final SurfaceControl.Transaction t = spy(StubTransaction.class);
 
         makeWindowVisible(app);
@@ -707,7 +724,7 @@
 
     @Test
     public void testVisibilityChangeSwitchUser() {
-        final WindowState window = createWindow(null, TYPE_APPLICATION, "app");
+        final WindowState window = newWindowBuilder("app", TYPE_APPLICATION).build();
         window.mHasSurface = true;
         spyOn(window);
         doReturn(false).when(window).showForAllUsers();
@@ -729,8 +746,9 @@
         final CompatModePackages cmp = mWm.mAtmService.mCompatModePackages;
         spyOn(cmp);
         doReturn(overrideScale).when(cmp).getCompatScale(anyString(), anyInt());
-        final WindowState w = createWindow(null, TYPE_APPLICATION_OVERLAY, "win");
-        final WindowState child = createWindow(w, TYPE_APPLICATION_PANEL, "child");
+        final WindowState w = newWindowBuilder("win", TYPE_APPLICATION_OVERLAY).build();
+        final WindowState child = newWindowBuilder("child", TYPE_APPLICATION_PANEL).setParent(
+                w).build();
 
         assertTrue(w.hasCompatScale());
         assertTrue(child.hasCompatScale());
@@ -788,7 +806,8 @@
 
         // Child window without scale (e.g. different app) should apply inverse scale of parent.
         doReturn(1f).when(cmp).getCompatScale(anyString(), anyInt());
-        final WindowState child2 = createWindow(w, TYPE_APPLICATION_SUB_PANEL, "child2");
+        final WindowState child2 = newWindowBuilder("child2", TYPE_APPLICATION_SUB_PANEL).setParent(
+                w).build();
         makeWindowVisible(w, child2);
         clearInvocations(t);
         child2.prepareSurfaces();
@@ -798,10 +817,10 @@
     @SetupWindows(addWindows = { W_ABOVE_ACTIVITY, W_NOTIFICATION_SHADE })
     @Test
     public void testRequestDrawIfNeeded() {
-        final WindowState startingApp = createWindow(null /* parent */,
-                TYPE_BASE_APPLICATION, "startingApp");
-        final WindowState startingWindow = createWindow(null /* parent */,
-                TYPE_APPLICATION_STARTING, startingApp.mToken, "starting");
+        final WindowState startingApp = newWindowBuilder("startingApp",
+                TYPE_BASE_APPLICATION).build();
+        final WindowState startingWindow = newWindowBuilder("starting",
+                TYPE_APPLICATION_STARTING).setWindowToken(startingApp.mToken).build();
         startingApp.mActivityRecord.mStartingWindow = startingWindow;
         final WindowState keyguardHostWindow = mNotificationShadeWindow;
         final WindowState allDrawnApp = mAppWindow;
@@ -878,7 +897,7 @@
 
     @Test
     public void testRequestResizeForBlastSync() {
-        final WindowState win = createWindow(null, TYPE_APPLICATION, "window");
+        final WindowState win = newWindowBuilder("window", TYPE_APPLICATION).build();
         makeWindowVisible(win);
         makeLastConfigReportedToClient(win, true /* visible */);
         win.mLayoutSeq = win.getDisplayContent().mLayoutSeq;
@@ -926,8 +945,8 @@
         final Task task = createTask(mDisplayContent);
         final TaskFragment embeddedTf = createTaskFragmentWithEmbeddedActivity(task, organizer);
         final ActivityRecord embeddedActivity = embeddedTf.getTopMostActivity();
-        final WindowState win = createWindow(null /* parent */, TYPE_APPLICATION, embeddedActivity,
-                "App window");
+        final WindowState win = newWindowBuilder("App window", TYPE_APPLICATION).setWindowToken(
+                embeddedActivity).build();
         doReturn(true).when(embeddedActivity).isVisible();
         embeddedActivity.setVisibleRequested(true);
         makeWindowVisible(win);
@@ -949,14 +968,14 @@
 
     @Test
     public void testCantReceiveTouchWhenAppTokenHiddenRequested() {
-        final WindowState win0 = createWindow(null, TYPE_APPLICATION, "win0");
+        final WindowState win0 = newWindowBuilder("win0", TYPE_APPLICATION).build();
         win0.mActivityRecord.setVisibleRequested(false);
         assertFalse(win0.canReceiveTouchInput());
     }
 
     @Test
     public void testCantReceiveTouchWhenNotFocusable() {
-        final WindowState win0 = createWindow(null, TYPE_APPLICATION, "win0");
+        final WindowState win0 = newWindowBuilder("win0", TYPE_APPLICATION).build();
         final Task rootTask = win0.mActivityRecord.getRootTask();
         spyOn(rootTask);
         when(rootTask.shouldIgnoreInput()).thenReturn(true);
@@ -969,7 +988,7 @@
 
     @Test
     public void testUpdateInputWindowHandle() {
-        final WindowState win = createWindow(null, TYPE_APPLICATION, "win");
+        final WindowState win = newWindowBuilder("win", TYPE_APPLICATION).build();
         win.mAttrs.inputFeatures = WindowManager.LayoutParams.INPUT_FEATURE_DISABLE_USER_ACTIVITY;
         win.mAttrs.flags = FLAG_WATCH_OUTSIDE_TOUCH | FLAG_SPLIT_TOUCH;
         final InputWindowHandle handle = new InputWindowHandle(
@@ -982,7 +1001,6 @@
 
         assertTrue(handleWrapper.isChanged());
         assertTrue(testFlag(handle.inputConfig, InputConfig.WATCH_OUTSIDE_TOUCH));
-        assertFalse(testFlag(handle.inputConfig, InputConfig.PREVENT_SPLITTING));
         assertTrue(testFlag(handle.inputConfig, InputConfig.DISABLE_USER_ACTIVITY));
         // The window of standard resizable task should not use surface crop as touchable region.
         assertFalse(handle.replaceTouchableRegionWithCrop);
@@ -1026,7 +1044,7 @@
     @DisableFlags(Flags.FLAG_SCROLLING_FROM_LETTERBOX)
     @Test
     public void testTouchRegionUsesLetterboxBoundsIfTransformedBoundsAndLetterboxScrolling() {
-        final WindowState win = createWindow(null, TYPE_APPLICATION, "win");
+        final WindowState win = newWindowBuilder("win", TYPE_APPLICATION).build();
 
         // Transformed bounds used for size of touchable region if letterbox inner bounds are empty.
         final Rect transformedBounds = new Rect(0, 0, 300, 500);
@@ -1051,7 +1069,7 @@
     @DisableFlags(Flags.FLAG_SCROLLING_FROM_LETTERBOX)
     @Test
     public void testTouchRegionUsesLetterboxBoundsIfNullTransformedBoundsAndLetterboxScrolling() {
-        final WindowState win = createWindow(null, TYPE_APPLICATION, "win");
+        final WindowState win = newWindowBuilder("win", TYPE_APPLICATION).build();
 
         // Fragment bounds used for size of touchable region if letterbox inner bounds are empty
         // and Transform bounds are null.
@@ -1083,7 +1101,7 @@
     @EnableFlags(Flags.FLAG_SCROLLING_FROM_LETTERBOX)
     @Test
     public void testTouchRegionUsesTransformedBoundsIfLetterboxScrolling() {
-        final WindowState win = createWindow(null, TYPE_APPLICATION, "win");
+        final WindowState win = newWindowBuilder("win", TYPE_APPLICATION).build();
 
         // Transformed bounds used for size of touchable region if letterbox inner bounds are empty.
         final Rect transformedBounds = new Rect(0, 0, 300, 500);
@@ -1109,7 +1127,7 @@
     public void testHasActiveVisibleWindow() {
         final int uid = ActivityBuilder.DEFAULT_FAKE_UID;
 
-        final WindowState app = createWindow(null, TYPE_APPLICATION, "app", uid);
+        final WindowState app = newWindowBuilder("app", TYPE_APPLICATION).setOwnerId(uid).build();
         app.mActivityRecord.setVisible(false);
         app.mActivityRecord.setVisibility(false);
         assertFalse(mAtm.hasActiveVisibleWindow(uid));
@@ -1120,15 +1138,17 @@
         // Make the activity invisible and add a visible toast. The uid should have no active
         // visible window because toast can be misused by legacy app to bypass background check.
         app.mActivityRecord.setVisibility(false);
-        final WindowState overlay = createWindow(null, TYPE_APPLICATION_OVERLAY, "overlay", uid);
-        final WindowState toast = createWindow(null, TYPE_TOAST, app.mToken, "toast", uid);
+        final WindowState overlay = newWindowBuilder("overlay",
+                TYPE_APPLICATION_OVERLAY).setOwnerId(uid).build();
+        final WindowState toast = newWindowBuilder("toast", TYPE_TOAST).setWindowToken(
+                app.mToken).setOwnerId(uid).build();
         toast.onSurfaceShownChanged(true);
         assertFalse(mAtm.hasActiveVisibleWindow(uid));
 
         // Though starting window should belong to system. Make sure it is ignored to avoid being
         // allow-list unexpectedly, see b/129563343.
-        final WindowState starting =
-                createWindow(null, TYPE_APPLICATION_STARTING, app.mToken, "starting", uid);
+        final WindowState starting = newWindowBuilder("starting",
+                TYPE_APPLICATION_STARTING).setWindowToken(app.mToken).setOwnerId(uid).build();
         starting.onSurfaceShownChanged(true);
         assertFalse(mAtm.hasActiveVisibleWindow(uid));
 
@@ -1145,8 +1165,8 @@
     @SetupWindows(addWindows = { W_ACTIVITY, W_INPUT_METHOD })
     @Test
     public void testNeedsRelativeLayeringToIme_notAttached() {
-        WindowState sameTokenWindow = createWindow(null, TYPE_BASE_APPLICATION, mAppWindow.mToken,
-                "SameTokenWindow");
+        WindowState sameTokenWindow = newWindowBuilder("SameTokenWindow",
+                TYPE_BASE_APPLICATION).setWindowToken(mAppWindow.mToken).build();
         mDisplayContent.setImeLayeringTarget(mAppWindow);
         makeWindowVisible(mImeWindow);
         sameTokenWindow.mActivityRecord.getRootTask().setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
@@ -1158,8 +1178,8 @@
     @SetupWindows(addWindows = { W_ACTIVITY, W_INPUT_METHOD })
     @Test
     public void testNeedsRelativeLayeringToIme_startingWindow() {
-        WindowState sameTokenWindow = createWindow(null, TYPE_APPLICATION_STARTING,
-                mAppWindow.mToken, "SameTokenWindow");
+        WindowState sameTokenWindow = newWindowBuilder("SameTokenWindow",
+                TYPE_APPLICATION_STARTING).setWindowToken(mAppWindow.mToken).build();
         mDisplayContent.setImeLayeringTarget(mAppWindow);
         makeWindowVisible(mImeWindow);
         sameTokenWindow.mActivityRecord.getRootTask().setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
@@ -1169,9 +1189,9 @@
     @UseTestDisplay(addWindows = {W_ACTIVITY, W_INPUT_METHOD})
     @Test
     public void testNeedsRelativeLayeringToIme_systemDialog() {
-        WindowState systemDialogWindow = createWindow(null, TYPE_SECURE_SYSTEM_OVERLAY,
-                mDisplayContent,
-                "SystemDialog", true);
+        WindowState systemDialogWindow = newWindowBuilder("SystemDialog",
+                TYPE_SECURE_SYSTEM_OVERLAY).setDisplay(
+                mDisplayContent).setOwnerCanAddInternalSystemWindow(true).build();
         mDisplayContent.setImeLayeringTarget(mAppWindow);
         mAppWindow.getTask().setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
         makeWindowVisible(mImeWindow);
@@ -1182,20 +1202,21 @@
     @UseTestDisplay(addWindows = {W_INPUT_METHOD})
     @Test
     public void testNeedsRelativeLayeringToIme_notificationShadeShouldNotHideSystemDialog() {
-        WindowState systemDialogWindow = createWindow(null, TYPE_SECURE_SYSTEM_OVERLAY,
-                mDisplayContent,
-                "SystemDialog", true);
+        WindowState systemDialogWindow = newWindowBuilder("SystemDialog",
+                TYPE_SECURE_SYSTEM_OVERLAY).setDisplay(
+                mDisplayContent).setOwnerCanAddInternalSystemWindow(true).build();
         mDisplayContent.setImeLayeringTarget(systemDialogWindow);
         makeWindowVisible(mImeWindow);
-        WindowState notificationShade = createWindow(null, TYPE_NOTIFICATION_SHADE,
-                mDisplayContent, "NotificationShade", true);
+        WindowState notificationShade = newWindowBuilder("NotificationShade",
+                TYPE_NOTIFICATION_SHADE).setDisplay(
+                mDisplayContent).setOwnerCanAddInternalSystemWindow(true).build();
         notificationShade.mAttrs.flags |= FLAG_ALT_FOCUSABLE_IM;
         assertFalse(notificationShade.needsRelativeLayeringToIme());
     }
 
     @Test
     public void testSetFreezeInsetsState() {
-        final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+        final WindowState app = newWindowBuilder("app", TYPE_APPLICATION).build();
         spyOn(app);
         doReturn(true).when(app).isVisible();
 
@@ -1216,7 +1237,7 @@
         verify(app).notifyInsetsChanged();
 
         // Verify that invisible non-activity window won't dispatch insets changed.
-        final WindowState overlay = createWindow(null, TYPE_APPLICATION_OVERLAY, "overlay");
+        final WindowState overlay = newWindowBuilder("overlay", TYPE_APPLICATION_OVERLAY).build();
         makeWindowVisible(overlay);
         assertTrue(overlay.isReadyToDispatchInsetsState());
         overlay.mHasSurface = false;
@@ -1244,9 +1265,9 @@
 
     @Test
     public void testAdjustImeInsetsVisibilityWhenSwitchingApps() {
-        final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
-        final WindowState app2 = createWindow(null, TYPE_APPLICATION, "app2");
-        final WindowState imeWindow = createWindow(null, TYPE_APPLICATION, "imeWindow");
+        final WindowState app = newWindowBuilder("app", TYPE_APPLICATION).build();
+        final WindowState app2 = newWindowBuilder("app2", TYPE_APPLICATION).build();
+        final WindowState imeWindow = newWindowBuilder("imeWindow", TYPE_APPLICATION).build();
         spyOn(imeWindow);
         doReturn(true).when(imeWindow).isVisible();
         mDisplayContent.mInputMethodWindow = imeWindow;
@@ -1279,10 +1300,11 @@
 
     @Test
     public void testAdjustImeInsetsVisibilityWhenSwitchingApps_toAppInMultiWindowMode() {
-        final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
-        final WindowState app2 = createWindow(null, WINDOWING_MODE_MULTI_WINDOW,
-                ACTIVITY_TYPE_STANDARD, TYPE_APPLICATION, mDisplayContent, "app2");
-        final WindowState imeWindow = createWindow(null, TYPE_APPLICATION, "imeWindow");
+        final WindowState app = newWindowBuilder("app", TYPE_APPLICATION).build();
+        final WindowState app2 = newWindowBuilder("app2", TYPE_APPLICATION).setWindowingMode(
+                WINDOWING_MODE_MULTI_WINDOW).setActivityType(ACTIVITY_TYPE_STANDARD).setDisplay(
+                mDisplayContent).build();
+        final WindowState imeWindow = newWindowBuilder("imeWindow", TYPE_APPLICATION).build();
         spyOn(imeWindow);
         doReturn(true).when(imeWindow).isVisible();
         mDisplayContent.mInputMethodWindow = imeWindow;
@@ -1321,8 +1343,8 @@
     @SetupWindows(addWindows = W_ACTIVITY)
     @Test
     public void testUpdateImeControlTargetWhenLeavingMultiWindow() {
-        WindowState app = createWindow(null, TYPE_BASE_APPLICATION,
-                mAppWindow.mToken, "app");
+        WindowState app = newWindowBuilder("app", TYPE_BASE_APPLICATION).setWindowToken(
+                mAppWindow.mToken).build();
         mDisplayContent.setRemoteInsetsController(createDisplayWindowInsetsController());
 
         spyOn(app);
@@ -1349,8 +1371,8 @@
     @SetupWindows(addWindows = { W_ACTIVITY, W_INPUT_METHOD, W_NOTIFICATION_SHADE })
     @Test
     public void testNotificationShadeHasImeInsetsWhenMultiWindow() {
-        WindowState app = createWindow(null, TYPE_BASE_APPLICATION,
-                mAppWindow.mToken, "app");
+        WindowState app = newWindowBuilder("app", TYPE_BASE_APPLICATION).setWindowToken(
+                mAppWindow.mToken).build();
 
         // Simulate entering multi-window mode and windowing mode is multi-window.
         app.mActivityRecord.getRootTask().setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
@@ -1376,7 +1398,7 @@
 
     @Test
     public void testRequestedVisibility() {
-        final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+        final WindowState app = newWindowBuilder("app", TYPE_APPLICATION).build();
         app.mActivityRecord.setVisible(false);
         app.mActivityRecord.setVisibility(false);
         assertFalse(app.isVisibleRequested());
@@ -1391,7 +1413,7 @@
 
     @Test
     public void testKeepClearAreas() {
-        final WindowState window = createWindow(null, TYPE_APPLICATION, "window");
+        final WindowState window = newWindowBuilder("window", TYPE_APPLICATION).build();
         makeWindowVisible(window);
 
         final Rect keepClearArea1 = new Rect(0, 0, 10, 10);
@@ -1433,7 +1455,7 @@
 
     @Test
     public void testUnrestrictedKeepClearAreas() {
-        final WindowState window = createWindow(null, TYPE_APPLICATION, "window");
+        final WindowState window = newWindowBuilder("window", TYPE_APPLICATION).build();
         makeWindowVisible(window);
 
         final Rect keepClearArea1 = new Rect(0, 0, 10, 10);
@@ -1481,8 +1503,9 @@
         final InputMethodManagerInternal immi = InputMethodManagerInternal.get();
         spyOn(immi);
 
-        final WindowState imeTarget = createWindow(null /* parent */, TYPE_BASE_APPLICATION,
-                createActivityRecord(mDisplayContent), "imeTarget");
+        final WindowState imeTarget = newWindowBuilder("imeTarget",
+                TYPE_BASE_APPLICATION).setWindowToken(
+                createActivityRecord(mDisplayContent)).build();
 
         imeTarget.mActivityRecord.setVisibleRequested(true);
         makeWindowVisible(imeTarget);
@@ -1562,8 +1585,8 @@
 
     @Test
     public void testIsSecureLocked_flagSecureSet() {
-        WindowState window = createWindow(null /* parent */, TYPE_APPLICATION, "test-window",
-                1 /* ownerId */);
+        WindowState window = newWindowBuilder("test-window", TYPE_APPLICATION).setOwnerId(
+                1).build();
         window.mAttrs.flags |= WindowManager.LayoutParams.FLAG_SECURE;
 
         assertTrue(window.isSecureLocked());
@@ -1571,8 +1594,8 @@
 
     @Test
     public void testIsSecureLocked_flagSecureNotSet() {
-        WindowState window = createWindow(null /* parent */, TYPE_APPLICATION, "test-window",
-                1 /* ownerId */);
+        WindowState window = newWindowBuilder("test-window", TYPE_APPLICATION).setOwnerId(
+                1).build();
 
         assertFalse(window.isSecureLocked());
     }
@@ -1581,8 +1604,8 @@
     public void testIsSecureLocked_disableSecureWindows() {
         assumeTrue(Build.IS_DEBUGGABLE);
 
-        WindowState window = createWindow(null /* parent */, TYPE_APPLICATION, "test-window",
-                1 /* ownerId */);
+        WindowState window = newWindowBuilder("test-window", TYPE_APPLICATION).setOwnerId(
+                1).build();
         window.mAttrs.flags |= WindowManager.LayoutParams.FLAG_SECURE;
         ContentResolver cr = useFakeSettingsProvider();
 
@@ -1617,8 +1640,10 @@
         String testPackage = "test";
         int ownerId1 = 20;
         int ownerId2 = 21;
-        final WindowState window1 = createWindow(null, TYPE_APPLICATION, "window1", ownerId1);
-        final WindowState window2 = createWindow(null, TYPE_APPLICATION, "window2", ownerId2);
+        final WindowState window1 = newWindowBuilder("window1", TYPE_APPLICATION).setOwnerId(
+                ownerId1).build();
+        final WindowState window2 = newWindowBuilder("window2", TYPE_APPLICATION).setOwnerId(
+                ownerId2).build();
 
         // Setting packagename for targeted feature
         window1.mAttrs.packageName = testPackage;
@@ -1638,7 +1663,8 @@
     public void testIsSecureLocked_sensitiveContentBlockOrClearScreenCaptureForApp() {
         String testPackage = "test";
         int ownerId = 20;
-        final WindowState window = createWindow(null, TYPE_APPLICATION, "window", ownerId);
+        final WindowState window = newWindowBuilder("window", TYPE_APPLICATION).setOwnerId(
+                ownerId).build();
         window.mAttrs.packageName = testPackage;
         assertFalse(window.isSecureLocked());
 
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 ce0d912..37d2a75 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -478,7 +478,7 @@
     }
 
     private WindowState createCommonWindow(WindowState parent, int type, String name) {
-        final WindowState win = createWindow(parent, type, name);
+        final WindowState win = newWindowBuilder(name, type).setParent(parent).build();
         // Prevent common windows from been IME targets.
         win.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
         return win;
@@ -502,7 +502,8 @@
     }
 
     WindowState createNavBarWithProvidedInsets(DisplayContent dc) {
-        final WindowState navbar = createWindow(null, TYPE_NAVIGATION_BAR, dc, "navbar");
+        final WindowState navbar = newWindowBuilder("navbar", TYPE_NAVIGATION_BAR).setDisplay(
+                dc).build();
         final Binder owner = new Binder();
         navbar.mAttrs.providedInsets = new InsetsFrameProvider[] {
                 new InsetsFrameProvider(owner, 0, WindowInsets.Type.navigationBars())
@@ -513,7 +514,8 @@
     }
 
     WindowState createStatusBarWithProvidedInsets(DisplayContent dc) {
-        final WindowState statusBar = createWindow(null, TYPE_STATUS_BAR, dc, "statusBar");
+        final WindowState statusBar = newWindowBuilder("statusBar", TYPE_STATUS_BAR).setDisplay(
+                dc).build();
         final Binder owner = new Binder();
         statusBar.mAttrs.providedInsets = new InsetsFrameProvider[] {
                 new InsetsFrameProvider(owner, 0, WindowInsets.Type.statusBars())
@@ -575,92 +577,13 @@
     WindowState createAppWindow(Task task, int type, String name) {
         final ActivityRecord activity = createNonAttachedActivityRecord(task.getDisplayContent());
         task.addChild(activity, 0);
-        return createWindow(null, type, activity, name);
+        return newWindowBuilder(name, type).setWindowToken(activity).build();
     }
 
-    WindowState createDreamWindow(WindowState parent, int type, String name) {
+    WindowState createDreamWindow(String name, int type) {
         final WindowToken token = createWindowToken(
                 mDisplayContent, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_DREAM, type);
-        return createWindow(parent, type, token, name);
-    }
-
-    // TODO: Move these calls to a builder?
-    WindowState createWindow(WindowState parent, int type, String name) {
-        return (parent == null)
-                ? createWindow(parent, type, mDisplayContent, name)
-                : createWindow(parent, type, parent.mToken, name);
-    }
-
-    WindowState createWindow(WindowState parent, int type, String name, int ownerId) {
-        return (parent == null)
-                ? createWindow(parent, type, mDisplayContent, name, ownerId)
-                : createWindow(parent, type, parent.mToken, name, ownerId);
-    }
-
-    WindowState createWindow(WindowState parent, int windowingMode, int activityType,
-            int type, DisplayContent dc, String name) {
-        final WindowToken token = createWindowToken(dc, windowingMode, activityType, type);
-        return createWindow(parent, type, token, name);
-    }
-
-    WindowState createWindow(WindowState parent, int type, DisplayContent dc, String name) {
-        return createWindow(
-                parent, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, type, dc, name);
-    }
-
-    WindowState createWindow(WindowState parent, int type, DisplayContent dc, String name,
-            int ownerId) {
-        final WindowToken token = createWindowToken(
-                dc, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, type);
-        return createWindow(parent, type, token, name, ownerId);
-    }
-
-    WindowState createWindow(WindowState parent, int type, DisplayContent dc, String name,
-            boolean ownerCanAddInternalSystemWindow) {
-        final WindowToken token = createWindowToken(
-                dc, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, type);
-        return createWindow(parent, type, token, name, 0 /* ownerId */,
-                ownerCanAddInternalSystemWindow);
-    }
-
-    WindowState createWindow(WindowState parent, int type, WindowToken token, String name) {
-        return createWindow(parent, type, token, name, 0 /* ownerId */,
-                false /* ownerCanAddInternalSystemWindow */);
-    }
-
-    WindowState createWindow(WindowState parent, int type, WindowToken token, String name,
-            int ownerId) {
-        return createWindow(parent, type, token, name, ownerId,
-                false /* ownerCanAddInternalSystemWindow */);
-    }
-
-    WindowState createWindow(WindowState parent, int type, WindowToken token, String name,
-            int ownerId, boolean ownerCanAddInternalSystemWindow) {
-        return createWindow(parent, type, token, name, ownerId, ownerCanAddInternalSystemWindow,
-                mIWindow);
-    }
-
-    WindowState createWindow(WindowState parent, int type, WindowToken token, String name,
-            int ownerId, boolean ownerCanAddInternalSystemWindow, IWindow iwindow) {
-        return createWindow(parent, type, token, name, ownerId, UserHandle.getUserId(ownerId),
-                ownerCanAddInternalSystemWindow, mWm, getTestSession(token), iwindow);
-    }
-
-    static WindowState createWindow(WindowState parent, int type, WindowToken token,
-            String name, int ownerId, int userId, boolean ownerCanAddInternalSystemWindow,
-            WindowManagerService service, Session session, IWindow iWindow) {
-        SystemServicesTestRule.checkHoldsLock(service.mGlobalLock);
-
-        final WindowManager.LayoutParams attrs = new WindowManager.LayoutParams(type);
-        attrs.setTitle(name);
-        attrs.packageName = "test";
-
-        final WindowState w = new WindowState(service, session, iWindow, token, parent,
-                OP_NONE, attrs, VISIBLE, ownerId, userId, ownerCanAddInternalSystemWindow);
-        // TODO: Probably better to make this call in the WindowState ctor to avoid errors with
-        // adding it to the token...
-        token.addWindow(w);
-        return w;
+        return newWindowBuilder(name, type).setWindowToken(token).build();
     }
 
     static void makeWindowVisible(WindowState... windows) {
@@ -1920,11 +1843,14 @@
         private final WindowManagerService mWMService;
         private final SparseArray<IBinder> mTaskAppMap = new SparseArray<>();
         private final HashMap<IBinder, WindowState> mAppWindowMap = new HashMap<>();
+        private final DisplayContent mDisplayContent;
 
-        TestStartingWindowOrganizer(ActivityTaskManagerService service) {
+        TestStartingWindowOrganizer(ActivityTaskManagerService service,
+                DisplayContent displayContent) {
             mAtm = service;
             mWMService = mAtm.mWindowManager;
             mAtm.mTaskOrganizerController.registerTaskOrganizer(this);
+            mDisplayContent = displayContent;
         }
 
         @Override
@@ -1933,10 +1859,11 @@
                 final ActivityRecord activity = ActivityRecord.forTokenLocked(info.appToken);
                 IWindow iWindow = mock(IWindow.class);
                 doReturn(mock(IBinder.class)).when(iWindow).asBinder();
-                final WindowState window = WindowTestsBase.createWindow(null,
-                        TYPE_APPLICATION_STARTING, activity,
-                        "Starting window", 0 /* ownerId */, 0 /* userId*/,
-                        false /* internalWindows */, mWMService, createTestSession(mAtm), iWindow);
+                // WindowToken is already passed, windowTokenCreator is not needed here.
+                final WindowState window = new WindowTestsBase.WindowStateBuilder("Starting window",
+                        TYPE_APPLICATION_STARTING, mWMService, mDisplayContent, iWindow,
+                        (unused) -> createTestSession(mAtm),
+                        null /* windowTokenCreator */).setWindowToken(activity).build();
                 activity.mStartingWindow = window;
                 mAppWindowMap.put(info.appToken, window);
                 mTaskAppMap.put(info.taskInfo.taskId, info.appToken);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
index f226b9d..a02c3db 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
@@ -74,11 +74,16 @@
 
         assertEquals(0, token.getWindowsCount());
 
-        final WindowState window1 = createWindow(null, TYPE_APPLICATION, token, "window1");
-        final WindowState window11 = createWindow(window1, FIRST_SUB_WINDOW, token, "window11");
-        final WindowState window12 = createWindow(window1, FIRST_SUB_WINDOW, token, "window12");
-        final WindowState window2 = createWindow(null, TYPE_APPLICATION, token, "window2");
-        final WindowState window3 = createWindow(null, TYPE_APPLICATION, token, "window3");
+        final WindowState window1 = newWindowBuilder("window1", TYPE_APPLICATION).setWindowToken(
+                token).build();
+        final WindowState window11 = newWindowBuilder("window11", FIRST_SUB_WINDOW).setParent(
+                window1).setWindowToken(token).build();
+        final WindowState window12 = newWindowBuilder("window12", FIRST_SUB_WINDOW).setParent(
+                window1).setWindowToken(token).build();
+        final WindowState window2 = newWindowBuilder("window2", TYPE_APPLICATION).setWindowToken(
+                token).build();
+        final WindowState window3 = newWindowBuilder("window3", TYPE_APPLICATION).setWindowToken(
+                token).build();
 
         token.addWindow(window1);
         // NOTE: Child windows will not be added to the token as window containers can only
@@ -105,8 +110,10 @@
     public void testAddWindow_assignsLayers() {
         final TestWindowToken token1 = createTestWindowToken(0, mDisplayContent);
         final TestWindowToken token2 = createTestWindowToken(0, mDisplayContent);
-        final WindowState window1 = createWindow(null, TYPE_STATUS_BAR, token1, "window1");
-        final WindowState window2 = createWindow(null, TYPE_STATUS_BAR, token2, "window2");
+        final WindowState window1 = newWindowBuilder("window1", TYPE_STATUS_BAR).setWindowToken(
+                token1).build();
+        final WindowState window2 = newWindowBuilder("window2", TYPE_STATUS_BAR).setWindowToken(
+                token2).build();
 
         token1.addWindow(window1);
         token2.addWindow(window2);
@@ -122,8 +129,10 @@
 
         assertEquals(token, dc.getWindowToken(token.token));
 
-        final WindowState window1 = createWindow(null, TYPE_APPLICATION, token, "window1");
-        final WindowState window2 = createWindow(null, TYPE_APPLICATION, token, "window2");
+        final WindowState window1 = newWindowBuilder("window1", TYPE_APPLICATION).setWindowToken(
+                token).build();
+        final WindowState window2 = newWindowBuilder("window2", TYPE_APPLICATION).setWindowToken(
+                token).build();
 
         window2.removeImmediately();
         // The token should still be mapped in the display content since it still has a child.
@@ -147,8 +156,10 @@
         // Verify that the token is on the display
         assertNotNull(mDisplayContent.getWindowToken(token.token));
 
-        final WindowState window1 = createWindow(null, TYPE_TOAST, token, "window1");
-        final WindowState window2 = createWindow(null, TYPE_TOAST, token, "window2");
+        final WindowState window1 = newWindowBuilder("window1", TYPE_TOAST).setWindowToken(
+                token).build();
+        final WindowState window2 = newWindowBuilder("window2", TYPE_TOAST).setWindowToken(
+                token).build();
 
         mDisplayContent.removeWindowToken(token.token, true /* animateExit */);
         // Verify that the token is no longer mapped on the display
@@ -231,7 +242,8 @@
 
         assertNull(fromClientToken.mSurfaceControl);
 
-        createWindow(null, TYPE_APPLICATION_OVERLAY, fromClientToken, "window");
+        newWindowBuilder("window", TYPE_APPLICATION_OVERLAY).setWindowToken(
+                fromClientToken).build();
         assertNotNull(fromClientToken.mSurfaceControl);
 
         final WindowToken nonClientToken = new WindowToken.Builder(mDisplayContent.mWmService,
@@ -285,7 +297,7 @@
 
         // Simulate an app window to be the IME layering target, assume the app window has no
         // frozen insets state by default.
-        final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+        final WindowState app = newWindowBuilder("app", TYPE_APPLICATION).build();
         mDisplayContent.setImeLayeringTarget(app);
         assertNull(app.getFrozenInsetsState());
         assertTrue(app.isImeLayeringTarget());
@@ -299,7 +311,8 @@
     @Test
     public void testRemoveWindowToken_noAnimateExitWhenSet() {
         final TestWindowToken token = createTestWindowToken(0, mDisplayContent);
-        final WindowState win = createWindow(null, TYPE_APPLICATION, token, "win");
+        final WindowState win = newWindowBuilder("win", TYPE_APPLICATION).setWindowToken(
+                token).build();
         makeWindowVisible(win);
         assertTrue(win.isOnScreen());
         spyOn(win);
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 4f60106..84e2118 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
@@ -221,7 +221,7 @@
     }
 
     WindowState createWindow(String name) {
-        return createWindow(null, TYPE_BASE_APPLICATION, mDisplayContent, name);
+        return newWindowBuilder(name, TYPE_BASE_APPLICATION).setDisplay(mDisplayContent).build();
     }
 
     @Test
@@ -263,12 +263,12 @@
     @Test
     public void testAssignWindowLayers_ForImeWithAppTargetWithChildWindows() {
         final WindowState imeAppTarget = createWindow("imeAppTarget");
-        final WindowState imeAppTargetChildAboveWindow = createWindow(imeAppTarget,
-                TYPE_APPLICATION_ATTACHED_DIALOG, imeAppTarget.mToken,
-                "imeAppTargetChildAboveWindow");
-        final WindowState imeAppTargetChildBelowWindow = createWindow(imeAppTarget,
-                TYPE_APPLICATION_MEDIA_OVERLAY, imeAppTarget.mToken,
-                "imeAppTargetChildBelowWindow");
+        final WindowState imeAppTargetChildAboveWindow = newWindowBuilder(
+                "imeAppTargetChildAboveWindow", TYPE_APPLICATION_ATTACHED_DIALOG).setParent(
+                imeAppTarget).setWindowToken(imeAppTarget.mToken).build();
+        final WindowState imeAppTargetChildBelowWindow = newWindowBuilder(
+                "imeAppTargetChildBelowWindow", TYPE_APPLICATION_MEDIA_OVERLAY).setParent(
+                imeAppTarget).setWindowToken(imeAppTarget.mToken).build();
 
         mDisplayContent.setImeLayeringTarget(imeAppTarget);
         makeWindowVisible(mImeWindow);
@@ -313,9 +313,9 @@
 
     @Test
     public void testAssignWindowLayers_ForImeNonAppImeTarget() {
-        final WindowState imeSystemOverlayTarget = createWindow(null, TYPE_SYSTEM_OVERLAY,
-                mDisplayContent, "imeSystemOverlayTarget",
-                true /* ownerCanAddInternalSystemWindow */);
+        final WindowState imeSystemOverlayTarget = newWindowBuilder("imeSystemOverlayTarget",
+                TYPE_SYSTEM_OVERLAY).setDisplay(mDisplayContent).setOwnerCanAddInternalSystemWindow(
+                true).build();
 
         mDisplayContent.setImeLayeringTarget(imeSystemOverlayTarget);
         mDisplayContent.assignChildLayers(mTransaction);
@@ -354,18 +354,19 @@
     @Test
     public void testStackLayers() {
         final WindowState anyWindow1 = createWindow("anyWindow");
-        final WindowState pinnedStackWindow = createWindow(null, WINDOWING_MODE_PINNED,
-                ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION, mDisplayContent,
-                "pinnedStackWindow");
-        final WindowState dockedStackWindow = createWindow(null,
-                WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, TYPE_BASE_APPLICATION,
-                mDisplayContent, "dockedStackWindow");
-        final WindowState assistantStackWindow = createWindow(null,
-                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, TYPE_BASE_APPLICATION,
-                mDisplayContent, "assistantStackWindow");
-        final WindowState homeActivityWindow = createWindow(null, WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_HOME, TYPE_BASE_APPLICATION,
-                mDisplayContent, "homeActivityWindow");
+        final WindowState pinnedStackWindow = newWindowBuilder("pinnedStackWindow",
+                TYPE_BASE_APPLICATION).setWindowingMode(WINDOWING_MODE_PINNED).setActivityType(
+                ACTIVITY_TYPE_STANDARD).setDisplay(mDisplayContent).build();
+        final WindowState dockedStackWindow = newWindowBuilder("dockedStackWindow",
+                TYPE_BASE_APPLICATION).setWindowingMode(
+                WINDOWING_MODE_MULTI_WINDOW).setActivityType(ACTIVITY_TYPE_STANDARD).setDisplay(
+                mDisplayContent).build();
+        final WindowState assistantStackWindow = newWindowBuilder("assistantStackWindow",
+                TYPE_BASE_APPLICATION).setWindowingMode(WINDOWING_MODE_FULLSCREEN).setActivityType(
+                ACTIVITY_TYPE_ASSISTANT).setDisplay(mDisplayContent).build();
+        final WindowState homeActivityWindow = newWindowBuilder("homeActivityWindow",
+                TYPE_BASE_APPLICATION).setWindowingMode(WINDOWING_MODE_FULLSCREEN).setActivityType(
+                ACTIVITY_TYPE_HOME).setDisplay(mDisplayContent).build();
         final WindowState anyWindow2 = createWindow("anyWindow2");
 
         mDisplayContent.assignChildLayers(mTransaction);
@@ -383,13 +384,12 @@
 
     @Test
     public void testAssignWindowLayers_ForSysUiPanels() {
-        final WindowState navBarPanel =
-                createWindow(null, TYPE_NAVIGATION_BAR_PANEL, mDisplayContent, "NavBarPanel");
-        final WindowState statusBarPanel =
-                createWindow(null, TYPE_STATUS_BAR_ADDITIONAL, mDisplayContent,
-                        "StatusBarAdditional");
-        final WindowState statusBarSubPanel =
-                createWindow(null, TYPE_STATUS_BAR_SUB_PANEL, mDisplayContent, "StatusBarSubPanel");
+        final WindowState navBarPanel = newWindowBuilder("NavBarPanel",
+                TYPE_NAVIGATION_BAR_PANEL).setDisplay(mDisplayContent).build();
+        final WindowState statusBarPanel = newWindowBuilder("StatusBarAdditional",
+                TYPE_STATUS_BAR_ADDITIONAL).setDisplay(mDisplayContent).build();
+        final WindowState statusBarSubPanel = newWindowBuilder("StatusBarSubPanel",
+                TYPE_STATUS_BAR_SUB_PANEL).setDisplay(mDisplayContent).build();
         mDisplayContent.assignChildLayers(mTransaction);
 
         // Ime should be above all app windows and below system windows if it is targeting an app
@@ -401,15 +401,16 @@
 
     @Test
     public void testAssignWindowLayers_ForImeOnPopupImeLayeringTarget() {
-        final WindowState imeAppTarget = createWindow(null, TYPE_APPLICATION,
-                mAppWindow.mActivityRecord, "imeAppTarget");
+        final WindowState imeAppTarget = newWindowBuilder("imeAppTarget",
+                TYPE_APPLICATION).setWindowToken(mAppWindow.mActivityRecord).build();
         mDisplayContent.setImeInputTarget(imeAppTarget);
         mDisplayContent.setImeLayeringTarget(imeAppTarget);
         mDisplayContent.setImeControlTarget(imeAppTarget);
 
         // Set a popup IME layering target and keeps the original IME control target behinds it.
-        final WindowState popupImeTargetWin = createWindow(imeAppTarget,
-                TYPE_APPLICATION_SUB_PANEL, mAppWindow.mActivityRecord, "popupImeTargetWin");
+        final WindowState popupImeTargetWin = newWindowBuilder("popupImeTargetWin",
+                TYPE_APPLICATION_SUB_PANEL).setParent(imeAppTarget).setWindowToken(
+                mAppWindow.mActivityRecord).build();
         mDisplayContent.setImeLayeringTarget(popupImeTargetWin);
         mDisplayContent.updateImeParent();
 
@@ -424,11 +425,11 @@
         // then we can drop all negative layering on the windowing side.
 
         final WindowState anyWindow = createWindow("anyWindow");
-        final WindowState child = createWindow(anyWindow, TYPE_APPLICATION_MEDIA, mDisplayContent,
-                "TypeApplicationMediaChild");
-        final WindowState mediaOverlayChild = createWindow(anyWindow,
-                TYPE_APPLICATION_MEDIA_OVERLAY,
-                mDisplayContent, "TypeApplicationMediaOverlayChild");
+        final WindowState child = newWindowBuilder("TypeApplicationMediaChild",
+                TYPE_APPLICATION_MEDIA).setParent(anyWindow).setDisplay(mDisplayContent).build();
+        final WindowState mediaOverlayChild = newWindowBuilder("TypeApplicationMediaOverlayChild",
+                TYPE_APPLICATION_MEDIA_OVERLAY).setParent(anyWindow).setDisplay(
+                mDisplayContent).build();
 
         mDisplayContent.assignChildLayers(mTransaction);
 
@@ -440,14 +441,17 @@
     public void testAssignWindowLayers_ForPostivelyZOrderedSubtype() {
         final WindowState anyWindow = createWindow("anyWindow");
         final ArrayList<WindowState> childList = new ArrayList<>();
-        childList.add(createWindow(anyWindow, TYPE_APPLICATION_PANEL, mDisplayContent,
-                "TypeApplicationPanelChild"));
-        childList.add(createWindow(anyWindow, TYPE_APPLICATION_SUB_PANEL, mDisplayContent,
-                "TypeApplicationSubPanelChild"));
-        childList.add(createWindow(anyWindow, TYPE_APPLICATION_ATTACHED_DIALOG, mDisplayContent,
-                "TypeApplicationAttachedDialogChild"));
-        childList.add(createWindow(anyWindow, TYPE_APPLICATION_ABOVE_SUB_PANEL, mDisplayContent,
-                "TypeApplicationAboveSubPanelPanelChild"));
+        childList.add(newWindowBuilder("TypeApplicationPanelChild",
+                TYPE_APPLICATION_PANEL).setParent(anyWindow).setDisplay(mDisplayContent).build());
+        childList.add(newWindowBuilder("TypeApplicationSubPanelChild",
+                TYPE_APPLICATION_SUB_PANEL).setParent(anyWindow).setDisplay(
+                mDisplayContent).build());
+        childList.add(newWindowBuilder("TypeApplicationAttachedDialogChild",
+                TYPE_APPLICATION_ATTACHED_DIALOG).setParent(anyWindow).setDisplay(
+                mDisplayContent).build());
+        childList.add(newWindowBuilder("TypeApplicationAboveSubPanelPanelChild",
+                TYPE_APPLICATION_ABOVE_SUB_PANEL).setParent(anyWindow).setDisplay(
+                mDisplayContent).build());
 
         final LayerRecordingTransaction t = mTransaction;
         mDisplayContent.assignChildLayers(t);
@@ -469,8 +473,8 @@
 
         // Create a popupWindow
         assertWindowHigher(mImeWindow, mAppWindow);
-        final WindowState popupWindow = createWindow(mAppWindow, TYPE_APPLICATION_PANEL,
-                mDisplayContent, "PopupWindow");
+        final WindowState popupWindow = newWindowBuilder("PopupWindow",
+                TYPE_APPLICATION_PANEL).setParent(mAppWindow).setDisplay(mDisplayContent).build();
         spyOn(popupWindow);
 
         mDisplayContent.assignChildLayers(mTransaction);
@@ -492,8 +496,9 @@
         makeWindowVisible(mImeWindow);
 
         // Create a popupWindow
-        final WindowState systemDialogWindow = createWindow(null, TYPE_SECURE_SYSTEM_OVERLAY,
-                mDisplayContent, "SystemDialog", true);
+        final WindowState systemDialogWindow = newWindowBuilder("SystemDialog",
+                TYPE_SECURE_SYSTEM_OVERLAY).setDisplay(
+                mDisplayContent).setOwnerCanAddInternalSystemWindow(true).build();
         systemDialogWindow.mAttrs.flags |= FLAG_ALT_FOCUSABLE_IM;
         spyOn(systemDialogWindow);
 
diff --git a/services/texttospeech/java/com/android/server/texttospeech/TextToSpeechManagerPerUserService.java b/services/texttospeech/java/com/android/server/texttospeech/TextToSpeechManagerPerUserService.java
index d49214a..a9ae5f7 100644
--- a/services/texttospeech/java/com/android/server/texttospeech/TextToSpeechManagerPerUserService.java
+++ b/services/texttospeech/java/com/android/server/texttospeech/TextToSpeechManagerPerUserService.java
@@ -16,6 +16,10 @@
 
 package com.android.server.texttospeech;
 
+import static android.content.Context.BIND_AUTO_CREATE;
+import static android.content.Context.BIND_FOREGROUND_SERVICE;
+import static android.content.Context.BIND_SCHEDULE_LIKE_TOP_APP;
+
 import static com.android.internal.infra.AbstractRemoteService.PERMANENT_BOUND_TIMEOUT_MS;
 
 import android.annotation.NonNull;
@@ -95,7 +99,7 @@
                 ITextToSpeechSessionCallback callback) {
             super(context,
                     new Intent(TextToSpeech.Engine.INTENT_ACTION_TTS_SERVICE).setPackage(engine),
-                    Context.BIND_AUTO_CREATE | Context.BIND_SCHEDULE_LIKE_TOP_APP,
+                    BIND_AUTO_CREATE | BIND_SCHEDULE_LIKE_TOP_APP | BIND_FOREGROUND_SERVICE,
                     userId,
                     ITextToSpeechService.Stub::asInterface);
             mEngine = engine;
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index e7c9e92..e27dbe5 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -1869,10 +1869,18 @@
     }
 
     private boolean shouldDeleteObsoleteData(UserHandle userHandle) {
-        final DevicePolicyManagerInternal dpmInternal = getDpmInternal();
-        // If a profile owner is not defined for the given user, obsolete data should be deleted
-        return dpmInternal == null
-                || dpmInternal.getProfileOwnerOrDeviceOwnerSupervisionComponent(userHandle) == null;
+        if (android.app.supervision.flags.Flags.deprecateDpmSupervisionApis()) {
+            final SupervisionManagerInternal smInternal = getSupervisionManagerInternal();
+            // If supervision is not enabled for the given user, obsolete data should be deleted.
+            return smInternal == null
+                    || !smInternal.isSupervisionEnabledForUser(userHandle.getIdentifier());
+        } else {
+            final DevicePolicyManagerInternal dpmInternal = getDpmInternal();
+            // If a profile owner is not defined for the given user, obsolete data should be deleted
+            return dpmInternal == null
+                    || dpmInternal.getProfileOwnerOrDeviceOwnerSupervisionComponent(userHandle)
+                            == null;
+        }
     }
 
     private String buildFullToken(String packageName, String token) {
diff --git a/telecomm/java/android/telecom/StatusHints.java b/telecomm/java/android/telecom/StatusHints.java
index 5f0c8d72..31b84ff 100644
--- a/telecomm/java/android/telecom/StatusHints.java
+++ b/telecomm/java/android/telecom/StatusHints.java
@@ -27,6 +27,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.UserHandle;
+import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
 
@@ -40,6 +41,7 @@
     private final CharSequence mLabel;
     private Icon mIcon;
     private final Bundle mExtras;
+    private static final String TAG = StatusHints.class.getSimpleName();
 
     /**
      * @hide
@@ -150,17 +152,37 @@
         // incompatible types.
         if (icon != null && (icon.getType() == Icon.TYPE_URI
                 || icon.getType() == Icon.TYPE_URI_ADAPTIVE_BITMAP)) {
-            String encodedUser = icon.getUri().getEncodedUserInfo();
-            // If there is no encoded user, the URI is calling into the calling user space
-            if (encodedUser != null) {
-                int userId = Integer.parseInt(encodedUser);
-                // Do not try to save the icon if the user id isn't in the calling user space.
-                if (userId != callingUserHandle.getIdentifier()) return null;
+            int callingUserId = callingUserHandle.getIdentifier();
+            int requestingUserId = getUserIdFromAuthority(
+                    icon.getUri().getAuthority(), callingUserId);
+            if (callingUserId != requestingUserId) {
+                return null;
             }
+
         }
         return icon;
     }
 
+    /**
+     * Derives the user id from the authority or the default user id if none could be found.
+     * @param auth
+     * @param defaultUserId
+     * @return The user id from the given authority.
+     * @hide
+     */
+    public static int getUserIdFromAuthority(String auth, int defaultUserId) {
+        if (auth == null) return defaultUserId;
+        int end = auth.lastIndexOf('@');
+        if (end == -1) return defaultUserId;
+        String userIdString = auth.substring(0, end);
+        try {
+            return Integer.parseInt(userIdString);
+        } catch (NumberFormatException e) {
+            Log.w(TAG, "Error parsing userId." + e);
+            return UserHandle.USER_NULL;
+        }
+    }
+
     @Override
     public void writeToParcel(Parcel out, int flags) {
         out.writeCharSequence(mLabel);
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 7082f00..e65e4b0 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -29,6 +29,7 @@
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
+import android.annotation.TestApi;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.EnabledSince;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -1886,6 +1887,34 @@
     }
 
     /**
+     * This test API determines the foreground service delegation state for a VoIP app that adds
+     * calls via {@link TelecomManager#addCall(CallAttributes, Executor, OutcomeReceiver,
+     * CallControlCallback, CallEventCallback)}.  Foreground Service Delegation allows applications
+     * to operate in the background  starting in Android 14 and is granted by Telecom via a request
+     * to the ActivityManager.
+     *
+     * @param handle of the voip app that is being checked
+     * @return true if the app has foreground service delegation. Otherwise, false.
+     *
+     * @hide
+     */
+    @FlaggedApi(Flags.FLAG_VOIP_CALL_MONITOR_REFACTOR)
+    @TestApi
+    public boolean hasForegroundServiceDelegation(@Nullable PhoneAccountHandle handle) {
+        ITelecomService service = getTelecomService();
+        if (service != null) {
+            try {
+                return service.hasForegroundServiceDelegation(handle, mContext.getOpPackageName());
+            } catch (RemoteException e) {
+                Log.e(TAG,
+                        "RemoteException calling ITelecomService#hasForegroundServiceDelegation.",
+                        e);
+            }
+        }
+        return false;
+    }
+
+    /**
      * Return the line 1 phone number for given phone account.
      *
      * <p>Requires Permission:
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index c85374e..b32379a 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -409,4 +409,10 @@
      */
     void addCall(in CallAttributes callAttributes, in ICallEventCallback callback, String callId,
         String callingPackage);
+
+    /**
+     * @see TelecomServiceImpl#hasForegroundServiceDelegation
+     */
+    boolean hasForegroundServiceDelegation(in PhoneAccountHandle phoneAccountHandle,
+                                                       String callingPackage);
 }
diff --git a/telephony/java/android/telephony/CellularIdentifierDisclosure.java b/telephony/java/android/telephony/CellularIdentifierDisclosure.java
index 0b6a70f..92c51ec 100644
--- a/telephony/java/android/telephony/CellularIdentifierDisclosure.java
+++ b/telephony/java/android/telephony/CellularIdentifierDisclosure.java
@@ -74,6 +74,14 @@
     /** IMEI DETATCH INDICATION. Reference: 3GPP TS 24.008 9.2.14.
      * Applies to 2g and 3g networks. Used for circuit-switched detach. */
     public static final int NAS_PROTOCOL_MESSAGE_IMSI_DETACH_INDICATION = 11;
+    /** Vendor-specific enumeration to identify a disclosure as potentially benign.
+     * Enables vendors to semantically classify disclosures based on their own logic. */
+    @FlaggedApi(Flags.FLAG_VENDOR_SPECIFIC_CELLULAR_IDENTIFIER_DISCLOSURE_INDICATIONS)
+    public static final int NAS_PROTOCOL_MESSAGE_THREAT_IDENTIFIER_FALSE = 12;
+    /** Vendor-specific enumeration to identify a disclosure as potentially harmful.
+     * Enables vendors to semantically classify disclosures based on their own logic. */
+    @FlaggedApi(Flags.FLAG_VENDOR_SPECIFIC_CELLULAR_IDENTIFIER_DISCLOSURE_INDICATIONS)
+    public static final int NAS_PROTOCOL_MESSAGE_THREAT_IDENTIFIER_TRUE = 13;
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
@@ -84,7 +92,9 @@
             NAS_PROTOCOL_MESSAGE_AUTHENTICATION_AND_CIPHERING_RESPONSE,
             NAS_PROTOCOL_MESSAGE_REGISTRATION_REQUEST, NAS_PROTOCOL_MESSAGE_DEREGISTRATION_REQUEST,
             NAS_PROTOCOL_MESSAGE_CM_REESTABLISHMENT_REQUEST,
-            NAS_PROTOCOL_MESSAGE_CM_SERVICE_REQUEST, NAS_PROTOCOL_MESSAGE_IMSI_DETACH_INDICATION})
+            NAS_PROTOCOL_MESSAGE_CM_SERVICE_REQUEST, NAS_PROTOCOL_MESSAGE_IMSI_DETACH_INDICATION,
+            NAS_PROTOCOL_MESSAGE_THREAT_IDENTIFIER_FALSE,
+            NAS_PROTOCOL_MESSAGE_THREAT_IDENTIFIER_TRUE})
     public @interface NasProtocolMessage {
     }
 
@@ -156,6 +166,14 @@
         return mIsEmergency;
     }
 
+    /**
+     * @return if the modem vendor classifies the disclosure as benign.
+     */
+    @FlaggedApi(Flags.FLAG_VENDOR_SPECIFIC_CELLULAR_IDENTIFIER_DISCLOSURE_INDICATIONS)
+    public boolean isBenign() {
+        return mNasProtocolMessage == NAS_PROTOCOL_MESSAGE_THREAT_IDENTIFIER_FALSE;
+    }
+
     @Override
     public int describeContents() {
         return 0;
diff --git a/telephony/java/android/telephony/satellite/ISatelliteCommunicationAllowedStateCallback.aidl b/telephony/java/android/telephony/satellite/ISatelliteCommunicationAccessStateCallback.aidl
similarity index 81%
rename from telephony/java/android/telephony/satellite/ISatelliteCommunicationAllowedStateCallback.aidl
rename to telephony/java/android/telephony/satellite/ISatelliteCommunicationAccessStateCallback.aidl
index 2730f90..a3c66a0 100644
--- a/telephony/java/android/telephony/satellite/ISatelliteCommunicationAllowedStateCallback.aidl
+++ b/telephony/java/android/telephony/satellite/ISatelliteCommunicationAccessStateCallback.aidl
@@ -19,18 +19,18 @@
 import android.telephony.satellite.SatelliteAccessConfiguration;
 
 /**
- * Interface for satellite communication allowed state callback.
+ * Interface for satellite communication access state callback.
  * @hide
  */
-oneway interface ISatelliteCommunicationAllowedStateCallback {
+oneway interface ISatelliteCommunicationAccessStateCallback {
     /**
      * Telephony does not guarantee that whenever there is a change in communication allowed
      * state, this API will be called. Telephony does its best to detect the changes and notify
-     * its listners accordingly.
+     * its listeners accordingly.
      *
      * @param allowed whether satellite communication state or not
      */
-    void onSatelliteCommunicationAllowedStateChanged(in boolean isAllowed);
+    void onAccessAllowedStateChanged(in boolean isAllowed);
 
     /**
      * Callback method invoked when the satellite access configuration changes
@@ -39,6 +39,6 @@
      * When satellite is not allowed at the current location,
      * {@code satelliteRegionalConfiguration} will be null.
      */
-    void onSatelliteAccessConfigurationChanged(in SatelliteAccessConfiguration
+    void onAccessConfigurationChanged(in SatelliteAccessConfiguration
         satelliteAccessConfiguration);
 }
diff --git a/telephony/java/android/telephony/satellite/SatelliteCommunicationAllowedStateCallback.java b/telephony/java/android/telephony/satellite/SatelliteCommunicationAccessStateCallback.java
similarity index 79%
rename from telephony/java/android/telephony/satellite/SatelliteCommunicationAllowedStateCallback.java
rename to telephony/java/android/telephony/satellite/SatelliteCommunicationAccessStateCallback.java
index 6291102..7fb8a96 100644
--- a/telephony/java/android/telephony/satellite/SatelliteCommunicationAllowedStateCallback.java
+++ b/telephony/java/android/telephony/satellite/SatelliteCommunicationAccessStateCallback.java
@@ -25,24 +25,24 @@
 
 
 /**
- * A callback class for monitoring satellite communication allowed state changed events.
+ * A callback class for monitoring satellite communication access state changed events.
  *
  * @hide
  */
 @SystemApi
 @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
-public interface SatelliteCommunicationAllowedStateCallback {
+public interface SatelliteCommunicationAccessStateCallback {
 
     /**
      * Telephony does not guarantee that whenever there is a change in communication allowed state,
      * this API will be called. Telephony does its best to detect the changes and notify its
-     * listeners accordingly. Satellite communication is allowed at a location when it is legally
-     * allowed by the local authority and satellite signal coverage is available.
+     * listeners accordingly. Satellite communication access is allowed at a location when it is
+     * legally allowed by the local authority and satellite signal coverage is available.
      *
      * @param isAllowed {@code true} means satellite is allowed,
      *                  {@code false} satellite is not allowed.
      */
-    void onSatelliteCommunicationAllowedStateChanged(boolean isAllowed);
+    void onAccessAllowedStateChanged(boolean isAllowed);
 
     /**
      * Callback method invoked when the satellite access configuration changes
@@ -52,6 +52,6 @@
      *                                       the current location,
      *                                       {@code satelliteRegionalConfiguration} will be null.
      */
-    default void onSatelliteAccessConfigurationChanged(
+    default void onAccessConfigurationChanged(
             @Nullable SatelliteAccessConfiguration satelliteAccessConfiguration) {};
 }
diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java
index b885b30..b7b209b 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -100,9 +100,9 @@
     private static final ConcurrentHashMap<Consumer<Boolean>,
             IBooleanConsumer> sSatelliteSupportedStateCallbackMap =
             new ConcurrentHashMap<>();
-    private static final ConcurrentHashMap<SatelliteCommunicationAllowedStateCallback,
-            ISatelliteCommunicationAllowedStateCallback>
-            sSatelliteCommunicationAllowedStateCallbackMap =
+    private static final ConcurrentHashMap<SatelliteCommunicationAccessStateCallback,
+            ISatelliteCommunicationAccessStateCallback>
+            sSatelliteCommunicationAccessStateCallbackMap =
             new ConcurrentHashMap<>();
     private static final ConcurrentHashMap<SatelliteDisallowedReasonsCallback,
             ISatelliteDisallowedReasonsCallback>
@@ -3398,10 +3398,10 @@
     }
 
     /**
-     * Registers for the satellite communication allowed state changed.
+     * Registers for the satellite communication access state changed event.
      *
      * @param executor The executor on which the callback will be called.
-     * @param callback The callback to handle satellite communication allowed state changed event.
+     * @param callback The callback to handle satellite communication access state changed event.
      * @return The {@link SatelliteResult} result of the operation.
      * @throws SecurityException     if the caller doesn't have required permission.
      * @throws IllegalStateException if the Telephony process is not currently available.
@@ -3411,54 +3411,54 @@
     @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
     @SatelliteResult
-    public int registerForCommunicationAllowedStateChanged(
+    public int registerForCommunicationAccessStateChanged(
             @NonNull @CallbackExecutor Executor executor,
-            @NonNull SatelliteCommunicationAllowedStateCallback callback) {
+            @NonNull SatelliteCommunicationAccessStateCallback callback) {
         Objects.requireNonNull(executor);
         Objects.requireNonNull(callback);
 
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
-                ISatelliteCommunicationAllowedStateCallback internalCallback =
-                        new ISatelliteCommunicationAllowedStateCallback.Stub() {
+                ISatelliteCommunicationAccessStateCallback internalCallback =
+                        new ISatelliteCommunicationAccessStateCallback.Stub() {
                             @Override
-                            public void onSatelliteCommunicationAllowedStateChanged(
+                            public void onAccessAllowedStateChanged(
                                     boolean isAllowed) {
                                 executor.execute(() -> Binder.withCleanCallingIdentity(
-                                        () -> callback.onSatelliteCommunicationAllowedStateChanged(
+                                        () -> callback.onAccessAllowedStateChanged(
                                                 isAllowed)));
                             }
 
                             @Override
-                            public void onSatelliteAccessConfigurationChanged(
+                            public void onAccessConfigurationChanged(
                                     @Nullable SatelliteAccessConfiguration
                                             satelliteAccessConfiguration) {
                                 executor.execute(() -> Binder.withCleanCallingIdentity(
-                                        () -> callback.onSatelliteAccessConfigurationChanged(
+                                        () -> callback.onAccessConfigurationChanged(
                                                 satelliteAccessConfiguration)));
                             }
                         };
-                sSatelliteCommunicationAllowedStateCallbackMap.put(callback, internalCallback);
-                return telephony.registerForCommunicationAllowedStateChanged(
+                sSatelliteCommunicationAccessStateCallbackMap.put(callback, internalCallback);
+                return telephony.registerForCommunicationAccessStateChanged(
                         mSubId, internalCallback);
             } else {
                 throw new IllegalStateException("telephony service is null.");
             }
         } catch (RemoteException ex) {
-            loge("registerForCommunicationAllowedStateChanged() RemoteException: " + ex);
+            loge("registerForCommunicationAccessStateChanged() RemoteException: " + ex);
             ex.rethrowAsRuntimeException();
         }
         return SATELLITE_RESULT_REQUEST_FAILED;
     }
 
     /**
-     * Unregisters for the satellite communication allowed state changed.
+     * Unregisters for the satellite communication access state changed event.
      * If callback was not registered before, the request will be ignored.
      *
      * @param callback The callback that was passed to
-     *                 {@link #registerForCommunicationAllowedStateChanged(Executor,
-     *                 SatelliteCommunicationAllowedStateCallback)}
+     *                 {@link #registerForCommunicationAccessStateChanged(Executor,
+     *                 SatelliteCommunicationAccessStateCallback)}
      * @throws SecurityException     if the caller doesn't have required permission.
      * @throws IllegalStateException if the Telephony process is not currently available.
      * @hide
@@ -3466,26 +3466,26 @@
     @SystemApi
     @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
     @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
-    public void unregisterForCommunicationAllowedStateChanged(
-            @NonNull SatelliteCommunicationAllowedStateCallback callback) {
+    public void unregisterForCommunicationAccessStateChanged(
+            @NonNull SatelliteCommunicationAccessStateCallback callback) {
         Objects.requireNonNull(callback);
-        ISatelliteCommunicationAllowedStateCallback internalCallback =
-                sSatelliteCommunicationAllowedStateCallbackMap.remove(callback);
+        ISatelliteCommunicationAccessStateCallback internalCallback =
+                sSatelliteCommunicationAccessStateCallbackMap.remove(callback);
 
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
                 if (internalCallback != null) {
-                    telephony.unregisterForCommunicationAllowedStateChanged(mSubId,
+                    telephony.unregisterForCommunicationAccessStateChanged(mSubId,
                             internalCallback);
                 } else {
-                    loge("unregisterForCommunicationAllowedStateChanged: No internal callback.");
+                    loge("unregisterForCommunicationAccessStateChanged: No internal callback.");
                 }
             } else {
                 throw new IllegalStateException("telephony service is null.");
             }
         } catch (RemoteException ex) {
-            loge("unregisterForCommunicationAllowedStateChanged() RemoteException: " + ex);
+            loge("unregisterForCommunicationAccessStateChanged() RemoteException: " + ex);
             ex.rethrowAsRuntimeException();
         }
     }
@@ -3690,6 +3690,11 @@
      * @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.
+     *                 If the request is successful, {@link OutcomeReceiver#onResult}
+     *                 will be called.
+     *                 If the request is not successful,
+     *                 {@link OutcomeReceiver#onError(Throwable)} will return an error with
+     *                 a SatelliteException.
      *
      * @throws SecurityException if the caller doesn't have required permission.
      * @hide
@@ -3699,7 +3704,7 @@
     @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
     public void provisionSatellite(@NonNull List<SatelliteSubscriberInfo> list,
             @NonNull @CallbackExecutor Executor executor,
-            @NonNull OutcomeReceiver<Boolean, SatelliteException> callback) {
+            @NonNull OutcomeReceiver<Void, SatelliteException> callback) {
         Objects.requireNonNull(executor);
         Objects.requireNonNull(callback);
 
@@ -3713,8 +3718,8 @@
                             if (resultData.containsKey(KEY_PROVISION_SATELLITE_TOKENS)) {
                                 boolean isUpdated =
                                         resultData.getBoolean(KEY_PROVISION_SATELLITE_TOKENS);
-                                executor.execute(() -> Binder.withCleanCallingIdentity(() ->
-                                        callback.onResult(isUpdated)));
+                                executor.execute(() -> Binder.withCleanCallingIdentity(
+                                        () -> callback.onResult(null)));
                             } else {
                                 loge("KEY_REQUEST_PROVISION_TOKENS does not exist.");
                                 executor.execute(() -> Binder.withCleanCallingIdentity(() ->
@@ -3746,6 +3751,11 @@
      * @param list The list of deprovisioned 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.
+     *                 If the request is successful, {@link OutcomeReceiver#onResult}
+     *                 will be called.
+     *                 If the request is not successful,
+     *                 {@link OutcomeReceiver#onError(Throwable)} will return an error with
+     *                 a SatelliteException.
      *
      * @throws SecurityException if the caller doesn't have required permission.
      * @hide
@@ -3755,7 +3765,7 @@
     @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
     public void deprovisionSatellite(@NonNull List<SatelliteSubscriberInfo> list,
             @NonNull @CallbackExecutor Executor executor,
-            @NonNull OutcomeReceiver<Boolean, SatelliteException> callback) {
+            @NonNull OutcomeReceiver<Void, SatelliteException> callback) {
         Objects.requireNonNull(executor);
         Objects.requireNonNull(callback);
 
@@ -3770,7 +3780,7 @@
                                 boolean isUpdated =
                                         resultData.getBoolean(KEY_DEPROVISION_SATELLITE_TOKENS);
                                 executor.execute(() -> Binder.withCleanCallingIdentity(() ->
-                                        callback.onResult(isUpdated)));
+                                        callback.onResult(null)));
                             } else {
                                 loge("KEY_DEPROVISION_SATELLITE_TOKENS does not exist.");
                                 executor.execute(() -> Binder.withCleanCallingIdentity(() ->
diff --git a/telephony/java/android/telephony/satellite/SatellitePosition.java b/telephony/java/android/telephony/satellite/SatellitePosition.java
index 46af5c8..354b729 100644
--- a/telephony/java/android/telephony/satellite/SatellitePosition.java
+++ b/telephony/java/android/telephony/satellite/SatellitePosition.java
@@ -33,6 +33,7 @@
  * Longitude is the angular distance, measured in degrees, east or west of the prime longitude line
  * ranging from -180 to 180 degrees
  * Altitude is the distance from the center of the Earth to the satellite, measured in kilometers
+ * Latitude is not added as only geo stationary satellite are handled for now.
  *
  * @hide
  */
diff --git a/telephony/java/android/telephony/satellite/SatelliteSessionStats.java b/telephony/java/android/telephony/satellite/SatelliteSessionStats.java
index 556ec1a..402ac73 100644
--- a/telephony/java/android/telephony/satellite/SatelliteSessionStats.java
+++ b/telephony/java/android/telephony/satellite/SatelliteSessionStats.java
@@ -252,6 +252,13 @@
         }
     }
 
+    public void resetCountOfUserMessagesInQueueToBeSent() {
+        for (Map.Entry<Integer, SatelliteSessionStats> entry : datagramStats.entrySet()) {
+            SatelliteSessionStats statsPerDatagramType = entry.getValue();
+            statsPerDatagramType.mCountOfUserMessagesInQueueToBeSent = 0;
+        }
+    }
+
     public int getCountOfSuccessfulOutgoingDatagram(
             @SatelliteManager.DatagramType int datagramType) {
         SatelliteSessionStats data = datagramStats.getOrDefault(datagramType,
diff --git a/telephony/java/android/telephony/satellite/SatelliteSubscriberInfo.java b/telephony/java/android/telephony/satellite/SatelliteSubscriberInfo.java
index 6e33995..9d9cac9 100644
--- a/telephony/java/android/telephony/satellite/SatelliteSubscriberInfo.java
+++ b/telephony/java/android/telephony/satellite/SatelliteSubscriberInfo.java
@@ -47,7 +47,7 @@
 
     /** apn */
     private String mNiddApn;
-    private int mSubId;
+    private int mSubscriptionId;
 
     /** SubscriberId format is the ICCID. */
     public static final int SUBSCRIBER_ID_TYPE_ICCID = 0;
@@ -75,7 +75,7 @@
         this.mSubscriberId = builder.mSubscriberId;
         this.mCarrierId = builder.mCarrierId;
         this.mNiddApn = builder.mNiddApn;
-        this.mSubId = builder.mSubId;
+        this.mSubscriptionId = builder.mSubscriptionId;
         this.mSubscriberIdType = builder.mSubscriberIdType;
     }
 
@@ -87,7 +87,7 @@
         private int mCarrierId;
         @NonNull
         private String mNiddApn;
-        private int mSubId;
+        private int mSubscriptionId;
         @SubscriberIdType
         private int mSubscriberIdType;
 
@@ -125,8 +125,8 @@
          * Set the subId and returns the Builder class.
          */
         @NonNull
-        public Builder setSubId(int subId) {
-            mSubId = subId;
+        public Builder setSubscriptionId(int subId) {
+            mSubscriptionId = subId;
             return this;
         }
 
@@ -153,7 +153,7 @@
         out.writeString(mSubscriberId);
         out.writeInt(mCarrierId);
         out.writeString(mNiddApn);
-        out.writeInt(mSubId);
+        out.writeInt(mSubscriptionId);
         out.writeInt(mSubscriberIdType);
     }
 
@@ -203,8 +203,8 @@
     /**
      * Return the subscriptionId of the subscription which is used for satellite attachment.
      */
-    public int getSubId() {
-        return mSubId;
+    public int getSubscriptionId() {
+        return mSubscriptionId;
     }
 
     /**
@@ -231,8 +231,8 @@
         sb.append(mNiddApn);
         sb.append(",");
 
-        sb.append("SubId:");
-        sb.append(mSubId);
+        sb.append("SubscriptionId:");
+        sb.append(mSubscriptionId);
         sb.append(",");
 
         sb.append("SubscriberIdType:");
@@ -242,7 +242,8 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(mSubscriberId, mCarrierId, mNiddApn, mSubId, mSubscriberIdType);
+        return Objects.hash(
+                mSubscriberId, mCarrierId, mNiddApn, mSubscriptionId, mSubscriberIdType);
     }
 
     @Override
@@ -251,7 +252,8 @@
         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
+                && Objects.equals(mNiddApn, that.mNiddApn)
+                && mSubscriptionId == that.mSubscriptionId
                 && mSubscriberIdType == that.mSubscriberIdType;
     }
 
@@ -259,7 +261,7 @@
         mSubscriberId = in.readString();
         mCarrierId = in.readInt();
         mNiddApn = in.readString();
-        mSubId = in.readInt();
+        mSubscriptionId = in.readInt();
         mSubscriberIdType = in.readInt();
     }
 }
diff --git a/telephony/java/android/telephony/satellite/SatelliteTransmissionUpdateCallback.java b/telephony/java/android/telephony/satellite/SatelliteTransmissionUpdateCallback.java
index b5dfb63..e18fad3 100644
--- a/telephony/java/android/telephony/satellite/SatelliteTransmissionUpdateCallback.java
+++ b/telephony/java/android/telephony/satellite/SatelliteTransmissionUpdateCallback.java
@@ -78,6 +78,9 @@
     /**
      * Called when framework receives a request to send a datagram.
      *
+     * Informs external apps that device is working on sending a datagram out and is in the process
+     * of checking if all the conditions required to send datagrams are met.
+     *
      * @param datagramType The type of the requested datagram.
      */
     @FlaggedApi(Flags.FLAG_SATELLITE_SYSTEM_APIS)
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index aa57730..08c0030 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -69,7 +69,7 @@
 import android.telephony.ims.aidl.IRcsConfigCallback;
 import android.telephony.satellite.INtnSignalStrengthCallback;
 import android.telephony.satellite.ISatelliteCapabilitiesCallback;
-import android.telephony.satellite.ISatelliteCommunicationAllowedStateCallback;
+import android.telephony.satellite.ISatelliteCommunicationAccessStateCallback;
 import android.telephony.satellite.ISatelliteDatagramCallback;
 import android.telephony.satellite.ISatelliteDisallowedReasonsCallback;
 import android.telephony.satellite.ISatelliteTransmissionUpdateCallback;
@@ -3446,20 +3446,20 @@
      */
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
             + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
-    int registerForCommunicationAllowedStateChanged(int subId,
-            in ISatelliteCommunicationAllowedStateCallback callback);
+    int registerForCommunicationAccessStateChanged(int subId,
+            in ISatelliteCommunicationAccessStateCallback callback);
 
     /**
      * Unregisters for satellite communication allowed state.
      * 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 registerForCommunicationAllowedStateChanged.
+     * @param callback The callback that was passed to registerForCommunicationAccessStateChanged.
      */
     @JavaPassthrough(annotation="@android.annotation.RequiresPermission("
             + "android.Manifest.permission.SATELLITE_COMMUNICATION)")
-    void unregisterForCommunicationAllowedStateChanged(int subId,
-            in ISatelliteCommunicationAllowedStateCallback callback);
+    void unregisterForCommunicationAccessStateChanged(int subId,
+            in ISatelliteCommunicationAccessStateCallback callback);
 
     /**
      * This API can be used by only CTS to override the boolean configs used by the
diff --git a/test-mock/Android.bp b/test-mock/Android.bp
index 71f3033..cadb0bd 100644
--- a/test-mock/Android.bp
+++ b/test-mock/Android.bp
@@ -72,6 +72,7 @@
         "tests/**/*.java",
     ],
     auto_gen_config: true,
+    team: "trendy_team_ravenwood",
 }
 
 // Make the current.txt available for use by the cts/tests/signature and /vendor tests.
diff --git a/tests/AppJankTest/src/android/app/jank/tests/JankUtils.java b/tests/AppJankTest/src/android/app/jank/tests/JankUtils.java
index df92898..9640a84 100644
--- a/tests/AppJankTest/src/android/app/jank/tests/JankUtils.java
+++ b/tests/AppJankTest/src/android/app/jank/tests/JankUtils.java
@@ -29,6 +29,7 @@
         AppJankStats jankStats = new AppJankStats(
                 /*App Uid*/APP_ID,
                 /*Widget Id*/"test widget id",
+                /*navigationComponent*/null,
                 /*Widget Category*/AppJankStats.WIDGET_CATEGORY_SCROLL,
                 /*Widget State*/AppJankStats.WIDGET_STATE_SCROLLING,
                 /*Total Frames*/100,
diff --git a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt
index 08b5f38..75bd5d1 100644
--- a/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt
+++ b/tests/FlickerTests/ActivityEmbedding/src/com/android/server/wm/flicker/activityembedding/open/MainActivityStartsSecondaryWithAlwaysExpandTest.kt
@@ -17,7 +17,6 @@
 package com.android.server.wm.flicker.activityembedding.open
 
 import android.graphics.Rect
-import android.platform.test.annotations.Presubmit
 import android.tools.flicker.junit.FlickerParametersRunnerFactory
 import android.tools.flicker.legacy.FlickerBuilder
 import android.tools.flicker.legacy.LegacyFlickerTest
@@ -68,13 +67,21 @@
         }
     }
 
-    @Ignore("Not applicable to this CUJ.") override fun navBarWindowIsVisibleAtStartAndEnd() {}
+    @Ignore("Not applicable to this CUJ.")
+    @Test
+    override fun navBarWindowIsVisibleAtStartAndEnd() {}
 
-    @FlakyTest(bugId = 291575593) override fun entireScreenCovered() {}
+    @FlakyTest(bugId = 291575593)
+    @Test
+    override fun entireScreenCovered() {}
 
-    @Ignore("Not applicable to this CUJ.") override fun statusBarWindowIsAlwaysVisible() {}
+    @Ignore("Not applicable to this CUJ.")
+    @Test
+    override fun statusBarWindowIsAlwaysVisible() {}
 
-    @Ignore("Not applicable to this CUJ.") override fun statusBarLayerPositionAtStartAndEnd() {}
+    @Ignore("Not applicable to this CUJ.")
+    @Test
+    override fun statusBarLayerPositionAtStartAndEnd() {}
 
     /** Transition begins with a split. */
     @FlakyTest(bugId = 286952194)
@@ -122,7 +129,6 @@
 
     /** Always expand activity is on top of the split. */
     @FlakyTest(bugId = 286952194)
-    @Presubmit
     @Test
     fun endsWithAlwaysExpandActivityOnTop() {
         flicker.assertWmEnd {
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 0ca8f37..e413645 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
@@ -176,12 +176,15 @@
     }
 
     @Ignore("Not applicable to this CUJ.")
+    @Test
     override fun visibleLayersShownMoreThanOneConsecutiveEntry() {}
 
     @FlakyTest(bugId = 342596801)
+    @Test
     override fun entireScreenCovered() = super.entireScreenCovered()
 
     @FlakyTest(bugId = 342596801)
+    @Test
     override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
         super.visibleWindowsShownMoreThanOneConsecutiveEntry()
 
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt
index b8f11dc..ad083fa 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromFixedOrientationTest.kt
@@ -16,7 +16,6 @@
 
 package com.android.server.wm.flicker.ime
 
-import android.platform.test.annotations.Postsubmit
 import android.platform.test.annotations.Presubmit
 import android.tools.Rotation
 import android.tools.flicker.junit.FlickerParametersRunnerFactory
@@ -81,7 +80,6 @@
     }
 
     @FlakyTest(bugId = 290767483)
-    @Postsubmit
     @Test
     fun imeLayerAlphaOneAfterSnapshotStartingWindowRemoval() {
         val layerTrace = flicker.reader.readLayersTrace() ?: error("Unable to read layers trace")
diff --git a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/BottomHalfPipAppHelper.kt b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/BottomHalfPipAppHelper.kt
index 6573c2c..fe344c9 100644
--- a/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/BottomHalfPipAppHelper.kt
+++ b/tests/FlickerTests/test-apps/app-helpers/src/com/android/server/wm/flicker/helpers/BottomHalfPipAppHelper.kt
@@ -18,24 +18,46 @@
 
 import android.app.Instrumentation
 import android.content.Intent
+import android.tools.traces.parsers.WindowManagerStateHelper
 import android.tools.traces.parsers.toFlickerComponent
-import com.android.server.wm.flicker.testapp.ActivityOptions
+import com.android.server.wm.flicker.testapp.ActivityOptions.BottomHalfPip
 
 class BottomHalfPipAppHelper(
     instrumentation: Instrumentation,
     private val useLaunchingActivity: Boolean = false,
+    private val fillTaskOnCreate: Boolean = true,
 ) : PipAppHelper(
     instrumentation,
-    appName = ActivityOptions.BottomHalfPip.LABEL,
-    componentNameMatcher = ActivityOptions.BottomHalfPip.COMPONENT
-        .toFlickerComponent()
+    appName = BottomHalfPip.LABEL,
+    componentNameMatcher = BottomHalfPip.COMPONENT.toFlickerComponent()
 ) {
     override val openAppIntent: Intent
         get() = super.openAppIntent.apply {
             component = if (useLaunchingActivity) {
-                ActivityOptions.BottomHalfPip.LAUNCHING_APP_COMPONENT
+                BottomHalfPip.LAUNCHING_APP_COMPONENT
             } else {
-                ActivityOptions.BottomHalfPip.COMPONENT
+                BottomHalfPip.COMPONENT
+            }
+            if (fillTaskOnCreate) {
+                putExtra(BottomHalfPip.EXTRA_BOTTOM_HALF_LAYOUT, false.toString())
             }
         }
+
+    override fun exitPipToOriginalTaskViaIntent(wmHelper: WindowManagerStateHelper) {
+        launchViaIntent(
+            wmHelper,
+            Intent().apply {
+                component = BottomHalfPip.COMPONENT
+                addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+            }
+        )
+    }
+
+    fun toggleBottomHalfLayout() {
+        clickObject(TOGGLE_BOTTOM_HALF_LAYOUT_ID)
+    }
+
+    companion object {
+        private const val TOGGLE_BOTTOM_HALF_LAYOUT_ID = "toggle_bottom_half_layout"
+    }
 }
\ 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 de17bf4..344cac1 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
@@ -75,8 +75,9 @@
             .waitForAndVerify()
     }
 
-    /** Expand the PIP window back to full screen via intent and wait until the app is visible */
-    fun exitPipToFullScreenViaIntent(wmHelper: WindowManagerStateHelper) = launchViaIntent(wmHelper)
+    /** Expand the PIP window back to original task via intent and wait until the app is visible */
+    open fun exitPipToOriginalTaskViaIntent(wmHelper: WindowManagerStateHelper) =
+        launchViaIntent(wmHelper)
 
     fun changeAspectRatio(wmHelper: WindowManagerStateHelper) {
         val intent = Intent("com.android.wm.shell.flicker.testapp.ASPECT_RATIO")
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_bottom_half_pip.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_bottom_half_pip.xml
new file mode 100644
index 0000000..2f9c3aa
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_bottom_half_pip.xml
@@ -0,0 +1,154 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2024 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    android:background="@android:color/holo_blue_bright">
+
+    <!-- All the buttons (and other clickable elements) should be arranged in a way so that it is
+         possible to "cycle" over all them by clicking on the D-Pad DOWN button. The way we do it
+         here is by arranging them this vertical LL and by relying on the nextFocusDown attribute
+         where things are arranged differently and to circle back up to the top once we reach the
+         bottom. -->
+
+    <Button
+        android:id="@+id/enter_pip"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="Enter PIP"
+        android:onClick="enterPip"/>
+
+    <Button
+        android:id="@+id/toggle_bottom_half_layout"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="Set Bottom Half Layout"
+        android:onClick="toggleBottomHalfLayout"/>
+
+    <CheckBox
+        android:id="@+id/with_custom_actions"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="With custom actions"/>
+
+    <RadioGroup
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        android:checkedButton="@id/enter_pip_on_leave_disabled">
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="Enter PiP on home press"/>
+
+        <RadioButton
+            android:id="@+id/enter_pip_on_leave_disabled"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="Disabled"
+            android:onClick="onAutoPipSelected"/>
+
+        <RadioButton
+            android:id="@+id/enter_pip_on_leave_manual"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="Via code behind"
+            android:onClick="onAutoPipSelected"/>
+
+        <RadioButton
+            android:id="@+id/enter_pip_on_leave_autoenter"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="Auto-enter PiP"
+            android:onClick="onAutoPipSelected"/>
+    </RadioGroup>
+
+    <RadioGroup
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        android:checkedButton="@id/ratio_default">
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="Ratio"/>
+
+        <RadioButton
+            android:id="@+id/ratio_default"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="Default"
+            android:onClick="onRatioSelected"/>
+
+        <RadioButton
+            android:id="@+id/ratio_square"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="Square [1:1]"
+            android:onClick="onRatioSelected"/>
+
+        <RadioButton
+            android:id="@+id/ratio_wide"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="Wide [2:1]"
+            android:onClick="onRatioSelected"/>
+
+        <RadioButton
+            android:id="@+id/ratio_tall"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="Tall [1:2]"
+            android:onClick="onRatioSelected"/>
+    </RadioGroup>
+
+    <CheckBox
+        android:id="@+id/set_source_rect_hint"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="Set SourceRectHint"/>
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="Media Session"/>
+
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content">
+
+        <Button
+            android:id="@+id/media_session_start"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:nextFocusDown="@id/media_session_stop"
+            android:text="Start"/>
+
+        <Button
+            android:id="@+id/media_session_stop"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:nextFocusDown="@id/enter_pip"
+            android:text="Stop"/>
+
+    </LinearLayout>
+
+</LinearLayout>
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BottomHalfPipActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BottomHalfPipActivity.java
index 3d48655..3bbb945 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BottomHalfPipActivity.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BottomHalfPipActivity.java
@@ -16,9 +16,14 @@
 
 package com.android.server.wm.flicker.testapp;
 
+import static com.android.server.wm.flicker.testapp.ActivityOptions.BottomHalfPip.EXTRA_BOTTOM_HALF_LAYOUT;
+
 import android.app.Activity;
+import android.content.Intent;
 import android.content.res.Configuration;
 import android.os.Bundle;
+import android.view.Gravity;
+import android.view.View;
 import android.view.ViewGroup.LayoutParams;
 import android.view.WindowManager;
 
@@ -26,11 +31,13 @@
 
 public class BottomHalfPipActivity extends PipActivity {
 
+    private boolean mUseBottomHalfLayout;
+
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_bottom_half_pip);
         setTheme(R.style.TranslucentTheme);
-        updateLayout();
     }
 
     @Override
@@ -41,14 +48,28 @@
     }
 
     /**
+     * Toggles the layout mode between fill task and half-bottom modes.
+     */
+    public void toggleBottomHalfLayout(View v) {
+        mUseBottomHalfLayout = !mUseBottomHalfLayout;
+        updateLayout();
+    }
+
+    /**
      * Sets to match parent layout if the activity is
-     * {@link Activity#isInPictureInPictureMode()}. Otherwise, set to bottom half
-     * layout.
+     * {@link Activity#isInPictureInPictureMode()}. Otherwise,
+     * follows {@link #mUseBottomHalfLayout}.
      *
      * @see #setToBottomHalfMode(boolean)
      */
     private void updateLayout() {
-        setToBottomHalfMode(!isInPictureInPictureMode());
+        final boolean useBottomHalfLayout;
+        if (isInPictureInPictureMode()) {
+            useBottomHalfLayout = false;
+        } else {
+            useBottomHalfLayout = mUseBottomHalfLayout;
+        }
+        setToBottomHalfMode(useBottomHalfLayout);
     }
 
     /**
@@ -57,15 +78,31 @@
      */
     private void setToBottomHalfMode(boolean useBottomHalfLayout) {
         final WindowManager.LayoutParams attrs = getWindow().getAttributes();
+        attrs.gravity = Gravity.BOTTOM;
         if (useBottomHalfLayout) {
             final int taskHeight = getWindowManager().getCurrentWindowMetrics().getBounds()
                     .height();
-            attrs.y = taskHeight / 2;
             attrs.height = taskHeight / 2;
         } else {
-            attrs.y = 0;
             attrs.height = LayoutParams.MATCH_PARENT;
         }
         getWindow().setAttributes(attrs);
     }
+
+    @Override
+    void handleIntentExtra(@NonNull Intent intent) {
+        super.handleIntentExtra(intent);
+        if (intent.hasExtra(EXTRA_BOTTOM_HALF_LAYOUT)) {
+            final String booleanString = intent.getStringExtra(EXTRA_BOTTOM_HALF_LAYOUT);
+            // We don't use Boolean#parseBoolean here because the impl only checks if the string
+            // equals to "true", and returns for any other cases. We use our own impl here to
+            // prevent false positive.
+            if ("true".equals(booleanString)) {
+                mUseBottomHalfLayout = true;
+            } else if ("false".equals(booleanString)) {
+                mUseBottomHalfLayout = false;
+            }
+        }
+        updateLayout();
+    }
 }
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BottomHalfPipLaunchingActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BottomHalfPipLaunchingActivity.java
index d9d4361..209f71e 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BottomHalfPipLaunchingActivity.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/BottomHalfPipLaunchingActivity.java
@@ -24,8 +24,12 @@
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-
         final Intent intent = new Intent(this, BottomHalfPipActivity.class);
+        // Pass extras to BottomHalfPipActivity.
+        final Bundle extras = getIntent().getExtras();
+        if (extras != null) {
+            intent.putExtras(extras);
+        }
         startActivity(intent);
     }
 }
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java
index 13d7f7f..ee25ab2 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/PipActivity.java
@@ -350,7 +350,7 @@
         mMediaSession.setActive(newState != STATE_STOPPED);
     }
 
-    private void handleIntentExtra(Intent intent) {
+    void handleIntentExtra(Intent intent) {
         // Set the fixed orientation if requested
         if (intent.hasExtra(EXTRA_PIP_ORIENTATION)) {
             final int ori = Integer.parseInt(getIntent().getStringExtra(EXTRA_PIP_ORIENTATION));
diff --git a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
index 4d7085f..d35c900 100644
--- a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
+++ b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt
@@ -830,6 +830,18 @@
                 KeyEvent.META_META_ON or KeyEvent.META_ALT_ON,
                 intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
             ),
+            TestData(
+                "META + ALT + 'V' -> Toggle Voice Access",
+                intArrayOf(
+                    KeyEvent.KEYCODE_META_LEFT,
+                    KeyEvent.KEYCODE_ALT_LEFT,
+                    KeyEvent.KEYCODE_V
+                ),
+                KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_VOICE_ACCESS,
+                intArrayOf(KeyEvent.KEYCODE_V),
+                KeyEvent.META_META_ON or KeyEvent.META_ALT_ON,
+                intArrayOf(KeyGestureEvent.ACTION_GESTURE_COMPLETE)
+            ),
         )
     }
 
@@ -843,6 +855,7 @@
         com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_STICKY_KEYS_FLAG,
         com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_MOUSE_KEYS,
         com.android.hardware.input.Flags.FLAG_ENABLE_TALKBACK_AND_MAGNIFIER_KEY_GESTURES,
+        com.android.hardware.input.Flags.FLAG_ENABLE_VOICE_ACCESS_KEY_GESTURES,
         com.android.window.flags.Flags.FLAG_ENABLE_MOVE_TO_NEXT_DISPLAY_SHORTCUT,
         com.android.window.flags.Flags.FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS
     )
@@ -861,6 +874,7 @@
         com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_STICKY_KEYS_FLAG,
         com.android.hardware.input.Flags.FLAG_KEYBOARD_A11Y_MOUSE_KEYS,
         com.android.hardware.input.Flags.FLAG_ENABLE_TALKBACK_AND_MAGNIFIER_KEY_GESTURES,
+        com.android.hardware.input.Flags.FLAG_ENABLE_VOICE_ACCESS_KEY_GESTURES,
         com.android.window.flags.Flags.FLAG_ENABLE_MOVE_TO_NEXT_DISPLAY_SHORTCUT,
         com.android.window.flags.Flags.FLAG_ENABLE_TASK_RESIZING_KEYBOARD_SHORTCUTS
     )
diff --git a/tests/InputScreenshotTest/robotests/Android.bp b/tests/InputScreenshotTest/robotests/Android.bp
index b2414a8..63a1384 100644
--- a/tests/InputScreenshotTest/robotests/Android.bp
+++ b/tests/InputScreenshotTest/robotests/Android.bp
@@ -66,7 +66,6 @@
         "android.test.mock.stubs.system",
         "truth",
     ],
-    upstream: true,
     java_resource_dirs: ["config"],
     instrumentation_for: "InputRoboApp",
 
diff --git a/tests/Internal/Android.bp b/tests/Internal/Android.bp
index 9f35c7b..e294da1 100644
--- a/tests/Internal/Android.bp
+++ b/tests/Internal/Android.bp
@@ -65,6 +65,7 @@
         "src/com/android/internal/util/ParcellingTests.java",
     ],
     auto_gen_config: true,
+    team: "trendy_team_ravenwood",
 }
 
 java_test_helper_library {
diff --git a/tests/testables/tests/AndroidTest.xml b/tests/testables/tests/AndroidTest.xml
index 85f6e62..392bf67 100644
--- a/tests/testables/tests/AndroidTest.xml
+++ b/tests/testables/tests/AndroidTest.xml
@@ -45,6 +45,7 @@
 
     <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
         <option name="directory-keys" value="/data/user/0/com.android.testables/files"/>
+        <option name="directory-keys" value="/data/user/10/com.android.testables/files"/>
         <option name="collect-on-run-ended-only" value="true"/>
         <option name="clean-up" value="true"/>
     </metrics_collector>
diff --git a/tools/aapt2/cmd/Command.cpp b/tools/aapt2/cmd/Command.cpp
index 449d93d..2031556 100644
--- a/tools/aapt2/cmd/Command.cpp
+++ b/tools/aapt2/cmd/Command.cpp
@@ -53,61 +53,67 @@
 
 void Command::AddRequiredFlag(StringPiece name, StringPiece description, std::string* value,
                               uint32_t flags) {
-  auto func = [value, flags](StringPiece arg) -> bool {
+  auto func = [value, flags](StringPiece arg, std::ostream*) -> bool {
     *value = (flags & Command::kPath) ? GetSafePath(arg) : std::string(arg);
     return true;
   };
 
-  flags_.emplace_back(Flag(name, description, /* required */ true, /* num_args */ 1, func));
+  flags_.emplace_back(
+      Flag(name, description, /* required */ true, /* num_args */ 1, std::move(func)));
 }
 
 void Command::AddRequiredFlagList(StringPiece name, StringPiece description,
                                   std::vector<std::string>* value, uint32_t flags) {
-  auto func = [value, flags](StringPiece arg) -> bool {
+  auto func = [value, flags](StringPiece arg, std::ostream*) -> bool {
     value->push_back((flags & Command::kPath) ? GetSafePath(arg) : std::string(arg));
     return true;
   };
 
-  flags_.emplace_back(Flag(name, description, /* required */ true, /* num_args */ 1, func));
+  flags_.emplace_back(
+      Flag(name, description, /* required */ true, /* num_args */ 1, std::move(func)));
 }
 
 void Command::AddOptionalFlag(StringPiece name, StringPiece description,
                               std::optional<std::string>* value, uint32_t flags) {
-  auto func = [value, flags](StringPiece arg) -> bool {
+  auto func = [value, flags](StringPiece arg, std::ostream*) -> bool {
     *value = (flags & Command::kPath) ? GetSafePath(arg) : std::string(arg);
     return true;
   };
 
-  flags_.emplace_back(Flag(name, description, /* required */ false, /* num_args */ 1, func));
+  flags_.emplace_back(
+      Flag(name, description, /* required */ false, /* num_args */ 1, std::move(func)));
 }
 
 void Command::AddOptionalFlagList(StringPiece name, StringPiece description,
                                   std::vector<std::string>* value, uint32_t flags) {
-  auto func = [value, flags](StringPiece arg) -> bool {
+  auto func = [value, flags](StringPiece arg, std::ostream*) -> bool {
     value->push_back((flags & Command::kPath) ? GetSafePath(arg) : std::string(arg));
     return true;
   };
 
-  flags_.emplace_back(Flag(name, description, /* required */ false, /* num_args */ 1, func));
+  flags_.emplace_back(
+      Flag(name, description, /* required */ false, /* num_args */ 1, std::move(func)));
 }
 
 void Command::AddOptionalFlagList(StringPiece name, StringPiece description,
                                   std::unordered_set<std::string>* value) {
-  auto func = [value](StringPiece arg) -> bool {
+  auto func = [value](StringPiece arg, std::ostream* out_error) -> bool {
     value->emplace(arg);
     return true;
   };
 
-  flags_.emplace_back(Flag(name, description, /* required */ false, /* num_args */ 1, func));
+  flags_.emplace_back(
+      Flag(name, description, /* required */ false, /* num_args */ 1, std::move(func)));
 }
 
 void Command::AddOptionalSwitch(StringPiece name, StringPiece description, bool* value) {
-  auto func = [value](StringPiece arg) -> bool {
+  auto func = [value](StringPiece arg, std::ostream* out_error) -> bool {
     *value = true;
     return true;
   };
 
-  flags_.emplace_back(Flag(name, description, /* required */ false, /* num_args */ 0, func));
+  flags_.emplace_back(
+      Flag(name, description, /* required */ false, /* num_args */ 0, std::move(func)));
 }
 
 void Command::AddOptionalSubcommand(std::unique_ptr<Command>&& subcommand, bool experimental) {
@@ -172,19 +178,74 @@
       argline = " ";
     }
   }
-  *out << " " << std::setw(kWidth) << std::left << "-h"
-       << "Displays this help menu\n";
   out->flush();
 }
 
-int Command::Execute(const std::vector<StringPiece>& args, std::ostream* out_error) {
+const std::string& Command::addEnvironmentArg(const Flag& flag, const char* env) {
+  if (*env && flag.num_args > 0) {
+    return environment_args_.emplace_back(flag.name + '=' + env);
+  }
+  return flag.name;
+}
+
+//
+// Looks for the flags specified in the environment and adds them to |args|.
+// Expected format:
+// - _AAPT2_UPPERCASE_NAME are added before all of the command line flags, so it's
+//   a default for the flag that may get overridden by the command line.
+// - AAPT2_UPPERCASE_NAME_ are added after them, making this to be the final value
+//   even if there was something on the command line.
+// - All dashes in the flag name get replaced with underscores, the rest of it is
+//   intact.
+//
+// E.g.
+//  --set-some-flag becomes either _AAPT2_SET_SOME_FLAG or AAPT2_SET_SOME_FLAG_
+//  --set-param=2 is _AAPT2_SET_SOME_FLAG=2
+//
+// Values get passed as it, with no processing or quoting.
+//
+// This way one can make sure aapt2 has the flags they need even when it is
+// launched in a way they can't control, e.g. deep inside a build.
+//
+void Command::parseFlagsFromEnvironment(std::vector<StringPiece>& args) {
+  // If the first argument is a subcommand then skip it and prepend the flags past that (the root
+  // command should only have a single '-h' flag anyway).
+  const int insert_pos = args.empty() ? 0 : args.front().starts_with('-') ? 0 : 1;
+
+  std::string env_name;
+  for (const Flag& flag : flags_) {
+    // First, the prefix version.
+    env_name.assign("_AAPT2_");
+    // Append the uppercased flag name, skipping all dashes in front and replacing them with
+    // underscores later.
+    auto name_start = flag.name.begin();
+    while (name_start != flag.name.end() && *name_start == '-') {
+      ++name_start;
+    }
+    std::transform(name_start, flag.name.end(), std::back_inserter(env_name),
+                   [](char c) { return c == '-' ? '_' : toupper(c); });
+    if (auto prefix_env = getenv(env_name.c_str())) {
+      args.insert(args.begin() + insert_pos, addEnvironmentArg(flag, prefix_env));
+    }
+    // Now reuse the same name variable to construct a suffix version: append the
+    // underscore and just skip the one in front.
+    env_name += '_';
+    if (auto suffix_env = getenv(env_name.c_str() + 1)) {
+      args.push_back(addEnvironmentArg(flag, suffix_env));
+    }
+  }
+}
+
+int Command::Execute(std::vector<StringPiece>& args, std::ostream* out_error) {
   TRACE_NAME_ARGS("Command::Execute", args);
   std::vector<std::string> file_args;
 
+  parseFlagsFromEnvironment(args);
+
   for (size_t i = 0; i < args.size(); i++) {
     StringPiece arg = args[i];
     if (*(arg.data()) != '-') {
-      // Continue parsing as the subcommand if the first argument matches one of the subcommands
+      // Continue parsing as a subcommand if the first argument matches one of the subcommands
       if (i == 0) {
         for (auto& subcommand : subcommands_) {
           if (arg == subcommand->name_ || (!subcommand->short_name_.empty()
@@ -211,37 +272,67 @@
       return 1;
     }
 
+    static constexpr auto matchShortArg = [](std::string_view arg, const Flag& flag) static {
+      return flag.name.starts_with("--") &&
+             arg.compare(0, 2, std::string_view(flag.name.c_str() + 1, 2)) == 0;
+    };
+
     bool match = false;
     for (Flag& flag : flags_) {
-      // Allow both "--arg value" and "--arg=value" syntax.
+      // Allow both "--arg value" and "--arg=value" syntax, and look for the cases where we can
+      // safely deduce the "--arg" flag from the short "-a" version when there's no value expected
+      bool matched_current = false;
       if (arg.starts_with(flag.name) &&
           (arg.size() == flag.name.size() || (flag.num_args > 0 && arg[flag.name.size()] == '='))) {
-        if (flag.num_args > 0) {
-          if (arg.size() == flag.name.size()) {
-            i++;
-            if (i >= args.size()) {
-              *out_error << flag.name << " missing argument.\n\n";
-              Usage(out_error);
-              return 1;
-            }
-            arg = args[i];
-          } else {
-            arg.remove_prefix(flag.name.size() + 1);
-            // Disallow empty arguments after '='.
-            if (arg.empty()) {
-              *out_error << flag.name << " has empty argument.\n\n";
-              Usage(out_error);
-              return 1;
-            }
+        matched_current = true;
+      } else if (flag.num_args == 0 && matchShortArg(arg, flag)) {
+        matched_current = true;
+        // It matches, now need to make sure no other flag would match as well.
+        // This is really inefficient, but we don't expect to have enough flags for it to matter
+        // (famous last words).
+        for (const Flag& other_flag : flags_) {
+          if (&other_flag == &flag) {
+            continue;
           }
-          flag.action(arg);
-        } else {
-          flag.action({});
+          if (matchShortArg(arg, other_flag)) {
+            matched_current = false;  // ambiguous, skip this match
+            break;
+          }
         }
-        flag.found = true;
-        match = true;
-        break;
       }
+      if (!matched_current) {
+        continue;
+      }
+
+      if (flag.num_args > 0) {
+        if (arg.size() == flag.name.size()) {
+          i++;
+          if (i >= args.size()) {
+            *out_error << flag.name << " missing argument.\n\n";
+            Usage(out_error);
+            return 1;
+          }
+          arg = args[i];
+        } else {
+          arg.remove_prefix(flag.name.size() + 1);
+          // Disallow empty arguments after '='.
+          if (arg.empty()) {
+            *out_error << flag.name << " has empty argument.\n\n";
+            Usage(out_error);
+            return 1;
+          }
+        }
+        if (!flag.action(arg, out_error)) {
+          return 1;
+        }
+      } else {
+        if (!flag.action({}, out_error)) {
+          return 1;
+        }
+      }
+      flag.found = true;
+      match = true;
+      break;
     }
 
     if (!match) {
diff --git a/tools/aapt2/cmd/Command.h b/tools/aapt2/cmd/Command.h
index 1416e98..767ca9b 100644
--- a/tools/aapt2/cmd/Command.h
+++ b/tools/aapt2/cmd/Command.h
@@ -14,10 +14,11 @@
  * limitations under the License.
  */
 
-#ifndef AAPT_COMMAND_H
-#define AAPT_COMMAND_H
+#pragma once
 
+#include <deque>
 #include <functional>
+#include <memory>
 #include <optional>
 #include <ostream>
 #include <string>
@@ -30,10 +31,17 @@
 
 class Command {
  public:
-  explicit Command(android::StringPiece name) : name_(name), full_subcommand_name_(name){};
+  explicit Command(android::StringPiece name) : Command(name, {}) {
+  }
 
   explicit Command(android::StringPiece name, android::StringPiece short_name)
-      : name_(name), short_name_(short_name), full_subcommand_name_(name){};
+      : name_(name), short_name_(short_name), full_subcommand_name_(name) {
+    flags_.emplace_back("--help", "Displays this help menu", false, 0,
+                        [this](android::StringPiece arg, std::ostream* out) {
+                          Usage(out);
+                          return false;
+                        });
+  }
 
   Command(Command&&) = default;
   Command& operator=(Command&&) = default;
@@ -76,41 +84,51 @@
   // Parses the command line arguments, sets the flag variable values, and runs the action of
   // the command. If the arguments fail to parse to the command and its subcommands, then the action
   // will not be run and the usage will be printed instead.
-  int Execute(const std::vector<android::StringPiece>& args, std::ostream* outError);
+  int Execute(std::vector<android::StringPiece>& args, std::ostream* out_error);
+
+  // Same, but for a temporary vector of args.
+  int Execute(std::vector<android::StringPiece>&& args, std::ostream* out_error) {
+    return Execute(args, out_error);
+  }
 
   // The action to preform when the command is executed.
   virtual int Action(const std::vector<std::string>& args) = 0;
 
  private:
   struct Flag {
-    explicit Flag(android::StringPiece name, android::StringPiece description,
-                  const bool is_required, const size_t num_args,
-                  std::function<bool(android::StringPiece value)>&& action)
+    explicit Flag(android::StringPiece name, android::StringPiece description, bool is_required,
+                  const size_t num_args,
+                  std::function<bool(android::StringPiece value, std::ostream* out_err)>&& action)
         : name(name),
           description(description),
-          is_required(is_required),
+          action(std::move(action)),
           num_args(num_args),
-          action(std::move(action)) {
+          is_required(is_required) {
     }
 
-    const std::string name;
-    const std::string description;
-    const bool is_required;
-    const size_t num_args;
-    const std::function<bool(android::StringPiece value)> action;
+    std::string name;
+    std::string description;
+    std::function<bool(android::StringPiece value, std::ostream* out_error)> action;
+    size_t num_args;
+    bool is_required;
     bool found = false;
   };
 
+  const std::string& addEnvironmentArg(const Flag& flag, const char* env);
+  void parseFlagsFromEnvironment(std::vector<android::StringPiece>& args);
+
   std::string name_;
   std::string short_name_;
-  std::string description_ = "";
+  std::string description_;
   std::string full_subcommand_name_;
 
   std::vector<Flag> flags_;
   std::vector<std::unique_ptr<Command>> subcommands_;
   std::vector<std::unique_ptr<Command>> experimental_subcommands_;
+  // A collection of arguments loaded from environment variables, with stable positions
+  // in memory - we add them to the vector of string views so the pointers may not change,
+  // with or without short string buffer utilization in std::string.
+  std::deque<std::string> environment_args_;
 };
 
 }  // namespace aapt
-
-#endif  // AAPT_COMMAND_H
diff --git a/tools/aapt2/cmd/Command_test.cpp b/tools/aapt2/cmd/Command_test.cpp
index 20d87e0..2a3cb2a 100644
--- a/tools/aapt2/cmd/Command_test.cpp
+++ b/tools/aapt2/cmd/Command_test.cpp
@@ -118,4 +118,45 @@
   EXPECT_NE(0, command.Execute({"--flag1"s, "2"s}, &std::cerr));
 }
 
+TEST(CommandTest, ShortOptions) {
+  TestCommand command;
+  bool flag = false;
+  command.AddOptionalSwitch("--flag", "", &flag);
+
+  ASSERT_EQ(0, command.Execute({"--flag"s}, &std::cerr));
+  EXPECT_TRUE(flag);
+
+  // Short version of a switch should work.
+  flag = false;
+  ASSERT_EQ(0, command.Execute({"-f"s}, &std::cerr));
+  EXPECT_TRUE(flag);
+
+  // Ambiguous names shouldn't parse via short options.
+  command.AddOptionalSwitch("--flag-2", "", &flag);
+  ASSERT_NE(0, command.Execute({"-f"s}, &std::cerr));
+
+  // But when we have a proper flag like that it should still work.
+  flag = false;
+  command.AddOptionalSwitch("-f", "", &flag);
+  ASSERT_EQ(0, command.Execute({"-f"s}, &std::cerr));
+  EXPECT_TRUE(flag);
+
+  // A regular short flag works fine as well.
+  flag = false;
+  command.AddOptionalSwitch("-d", "", &flag);
+  ASSERT_EQ(0, command.Execute({"-d"s}, &std::cerr));
+  EXPECT_TRUE(flag);
+
+  // A flag with a value only works via its long name syntax.
+  std::optional<std::string> val;
+  command.AddOptionalFlag("--with-val", "", &val);
+  ASSERT_EQ(0, command.Execute({"--with-val"s, "1"s}, &std::cerr));
+  EXPECT_TRUE(val);
+  EXPECT_STREQ("1", val->c_str());
+
+  // Make sure the flags that require a value can't be parsed via short syntax, -w=blah
+  // looks weird.
+  ASSERT_NE(0, command.Execute({"-w"s, "2"s}, &std::cerr));
+}
+
 }  // namespace aapt
\ No newline at end of file
diff --git a/tools/localedata/extract_icu_data.py b/tools/localedata/extract_icu_data.py
index 8f67fa8..ec53127 100755
--- a/tools/localedata/extract_icu_data.py
+++ b/tools/localedata/extract_icu_data.py
@@ -121,7 +121,7 @@
 
 def dump_script_codes(all_scripts):
     """Dump the SCRIPT_CODES table."""
-    print('const char SCRIPT_CODES[][4] = {')
+    print('constexpr const char SCRIPT_CODES[][4] = {')
     for index, script in enumerate(all_scripts):
         print("    /* %-2d */ {'%c', '%c', '%c', '%c'}," % (
             index, script[0], script[1], script[2], script[3]))
@@ -132,15 +132,33 @@
 def dump_script_data(likely_script_dict, all_scripts):
     """Dump the script data."""
     print()
-    print('const std::unordered_map<uint32_t, uint8_t> LIKELY_SCRIPTS({')
+    print('const char* lookupLikelyScript(uint32_t packed_lang_region) {')
+    print('    switch(packed_lang_region) {')
+
+    # partition the mapping by the script code
+    parts = {}
     for locale in sorted(likely_script_dict.keys()):
         script = likely_script_dict[locale]
-        print('    {0x%08Xu, %2du}, // %s -> %s' % (
-            pack_to_uint32(locale),
-            all_scripts.index(script),
-            locale.replace('_', '-'),
-            script))
-    print('});')
+        if script in parts:
+            l = parts[script]
+        else:
+            l = []
+            parts[script] = l
+        l.append(locale)
+
+    for script in sorted(parts.keys()):
+        locales = parts[script]
+        for locale in locales:
+            print('        case 0x%08Xu: // %s -> %s' % (
+                pack_to_uint32(locale),
+                locale.replace('_', '-'),
+                script))
+        print('            return SCRIPT_CODES[%2du];' %
+              all_scripts.index(script))
+    print('        default:')
+    print('            return nullptr;')
+    print('     }')
+    print('}')
 
 
 def pack_to_uint64(locale):
@@ -152,16 +170,32 @@
             (ord(script[2]) << 8) |
             ord(script[3]))
 
+def pack_script_to_uint32(script):
+    """Pack a 4-letter script code into a 32-bit unsigned integer."""
+    return ((ord(script[0]) << 24) |
+            (ord(script[1]) << 16) |
+            (ord(script[2]) << 8) |
+            ord(script[3]))
+
 
 def dump_representative_locales(representative_locales):
     """Dump the set of representative locales."""
     print()
-    print('std::unordered_set<uint64_t> REPRESENTATIVE_LOCALES({')
+    print('bool isLocaleRepresentative(uint32_t language_and_region, const char* script) {')
+    print('    const uint64_t packed_locale =')
+    print('            ((static_cast<uint64_t>(language_and_region)) << 32u) |')
+    print('            (static_cast<uint64_t>(packScript(script)));')
+    print('    switch(packed_locale) {')
     for locale in sorted(representative_locales):
-        print('    0x%08XLLU, // %s' % (
+        print('        case 0x%08XLLU: // %s' % (
             pack_to_uint64(locale),
             locale))
-    print('});')
+
+    print('            return true;')
+    print('        default:')
+    print('            return false;')
+    print('    }')
+    print('}')
 
 
 def read_and_dump_likely_data(cldr_source_dir):
@@ -182,7 +216,7 @@
 
 def escape_script_variable_name(script):
     """Escape characters, e.g. '~', in a C++ variable name"""
-    return script.replace("~", "_")
+    return script.replace("~", "0")
 
 def read_parent_data(icu_data_dir):
     """Read locale parent data from ICU data files."""
@@ -225,29 +259,52 @@
     """Dump information for parents of locales."""
     sorted_scripts = sorted(script_organized_dict.keys())
     print()
+
     for script in sorted_scripts:
         parent_dict = script_organized_dict[script]
-        print ('const std::unordered_map<uint32_t, uint32_t> %s_PARENTS({'
-            % escape_script_variable_name(script.upper()))
+
+        # partition the mapping by the parent's value
+        parts = {}
         for locale in sorted(parent_dict.keys()):
             parent = parent_dict[locale]
-            print('    {0x%08Xu, 0x%08Xu}, // %s -> %s' % (
-                pack_to_uint32(locale),
-                pack_to_uint32(parent),
-                locale.replace('_', '-'),
-                parent.replace('_', '-')))
-        print('});')
+            if parent in parts:
+                l = parts[parent]
+            else:
+                l = []
+                parts[parent] = l
+            l.append(locale)
+
+        print('static uint32_t find%sParent(uint32_t packed_lang_region) {' % escape_script_variable_name(script))
+        print('    switch(packed_lang_region) {')
+        for parent in sorted(parts.keys()):
+            locales = parts[parent]
+            for locale in locales:
+                print('        case 0x%08Xu: // %s -> %s' % (
+                    pack_to_uint32(locale),
+                    locale.replace('_', '-'),
+                    parent.replace('_', '-')))
+
+            print('            return 0x%08Xu;' % pack_to_uint32(parent))
+
+        print('        default:')
+        print('            return 0;')
+        print('    }')
+        print('}')
         print()
 
-    print('const struct {')
-    print('    const char script[4];')
-    print('    const std::unordered_map<uint32_t, uint32_t>* map;')
-    print('} SCRIPT_PARENTS[] = {')
+    print('uint32_t findParentLocalePackedKey(const char* script, uint32_t packed_lang_region) {')
+    print('    uint32_t packedScript = packScript(script);')
+    print('    switch (packedScript) {')
+
     for script in sorted_scripts:
-        print("    {{'%c', '%c', '%c', '%c'}, &%s_PARENTS}," % (
-            script[0], script[1], script[2], script[3],
-            escape_script_variable_name(script.upper())))
-    print('};')
+        print('        case 0x%08Xu: // %s' % (pack_script_to_uint32(script), script))
+        print('            return find%sParent(packed_lang_region);' %
+              escape_script_variable_name(script))
+
+    print('        default:')
+    print('            return 0;')
+    print('    }')
+    print('}')
 
 
 def dump_parent_tree_depth(parent_dict):
@@ -261,7 +318,9 @@
         max_depth = max(max_depth, depth)
     assert max_depth < 5 # Our algorithms assume small max_depth
     print()
-    print('const size_t MAX_PARENT_DEPTH = %d;' % max_depth)
+    print('uint32_t getMaxAncestorTreeDepth() {')
+    print('    return %d;' % max_depth)
+    print('}')
 
 
 def read_and_dump_parent_data(icu_data_dir, likely_script_dict):
@@ -286,10 +345,33 @@
         'external', 'icu', 'icu4c', 'source', 'data')
     cldr_source_dir = os.path.join(source_root, 'external', 'cldr')
 
+    print('''/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+''')
     print('// Auto-generated by %s' % sys.argv[0])
-    print()
+    print('''
+#include <androidfw/LocaleDataLookup.h>
+
+namespace android {
+''')
     likely_script_dict = read_and_dump_likely_data(cldr_source_dir)
     read_and_dump_parent_data(icu_data_dir, likely_script_dict)
+    print()
+    print('} // namespace android')
 
 
 if __name__ == '__main__':
diff --git a/tools/systemfeatures/Android.bp b/tools/systemfeatures/Android.bp
index 2ebede3..87ea5db 100644
--- a/tools/systemfeatures/Android.bp
+++ b/tools/systemfeatures/Android.bp
@@ -100,3 +100,72 @@
         unit_test: true,
     },
 }
+
+java_library_host {
+    name: "systemfeatures-errorprone-lib",
+    srcs: [
+        ":systemfeatures-gen-metadata-srcs",
+        "errorprone/java/**/*.java",
+    ],
+    static_libs: [
+        "//external/error_prone:error_prone_core",
+        "guava",
+        "jsr305",
+    ],
+    libs: [
+        "//external/auto:auto_service_annotations",
+    ],
+    javacflags: [
+        // These exports are needed because this errorprone plugin access some private classes
+        // of the java compiler.
+        "--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED",
+        "--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
+        "--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED",
+    ],
+    plugins: [
+        "//external/auto:auto_service_plugin",
+    ],
+}
+
+java_plugin {
+    name: "systemfeatures-errorprone",
+    static_libs: ["systemfeatures-errorprone-lib"],
+}
+
+java_test_host {
+    name: "systemfeatures-errorprone-tests",
+    srcs: [
+        "errorprone/tests/java/**/*.java",
+    ],
+    java_resource_dirs: ["tests/src"],
+    java_resources: [
+        ":systemfeatures-errorprone-tests-data",
+    ],
+    static_libs: [
+        "compile-testing-prebuilt",
+        "error_prone_test_helpers",
+        "framework-annotations-lib",
+        "hamcrest",
+        "hamcrest-library",
+        "junit",
+        "systemfeatures-errorprone-lib",
+        "truth",
+    ],
+    test_options: {
+        unit_test: true,
+    },
+}
+
+java_system_features_srcs {
+    name: "systemfeatures-gen-metadata-srcs",
+    full_class_name: "com.android.systemfeatures.RoSystemFeaturesMetadata",
+    metadata_only: true,
+    visibility: ["//visibility:private"],
+}
+
+filegroup {
+    name: "systemfeatures-errorprone-tests-data",
+    path: "tests/src",
+    srcs: ["tests/src/android/**/*.java"],
+    visibility: ["//visibility:private"],
+}
diff --git a/tools/systemfeatures/README.md b/tools/systemfeatures/README.md
index 5836f81..b1fec1a 100644
--- a/tools/systemfeatures/README.md
+++ b/tools/systemfeatures/README.md
@@ -4,8 +4,110 @@
 
 System features exposed from `PackageManager` are defined and aggregated as
 `<feature>` xml attributes across various partitions, and are currently queried
-at runtime through the framework. This directory contains tooling that will
-support *build-time* queries of select system features, enabling optimizations
+at runtime through the framework. This directory contains tooling that supports
+*build-time* queries of select system features, enabling optimizations
 like code stripping and conditionally dependencies when so configured.
 
-### TODO(b/203143243): Expand readme after landing codegen.
+### System Feature Codegen
+
+As not all system features can be fully specified or defined at build time (e.g.
+updatable partitisions and apex modules can change/remove such features), we
+use a conditional, build flag approach that allows a given device to customize
+the subset of build-time defined system features that are immutable and cannot
+be updated.
+
+#### Build Flags
+
+System features that can be fixed at build-time are declared in a common
+location, `build/release/flag_declarations/`. These have the form
+`RELEASE_SYSTEM_FEATURE_${X}`, where `${X}` corresponds to a feature defined in
+`PackageManager`, e.g., `TELEVISION` or `WATCH`.
+
+Build flag values can then be defined per device (or form factor), where such
+values either indicate the existence/version of the system feature, or that the
+feature is unavailable, e.g., for TV, we could define these build flag values:
+```
+name: "RELEASE_SYSTEM_FEATURE_TELEVISION"
+value: {
+  string_value: "0"  # Feature version = 0
+}
+```
+```
+name: "RELEASE_SYSTEM_FEATURE_WATCH"
+value: {
+  string_value: "UNAVAILABLE"
+}
+```
+
+See also [SystemFeaturesGenerator](src/com/android/systemfeatures/SystemFeaturesGenerator.kt)
+for more details.
+
+#### Runtime Queries
+
+Each declared build flag system feature is routed into codegen, generating a
+getter API in the internal class, `com.android.internal.pm.RoSystemFeatures`:
+```
+class RoSystemFeatures {
+    ...
+    public static boolean hasFeatureX(Context context);
+    ...
+}
+```
+By default, these queries simply fall back to the usual
+`PackageManager.hasSystemFeature(...)` runtime queries. However, if a device
+defines these features via build flags, the generated code will add annotations
+indicating fixed value for this query, and adjust the generated code to return
+the value directly. This in turn enables build-time stripping and optimization.
+
+> **_NOTE:_** Any build-time defined system features will also be implicitly
+used to accelerate calls to `PackageManager.hasSystemFeature(...)` for the
+feature, avoiding binder calls when possible.
+
+#### Lint
+
+A new `ErrorProne` rule is introduced to assist with migration and maintenance
+of codegen APIs for build-time defined system features. This is defined in the
+`systemfeatures-errorprone` build rule, which can be added to any Java target's
+`plugins` list.
+
+// TODO(b/203143243): Add plugin to key system targets after initial migration.
+
+1) Add the plugin dependency to a given `${TARGET}`:
+```
+java_library {
+    name: "${TARGET}",
+    plugins: ["systemfeatures-errorprone"],
+}
+```
+2) Run locally:
+```
+RUN_ERROR_PRONE=true m ${TARGET}
+```
+3) (Optional) Update the target rule to generate in-place patch files:
+```
+java_library {
+    name: "${TARGET}",
+    plugins: ["systemfeatures-errorprone"],
+    // DO NOT SUBMIT: GENERATE IN-PLACE PATCH FILES
+    errorprone: {
+        javacflags: [
+            "-XepPatchChecks:RoSystemFeaturesChecker",
+            "-XepPatchLocation:IN_PLACE",
+        ],
+    }
+    ...
+}
+```
+```
+RUN_ERROR_PRONE=true m ${TARGET}
+```
+
+See also [RoSystemFeaturesChecker](errorprone/java/com/android/systemfeatures/errorprone/RoSystemFeaturesChecker.java)
+for more details.
+
+> **_NOTE:_** Not all system feature queries or targets need or should be
+migrated. Only system features that are explicitly declared with build flags,
+and only targets that are built with the platform (i.e., not updatable), are
+candidates for this linting and migration, e.g., SystemUI, System Server, etc...
+
+// TODO(b/203143243): Wrap the in-place lint updates with a simple script for convenience.
diff --git a/tools/systemfeatures/errorprone/java/com/android/systemfeatures/errorprone/RoSystemFeaturesChecker.java b/tools/systemfeatures/errorprone/java/com/android/systemfeatures/errorprone/RoSystemFeaturesChecker.java
new file mode 100644
index 0000000..7812377
--- /dev/null
+++ b/tools/systemfeatures/errorprone/java/com/android/systemfeatures/errorprone/RoSystemFeaturesChecker.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemfeatures.errorprone;
+
+import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
+
+import com.android.systemfeatures.RoSystemFeaturesMetadata;
+
+import com.google.auto.service.AutoService;
+import com.google.errorprone.BugPattern;
+import com.google.errorprone.VisitorState;
+import com.google.errorprone.bugpatterns.BugChecker;
+import com.google.errorprone.fixes.SuggestedFix;
+import com.google.errorprone.matchers.Description;
+import com.google.errorprone.matchers.Matcher;
+import com.google.errorprone.matchers.Matchers;
+import com.google.errorprone.util.ASTHelpers;
+import com.sun.source.tree.ExpressionTree;
+import com.sun.source.tree.MethodInvocationTree;
+import com.sun.tools.javac.code.Symbol;
+
+@AutoService(BugChecker.class)
+@BugPattern(
+        name = "RoSystemFeaturesChecker",
+        summary = "Use RoSystemFeature instead of PackageManager.hasSystemFeature",
+        explanation =
+                "Directly invoking `PackageManager.hasSystemFeature` is less efficient than using"
+                    + " the `RoSystemFeatures` helper class. This check flags invocations like"
+                    + " `context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_FOO)`"
+                    + " and suggests replacing them with"
+                    + " `com.android.internal.pm.RoSystemFeatures.hasFeatureFoo(context)`.",
+        severity = WARNING)
+public class RoSystemFeaturesChecker extends BugChecker
+        implements BugChecker.MethodInvocationTreeMatcher {
+
+    private static final String PACKAGE_MANAGER_CLASS = "android.content.pm.PackageManager";
+    private static final String CONTEXT_CLASS = "android.content.Context";
+    private static final String RO_SYSTEM_FEATURE_SIMPLE_CLASS = "RoSystemFeatures";
+    private static final String RO_SYSTEM_FEATURE_CLASS =
+            "com.android.internal.pm." + RO_SYSTEM_FEATURE_SIMPLE_CLASS;
+    private static final String GET_PACKAGE_MANAGER_METHOD = "getPackageManager";
+    private static final String HAS_SYSTEM_FEATURE_METHOD = "hasSystemFeature";
+    private static final String FEATURE_PREFIX = "FEATURE_";
+
+    private static final Matcher<ExpressionTree> HAS_SYSTEM_FEATURE_MATCHER =
+            Matchers.instanceMethod()
+                    .onDescendantOf(PACKAGE_MANAGER_CLASS)
+                    .named(HAS_SYSTEM_FEATURE_METHOD)
+                    .withParameters(String.class.getName());
+
+    private static final Matcher<ExpressionTree> GET_PACKAGE_MANAGER_MATCHER =
+            Matchers.instanceMethod()
+                    .onDescendantOf(CONTEXT_CLASS)
+                    .named(GET_PACKAGE_MANAGER_METHOD);
+
+    @Override
+    public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) {
+        if (!HAS_SYSTEM_FEATURE_MATCHER.matches(tree, state)) {
+            return Description.NO_MATCH;
+        }
+
+        // Check if the PackageManager was obtained from a Context instance.
+        ExpressionTree packageManager = ASTHelpers.getReceiver(tree);
+        if (!GET_PACKAGE_MANAGER_MATCHER.matches(packageManager, state)) {
+            return Description.NO_MATCH;
+        }
+
+        // Get the feature argument and check if it's a PackageManager.FEATURE_X constant.
+        ExpressionTree feature = tree.getArguments().isEmpty() ? null : tree.getArguments().get(0);
+        Symbol featureSymbol = ASTHelpers.getSymbol(feature);
+        if (featureSymbol == null
+                || !featureSymbol.isStatic()
+                || !featureSymbol.getSimpleName().toString().startsWith(FEATURE_PREFIX)
+                || ASTHelpers.enclosingClass(featureSymbol) == null
+                || !ASTHelpers.enclosingClass(featureSymbol)
+                        .getQualifiedName()
+                        .contentEquals(PACKAGE_MANAGER_CLASS)) {
+            return Description.NO_MATCH;
+        }
+
+        // Check if the feature argument is part of the RoSystemFeatures API surface.
+        String featureName = featureSymbol.getSimpleName().toString();
+        String methodName = RoSystemFeaturesMetadata.getMethodNameForFeatureName(featureName);
+        if (methodName == null) {
+            return Description.NO_MATCH;
+        }
+
+        // Generate the appropriate fix.
+        String replacement =
+                String.format(
+                        "%s.%s(%s)",
+                        RO_SYSTEM_FEATURE_SIMPLE_CLASS,
+                        methodName,
+                        state.getSourceForNode(ASTHelpers.getReceiver(packageManager)));
+        // Note that ErrorProne doesn't offer a seamless way of removing the `PackageManager` import
+        // if unused after fix application, so for now we only offer best effort import suggestions.
+        SuggestedFix fix =
+                SuggestedFix.builder()
+                        .replace(tree, replacement)
+                        .addImport(RO_SYSTEM_FEATURE_CLASS)
+                        .removeStaticImport(PACKAGE_MANAGER_CLASS + "." + featureName)
+                        .build();
+        return describeMatch(tree, fix);
+    }
+}
diff --git a/tools/systemfeatures/errorprone/tests/java/com/android/systemfeatures/errorprone/RoSystemFeaturesCheckerTest.java b/tools/systemfeatures/errorprone/tests/java/com/android/systemfeatures/errorprone/RoSystemFeaturesCheckerTest.java
new file mode 100644
index 0000000..c517b24
--- /dev/null
+++ b/tools/systemfeatures/errorprone/tests/java/com/android/systemfeatures/errorprone/RoSystemFeaturesCheckerTest.java
@@ -0,0 +1,123 @@
+/*
+ * 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.systemfeatures.errorprone;
+
+import com.google.errorprone.BugCheckerRefactoringTestHelper;
+import com.google.errorprone.CompilationTestHelper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class RoSystemFeaturesCheckerTest {
+    private BugCheckerRefactoringTestHelper mRefactoringHelper;
+    private CompilationTestHelper mCompilationHelper;
+
+    @Before
+    public void setUp() {
+        mCompilationHelper =
+                CompilationTestHelper.newInstance(RoSystemFeaturesChecker.class, getClass());
+        mRefactoringHelper =
+                BugCheckerRefactoringTestHelper.newInstance(
+                        RoSystemFeaturesChecker.class, getClass());
+    }
+
+    @Test
+    public void testNoDiagnostic() {
+        mCompilationHelper
+                .addSourceFile("/android/content/Context.java")
+                .addSourceFile("/android/content/pm/PackageManager.java")
+                .addSourceLines("Example.java",
+                        """
+                        import android.content.Context;
+                        import android.content.pm.PackageManager;
+                        public class Example {
+                          void test(Context context) {
+                            boolean hasCustomFeature = context.getPackageManager()
+                                .hasSystemFeature("my.custom.feature");
+                            boolean hasNonAnnotatedFeature = context.getPackageManager()
+                                .hasSystemFeature(PackageManager.FEATURE_NOT_ANNOTATED);
+                            boolean hasNonRoApiFeature = context.getPackageManager()
+                                .hasSystemFeature(PackageManager.FEATURE_NOT_IN_RO_FEATURE_API);
+                          }
+                        }
+                        """)
+                .doTest();
+    }
+
+    @Test
+    public void testDiagnostic() {
+        mCompilationHelper
+                .addSourceFile("/android/content/Context.java")
+                .addSourceFile("/android/content/pm/PackageManager.java")
+                .addSourceLines("Example.java",
+                        """
+                        import android.content.Context;
+                        import android.content.pm.PackageManager;
+                        public class Example {
+                          void test(Context context) {
+                            boolean hasFeature = context.getPackageManager()
+                            // BUG: Diagnostic contains:
+                                .hasSystemFeature(PackageManager.FEATURE_PC);
+                          }
+                        }
+                        """)
+                .doTest();
+    }
+
+    @Test
+    public void testFix() {
+        mRefactoringHelper
+                .addInputLines("Example.java",
+                        """
+                        import static android.content.pm.PackageManager.FEATURE_WATCH;
+
+                        import android.content.Context;
+                        import android.content.pm.PackageManager;
+                        public class Example {
+                          static class CustomContext extends Context {};
+                          private CustomContext mContext;
+                          void test(Context context) {
+                            boolean hasPc = mContext.getPackageManager()
+                                .hasSystemFeature(PackageManager.FEATURE_PC);
+                            boolean hasWatch = context.getPackageManager()
+                                .hasSystemFeature(FEATURE_WATCH);
+                          }
+                        }
+                        """)
+                .addOutputLines("Example.java",
+                        """
+                        import android.content.Context;
+                        import android.content.pm.PackageManager;
+                        import com.android.internal.pm.RoSystemFeatures;
+                        public class Example {
+                          static class CustomContext extends Context {};
+                          private CustomContext mContext;
+                          void test(Context context) {
+                            boolean hasPc = RoSystemFeatures.hasFeaturePc(mContext);
+                            boolean hasWatch = RoSystemFeatures.hasFeatureWatch(context);
+                          }
+                        }
+                        """)
+                // Don't try compiling the output, as it requires pulling in the full set of code
+                // dependencies.
+                .allowBreakingChanges()
+                .doTest();
+    }
+}
diff --git a/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt b/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt
index f260e27..ea660b0 100644
--- a/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt
+++ b/tools/systemfeatures/src/com/android/systemfeatures/SystemFeaturesGenerator.kt
@@ -53,11 +53,20 @@
  *     public static ArrayMap<String, FeatureInfo> getReadOnlySystemEnabledFeatures();
  * }
  * </pre>
+ *
+ * <p> If `--metadata-only=true` is set, the resulting class would simply be:
+ * <pre>
+ * package com.foo;
+ * public final class RoSystemFeatures {
+ *     public static String getMethodNameForFeatureName(String featureName);
+  * }
+ * </pre>
  */
 object SystemFeaturesGenerator {
     private const val FEATURE_ARG = "--feature="
     private const val FEATURE_APIS_ARG = "--feature-apis="
     private const val READONLY_ARG = "--readonly="
+    private const val METADATA_ONLY_ARG = "--metadata-only="
     private val PACKAGEMANAGER_CLASS = ClassName.get("android.content.pm", "PackageManager")
     private val CONTEXT_CLASS = ClassName.get("android.content", "Context")
     private val FEATUREINFO_CLASS = ClassName.get("android.content.pm", "FeatureInfo")
@@ -84,6 +93,8 @@
         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.")
+        println("  --metadata-only=true|false Whether to simply output metadata about the")
+        println("                             generated API surface.")
     }
 
     /** Main entrypoint for build-time system feature codegen. */
@@ -106,6 +117,7 @@
         }
 
         var readonly = false
+        var metadataOnly = false
         var outputClassName: ClassName? = null
         val featureArgs = mutableListOf<FeatureInfo>()
         // We could just as easily hardcode this list, as the static API surface should change
@@ -115,6 +127,8 @@
             when {
                 arg.startsWith(READONLY_ARG) ->
                     readonly = arg.substring(READONLY_ARG.length).toBoolean()
+                arg.startsWith(METADATA_ONLY_ARG) ->
+                    metadataOnly = arg.substring(METADATA_ONLY_ARG.length).toBoolean()
                 arg.startsWith(FEATURE_ARG) -> {
                     featureArgs.add(parseFeatureArg(arg))
                 }
@@ -155,9 +169,13 @@
                 .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
                 .addJavadoc("@hide")
 
-        addFeatureMethodsToClass(classBuilder, features.values)
-        addMaybeFeatureMethodToClass(classBuilder, features.values)
-        addGetFeaturesMethodToClass(classBuilder, features.values)
+        if (metadataOnly) {
+            addMetadataMethodToClass(classBuilder, features.values)
+        } else {
+            addFeatureMethodsToClass(classBuilder, features.values)
+            addMaybeFeatureMethodToClass(classBuilder, features.values)
+            addGetFeaturesMethodToClass(classBuilder, features.values)
+        }
 
         // TODO(b/203143243): Add validation of build vs runtime values to ensure consistency.
         JavaFile.builder(outputClassName.packageName(), classBuilder.build())
@@ -214,11 +232,8 @@
         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)
+                MethodSpec.methodBuilder(feature.methodName)
                     .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                     .addJavadoc("Check for ${feature.name}.\n\n@hide")
                     .returns(Boolean::class.java)
@@ -341,5 +356,32 @@
         builder.addMethod(methodBuilder.build())
     }
 
-    private data class FeatureInfo(val name: String, val version: Int?, val readonly: Boolean)
+    /*
+     * Adds a metadata helper method that maps FEATURE_FOO names to their generated hasFeatureFoo()
+     * API counterpart, if defined.
+     */
+    private fun addMetadataMethodToClass(
+        builder: TypeSpec.Builder,
+        features: Collection<FeatureInfo>,
+    ) {
+        val methodBuilder =
+            MethodSpec.methodBuilder("getMethodNameForFeatureName")
+                .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
+                .addJavadoc("@return \"hasFeatureFoo\" if FEATURE_FOO is in the API, else null")
+                .returns(String::class.java)
+                .addParameter(String::class.java, "featureVarName")
+
+        methodBuilder.beginControlFlow("switch (featureVarName)")
+        for (feature in features) {
+            methodBuilder.addStatement("case \$S: return \$S", feature.name, feature.methodName)
+        }
+        methodBuilder.addStatement("default: return null").endControlFlow()
+
+        builder.addMethod(methodBuilder.build())
+    }
+
+    private data class FeatureInfo(val name: String, val version: Int?, val readonly: Boolean) {
+        // Turn "FEATURE_FOO" into "hasFeatureFoo".
+        val methodName get() = "has" + CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, name)
+    }
 }
diff --git a/tools/systemfeatures/tests/src/SystemFeaturesMetadataProcessorTest.java b/tools/systemfeatures/tests/src/SystemFeaturesMetadataProcessorTest.java
index 74ce6da..560454b 100644
--- a/tools/systemfeatures/tests/src/SystemFeaturesMetadataProcessorTest.java
+++ b/tools/systemfeatures/tests/src/SystemFeaturesMetadataProcessorTest.java
@@ -36,8 +36,8 @@
     @Test
     public void testSdkFeatureCount() {
         // See the fake PackageManager definition in this directory.
-        // It defines 5 annotated features, and any/all other constants should be ignored.
-        assertThat(SystemFeaturesMetadata.SDK_FEATURE_COUNT).isEqualTo(5);
+        // It defines 6 annotated features, and any/all other constants should be ignored.
+        assertThat(SystemFeaturesMetadata.SDK_FEATURE_COUNT).isEqualTo(6);
     }
 
     @Test
diff --git a/tools/systemfeatures/tests/src/Context.java b/tools/systemfeatures/tests/src/android/content/Context.java
similarity index 100%
rename from tools/systemfeatures/tests/src/Context.java
rename to tools/systemfeatures/tests/src/android/content/Context.java
diff --git a/tools/systemfeatures/tests/src/FeatureInfo.java b/tools/systemfeatures/tests/src/android/content/pm/FeatureInfo.java
similarity index 100%
rename from tools/systemfeatures/tests/src/FeatureInfo.java
rename to tools/systemfeatures/tests/src/android/content/pm/FeatureInfo.java
diff --git a/tools/systemfeatures/tests/src/PackageManager.java b/tools/systemfeatures/tests/src/android/content/pm/PackageManager.java
similarity index 86%
rename from tools/systemfeatures/tests/src/PackageManager.java
rename to tools/systemfeatures/tests/src/android/content/pm/PackageManager.java
index 839a937..4a9edd6 100644
--- a/tools/systemfeatures/tests/src/PackageManager.java
+++ b/tools/systemfeatures/tests/src/android/content/pm/PackageManager.java
@@ -36,6 +36,9 @@
     @SdkConstant(SdkConstantType.FEATURE)
     public static final String FEATURE_WIFI = "wifi";
 
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_NOT_IN_RO_FEATURE_API = "not_in_ro_feature_api";
+
     @SdkConstant(SdkConstantType.INTENT_CATEGORY)
     public static final String FEATURE_INTENT_CATEGORY = "intent_category_with_feature_name_prefix";
 
@@ -47,4 +50,9 @@
     public boolean hasSystemFeature(String featureName, int version) {
         return false;
     }
+
+    /** @hide */
+    public boolean hasSystemFeature(String featureName) {
+        return hasSystemFeature(featureName, 0);
+    }
 }
diff --git a/tools/systemfeatures/tests/src/ArrayMap.java b/tools/systemfeatures/tests/src/android/util/ArrayMap.java
similarity index 100%
rename from tools/systemfeatures/tests/src/ArrayMap.java
rename to tools/systemfeatures/tests/src/android/util/ArrayMap.java
diff --git a/tools/systemfeatures/tests/src/ArraySet.java b/tools/systemfeatures/tests/src/android/util/ArraySet.java
similarity index 100%
rename from tools/systemfeatures/tests/src/ArraySet.java
rename to tools/systemfeatures/tests/src/android/util/ArraySet.java